Grails Dependency Injection Outside of Services? - spring

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...

Related

Spring - Retrieve all scanned packages

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.

autowire a Spring bean based on the value of a property

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.

Spring prototype following prototype design pattern

Spring provides bean scope as "Prototype". Means whenever bean is required in application, Spring container will create a fresh/new instance of bean.
Does is follow prototype design pattern also?
Does it create object only once and in subsequent request calls clone() method on created object to create new object?
Also if someone can provide example of prototype in JDK, Spring, Hibernate or any J2EE framework.
No spring does not use cloning to create prototype scoped instances.
Below is the code snippet taken from AbstractBeanFactory.doGetBean() function:
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
#Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
The createBean method call boils down to below code:
BeanUtils.instantiateClass(constructorToUse);
Spring does not use the Prototype Pattern, it uses reflection.
Plus, in order to use clone() it would have to subclass somehow a bean, because clone() is protected, so it does not use clone() either.
Here is a code fragment from
org.springframework.beans.factory.support.SimpleInstantiationStrategy
where you can see the use of java.lang.reflect.Constructor and java.lang.Class reflection method:
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
if (beanDefinition.getMethodOverrides().isEmpty()) {
Constructor<?> constructorToUse;
synchronized (beanDefinition.constructorArgumentLock) {
constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
...
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
...
}
...
}
...
}
So the term prototype is used to suggest that at every call to getBean, you'll get a new instance with the same properties. This is more than a simple call to a constructor however, because you'll get a bean with all dependencies wired and other properties set, so in a sense it is a prototype. Or at least it fits the concept very well.
I have not dug into Spring source code, but I think Beans with prototype scope in Spring are not created using clone() method because it is not mandatory to implement the Cloneable interface for those beans.
Moreover, suppose it is creating them using clone(). It would then be dangerous if someone is expecting deep copy instead of shallow copy.
You can always test it and find the answer.
No. Spring scopes such as prototype or singletone do not follow strictly design patterns. The naming of scopes was used to intuitively suggest behavior container provides.
This way you can have a "singleton" pattern within the container and create another object outside of the container. Similarly "prototype" pattern does not have to implement "clone" functionality.
You may want to look into this link as well: Singleton design pattern vs Singleton beans in Spring container
More elaborate explanations here:
https://springframework.guru/gang-of-four-design-patterns/prototype-pattern/

Injecting a component in an aspect in a non-spring environment

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.

Persistence in an OSGi environment using Datanucleus JDO and blueprints

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;
}
}

Resources