I am trying to do persistence in an OSGi environment (Karaf running Felix) with as much modularity as possible. I chose JDO for its added features (mainly fetch groups) instead of JPA. The implementation being Datanucleus. I use Maven to build the whole project.
As I didn't have any prior experience with JDO or OSGi it was quite a challenge to make either of them work. I am presently able to do JDO persistence in a Java SE environment (unit tests work without a problem) and I know how to provide services in an OSGi environment using the blueprint container. But I am not able to make those two things work together. I am having classloading issues.
I was not able to build even a simple application that would be able to do JDO persistence on Karaf (I tried following this tutorial but it uses Spring DM and I was unable to rewrite it to use OSGi blueprint instead).
What I am most confused about is:
What value should I set the datanucleus.primaryClassLoader property to?
What class loader to pass as an argument to the JDOHelper.getPersistenceManagerFactory method?
What packages to explicitly import using the maven-bundle-plugin? (looks like at least javax.jdo, org.datanucleus.api.jdo and org.osgi.framework might be required)
What do the other bundles need besides a reference to PersistenceManagerFactory?
Additionally:
Is it possible to separate the persistence info from the value classes? If I understand it correctly, that would only be possible if using runtime enhancement which would be very complicated if at all doable.
Is it possible to define interdependent persistence capable classes in multiple bundles? Such as having Users defined in one bundle and their Addresses in another?
I would be extremely grateful for an example of a simple multi-bundle project that takes care of persistence using only Datanucleus, JDO API and OSGi blueprint.
Thank you
I can only provide some basic hints about getting JDO/datanucleus to work on top of Karaf.
As pointed in the tutorial, you'll need to extend the LocalPersistenceManagerFactoryBean, implementing as well the BundleContextAware interface.
The key point here is classloading: the LocalPersistenceManagerFactoryBean expects all classes to be loaded by one single classloader, which isn't the case in a OSGi runtime.
In order to get it working you'll need to:
Explicitly import the org.datanucleus.api.jdo in your manifest file.
The datanucleus.primaryClassLoader property could be set to the same classloader you'll pass to the JDOHelper.getPersistenceManagerFactory method. The classloader is the one used by the org.datanucleus.api.jdo bundle (see example below)
You'll need to set the datanucleus.plugin.pluginRegistryClassName property to org.datanucleus.plugin.OSGiPluginRegistry.
When stopping/uninstalling your bundle, you'll have to refresh the javax.jdo bundle for avoiding errors when re-creating the persistence manager factory (check this question on the subject)
Sample custom LocalPersistenceManagerFactoryBean:
public class OSGiLocalPersistenceManagerFactoryBean
extends LocalPersistenceManagerFactoryBean implements BundleContextAware {
public static final String JDO_BUNDLE_NAME = "org.datanucleus.api.jdo";
public static final String JDO_PMF_CLASS_NAME = "org.datanucleus.api.jdo.JDOPersistenceManagerFactory";
private BundleContext bundleContext;
#Override
protected PersistenceManagerFactory newPersistenceManagerFactory(String name) {
return JDOHelper.getPersistenceManagerFactory(name, getClassLoader());
}
#Override
protected PersistenceManagerFactory newPersistenceManagerFactory(Map props) {
ClassLoader classLoader = getClassLoader();
props.put("datanucleus.primaryClassLoader", classLoader);
if (FrameworkUtil.getBundle(this.getClass()) != null) { // running in OSGi
props.put("datanucleus.plugin.pluginRegistryClassName", "org.datanucleus.plugin.OSGiPluginRegistry");
}
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props, classLoader);
return pmf;
}
private ClassLoader getClassLoader() {
ClassLoader classLoader = null;
Bundle thisBundle = FrameworkUtil.getBundle(this.getClass());
if (thisBundle != null) { // on OSGi runtime
Bundle[] bundles = bundleContext.getBundles();
for (Bundle bundle : bundles) {
if (JDO_BUNDLE_NAME.equals(bundle.getSymbolicName())) {
try {
classLoader = bundle.loadClass(JDO_PMF_CLASS_NAME).getClassLoader();
} catch (ClassNotFoundException e) {
// do something fancy here ...
}
break;
}
}
} else { // on Java runtime
classLoader = this.getClass().getClassLoader();
}
return classLoader;
}
#Override
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
}
Related
I'm creating a Spring Starter project and need to get all classes which are marked with a custom annotation. The annotated class is not a spring bean.
My current solution is to use the ClassPathScanningCandidateComponentProvider to find the required classes.
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(CustomAnnotation.class));
candidates = scanner.findCandidateComponents("THE MISSING PACKAGE NAME");
The problem is that I'm currently provide an empty package String so that all packages/classes are scanned which slows the startup down.
I need to access the packages which are scanned by Spring to avoid the scanning of all packages and classes.
Is there a way to retrieve all packages programmatically which are scanned by Spring or is there an alternative solution to retrieve custom annotated classes which are not Spring beans.
Greets
One solution without the need to make a full classpath scan is to use the AutowiredAnnotationBeanPostProcessor:
private List<Class<?>> candidates = new ArrayList<>();
#Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if(beanClass.isAnnotationPresent(YourAnnotation.class)){
candiates.add(beanClass));
System.out.println(beanClass);
return new Object();
}
}
#Bean
public CandiateHolder candidates() {
return new CandidateHolder(candidates);
}
You can check if the bean class which should be instantiated has the required annotation. If its the case you add the class to a property to expose it later as a bean. Instead of returning null you have to return an instance of a new Object. The returned object can be used to wrap the class in a proxy. Cause I don't need an instance I will return a simple new object. Its maybe a dirty hack but it works.
I have to use this kind of hack cause an instantiation of the needed object will result in an runtime error cause it has to be instantiated in the framework I use.
The documentation states:
Developing auto-configuration and using conditions
If you work in a company that develops shared libraries, or if you work on an open-source or commercial library, you might want to develop your own auto-configuration. Auto-configuration classes can be bundled in external jars and still be picked-up by Spring Boot.
If I have annotations for everything else (even #AutoConfigureAfter or #AutoConfigureBefore annotations),
Why maintain a properties file to point to a class with an annotation?
Because we are not going to scan the world to figure out what auto-configuration classes exist in your project. For one, an auto-configuration is just a regular #Configuration class.
The way a Spring component is found is via explicit declaration or component scan but we need to know the list of auto-configuration classes way before we actually start the context.
When SpringBoot app is starting, it will not scan all the classes in jars, So SpringBoot starter should specify which classes are auto-configured. For example, in spring-boot-2.0.4.RELEASE, it initializes like this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
//1. method run will call the construtor below
SpringApplication.run(MyApplication.class, args);
}
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
//2. find all the classes whose key is ApplicationContextInitializer in spring.factories and initialize them
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
...
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
//3. use current thread classcloader to load resources in the classpath
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
//SpringFactoriesLoader.java
public static List<String> loadFactoryNames(Class<?> factoryClass, #Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
// 3.1 first find the configuration file
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(#Nullable ClassLoader classLoader) {
...
try {
Enumeration<URL> urls = (classLoader != null ?
// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//4. spring.factories file is defined here
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
...
}
all the entries in spring.factories are loaded by below method -
org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelector#loadFactoryNames
protected Collection<String> loadFactoryNames(Class<?> source) {
return SpringFactoriesLoader.loadFactoryNames(source, getClass().getClassLoader());
}
SpringFactoriesLoader belongs to the spring-core library see below screen shot
Improve the startup performance by creating a static list of candidates at compilation time.Just scan a static 'spring.factories' file, avoiding scanning a large number of class files in jars. Similar to #indexed in springframework 5.0. --META-INF/spring.components.
Centralized configuration management, concise and compatible.
nI am developing a Spring MVC web app using Spring 3.2. We will deploy the web app to different customers. Each customer may use one of several implementations of a service interface.
It's possible that the customer may need to reset these values, so we can't just hard-wire the implementation into the application, it needs to be externally configurable.
We are already using customer-specific property files that for setting simple properties such as Strings, numbers etc, but I'm asking how to set a particular implementation of an interface.
E.g.,
class MyClass {
// this is straightforward
#Value("${customer.propertyInPropertyFile}")
private String customerSpecificString;
// how to set the correct implementation for each customer?
private ISomeService service;
}
If there are 4 implementations of ISomeService, we can't autowire, or explicitly set a bean, as this will then be set in the compiled code - and it needs to be configurable after the application is deployed ( it would be OK to restart the application though if need be)..
Does anyone know how to do this? Would this better be performed using Spring EL, or profiles?
Thanks!
So, as I wanted to used Java configuration, I used the following solution:
#Configuration
#Profile("prod")
#EnableAsync
public class ProductionConfig extends BaseConfig
// inject property value which identifies theimplementation to use
Value("${service.impl}")
private String serviceName;
#Bean()
public IRepository repository() {
IRepository rc = null;
if(StringUtils.isEmpty(serviceName)){
rc = new Impl1();
} else if ("sword-mets".equals(serviceName)){
rc = new Impl2();
} else {
rc = new Impl3();
}
log.info("Setting in repository implementation " + rc);
return rc;
}
So, this isn't quite as clean as the suggested reply using aliases - the Config class needs to know about all the possible implementation classes - but is simple and avoids having to use the XML config just for this bean.
We have an aspect, named BusinessRuleAspect, in our code base that is used on the client side and on the server side of the project. On the server side we have spring, and the component is injected from the application context:
<bean id="businessRuleAspect" class="com.fugro.gwf.domain.rules.aspects.BusinessRuleAspect"
factory-method="aspectOf">
<property name="businessRuleManager" ref="businessRuleManager" />
</bean>
The question: on the client side we don't have spring, what is the best way of injecting the businessRuleManager component in the aspect in such a case?
Without understanding the exact internals of what you are doing (or perhaps rather why), what you can do is something like the following in your aspect. (Not the cleanest option, but at least a starting point)
If BusinessRuleManager is a singleton, how do you instantiate it/retrieve it from elsewhere in the non-Spring application? Or is it used only within the aspect?
after(PersistentObject persistentObject) :
persistentObjectPropertySetter(persistentObject)
{
if(businessRuleManager!=null)
businessRuleManager = BusinessRuleManager.getInstance();
if(businessRuleManager!=null)
{
businessRuleManager.execute(persistentObject,
thisJoinPoint.getSignature().getName());
}
}
And in your BusinessRuleManager:
class BusinessRuleManager{
.......
static private BusinessRuleManager instance;
static public BusinessRuleManager getInstance(){
if( instance == null )
instance = new BusinessRuleManager();
return instance;
}
.......
}
This should allow you to use the mix of Spring injection as well as a non-spring container.
In reply to your question: "If BusinessRuleManager is a singleton, how do you instantiate it/retrieve it from elsewhere in the non-Spring application?".
Here is the very concrete code that does it:
BusinessRuleManager businessRuleManager = ContextInjectionFactory.make(BusinessRuleManagerImpl.class, context);
context.set(BusinessRuleManager.class, businessRuleManager);
This construct puts an instance of our manager in the eclipse RCP context, ready to be injected in all kinds of other eclipse RCP components. This means that, borrowing your code we would get something like:
after(PersistentObject persistentObject) : persistentObjectPropertySetter(persistentObject) {
if(businessRuleManager==null) {
Bundle bundle = FrameworkUtil.getBundle(BusinessRuleManager.class);
BundleContext bundleContext = bundle.getBundleContext();
IEclipseContext eclipseCtx = EclipseContextFactory.getServiceContext(bundleContext);
businessRuleManager = eclipseCtx.get(BusinessRuleManager.class);
}
if(businessRuleManager!=null) {
businessRuleManager.execute(persistentObject, thisJoinPoint.getSignature().getName());
}
}
I tried to get it working, but failed miserably. The main problem is that the aspect lives in a domain module, which is shared between server and client. It's very difficult (and maybe even not desirable) to use the client side dependency injection framework ("RCP E4") in the shared code base of domain.
I have a Grails application that needs to run a strategy that will likely be swapped out over time. I know Spring underlies Grails, so I was wondering if I had access to Spring's IoC container so that I could externalize the actual dependency in an xml file (note: I have never actually done this, but just know of it, so I may be missing something). My goal is to be able to do something like the following:
class SchemaUpdateService {
public int calculateSomething(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
IStrategy strat = (IStrategy) ctx.getBean("mystrat");
}
}
And then map the appropriate implementation in the beans.xml file. I assume this is supported in Grails. Does anyone have any documentation on how this would work? Do I really just need the Spring IoC library and it will just work? Thanks!
You define your beans in resources.xml or resources.groovy. The grails documentation is very clear about how to access the Spring application context.
You can access the application context from any Grails artefact using
ApplicationContext ctx = grailsApplication.mainContext
You can then use this to retrieve whichever beans you're interested in:
IStrategy strat = (IStrategy) ctx.getBean("mystrat")
In classes that don't have access to grailsApplication, you could use a helper such as the following to access the application context and the beans therein
class SpringUtils {
static getBean(String name) {
applicationContext.getBean(name)
}
static <T> T getBean(String name, Class<T> requiredType) {
applicationContext.getBean(name, requiredType)
}
static ApplicationContext getApplicationContext() {
ApplicationHolder.application.mainContext
}
}
However, this should only be necessary if you need to retrieve different implementations of the same bean at runtime. If the required bean is known at compile-time, just wire the beans together in resources.xml or resources.groovy
First of all, you want to define your strategy in your grails-app/conf/spring/resources.groovy:
beans = {
myStrat(com.yourcompany.StrategyImpl) {
someProperty = someValue
}
}
Then, you simply def the a property with the same name into your service:
class SomeGrailsService {
def myStrat
def someMethod() {
return myStrat.doSomething()
}
}
In any Grails artefact (such as services and domain classes), Grails will automatically give the myStrat property the correct value. But don't forget, in a unit test you'll have to give it a value manually as the auto-wiring does not happen in unit tests.
Outside of a Grails artefact, you can use something like:
def myStrat = ApplicationHolder.application.mainContext.myStrat
In Grails 2.0, Graeme et al are deprecating the use of the *Holder classes (such as ApplicationHolder and ConfigurationHolder), so I'm not quite sure what the Grails 2.0 approach would be...