Autowiring custom-made Spring component in Grails - spring

I have a custom-made Spring which is bundled in a jar, which is then set as a dependency to my Grails app. I load the app-context for the bean with importBeans statement in resoueces.groovy like
beans = {
importBeans('classpath:app-context-my-component.xml')
}
The app-context-my-component.xml has some bean definitions and the following lines
<context:annotation-config />
<context:property-placeholder location="classpath:my-component.properties" />
The component I'm trying to use with Grails is annotated with #Component("myComponent").
Grails is loading the external application context. I know it because I first didn't have the .properties file on classpath and there we're no fallback mechanism for missing property in my #Value declarations.
In the Grails controller, the component is used as
class MyController {
def myComponent
def someaction() {
myComponent.doSomething()
}
}
The result is a NullPointerException i.e. the autowiring of the component didn't work after all. I tried to use #Autowired in the controller, but that gave such a strange problems that I thought it cannot be the road I want to take.
Grails version in use is 2.3.6
The Spring component is also set to use Spring version 3.2.7 to avoid incompatibilities.
UPDATE
Now with again time in my hands for this issue, I set up Spring logging in order to figure out what's happenig. Well, there's plenty of log that Spring context loading produces, but here's what I managed to grep from the whole lot
INFO xml.XmlBeanDefinitionReader Loading XML bean definitions from class path resource [app-context-my-component.xml]
INFO support.PropertySourcesPlaceholderConfigurer Loading properties file from class path resource [my-component.properties]
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public myapp.external.MyComponent myapp.MyService.getMyComponentClient()
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public myapp.external.MyComponent myapp.MyService.getMyComponentClient()
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public myapp.external.MyComponent myapp.MyService.getMyComponentClient()
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public void myapp.MyService.setMyComponentClient(myapp.external.MyComponent)
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public void myapp.MyService.setMyComponentClient(myapp.external.MyComponent)
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public void myapp.MyService.setMyComponentClient(myapp.external.MyComponent)
I changed the namespaces of the log, but myapp.external namespace refers to the external jar package, and myapp namespace refers to Grails application namespace. I have changed the usage, so that the external component is used from a Service instead of directly from Controller, but that detail had no change in the behaviour.
Per my understanding, the Spring context loading went well.
UPDATE 2
Based on #th3morg's answer I got an idea to try with XML-based config only, skipping all #Component and such annotations from the bean. And it worked! Now Grails manages to import the beans an no longer causes NPE on use.
Although this solves my problem, at least partially. I'm still interested in solution where Spring annotations could be used.

You should make sure that component scanning is setup in your jar. Checkout out the section titled "Using Spring Namespaces" here http://grails.org/doc/latest/guide/spring.html#theBeanBuilderDSLExplained. You can also use a resources.xml and add component scanning to that file if you can't modify your jar.
Alternatively, you can also wire up the beans one by one, though this is cumbersome and tedious:
beans = {
myComponent(com.my.MyComponent){
someOtherService = ref('someOtherService') //if there are other beans to wire by name
propertyOne = "x"
propertyTwo = 2
}
}

Related

EnableLoadTimeWeaving annotation causes application context to fail to load

I am trying to enable AspectJ load-time weaving (not Spring AOP) in a Spring Boot application. My goal is to weave advice into annotated fields and java.lang.reflect.Field.set(Object, Object) at load-time.
Per the Spring docs, I tried:
#Configuration
#EnableLoadTimeWeaving
public class Config {}
Running the Spring Boot application with this configuration resulted in the application context failing to load with this message:
Caused by: java.lang.IllegalStateException:
ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader]
does NOT provide an 'addTransformer(ClassFileTransformer)' method.
Specify a custom LoadTimeWeaver or start your Java virtual machine
with Spring's agent: -javaagent:spring-instrument-{version}.jar
The latter suggestion in that message is not a good option as I am trying to avoid necessitating launch script modifications. The aspect I need to weave actually resides in a library, so all implementing Spring Boot projects will have to make whatever changes required to get LTW to work.
I also tried this configuration:
#Configuration
#EnableLoadTimeWeaving
public class Config implements LoadTimeWeavingConfigurer {
#Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
Running the Spring Boot application with this configuration resulted in the application context failing to load with this message:
Caused by: java.lang.IllegalStateException:
ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader]
does NOT provide an 'addTransformer(ClassFileTransformer)' method.
It seems I need to make the JVM use a class loader that has an addTransformer(ClassFileTransformer) method. I don't know how to do that, particularly for this situation. Any suggestions?
I am not an active Spring user, but I know that Spring supports annotation- or XML-configured agent hot-attachment and has some container-specific classes for that according to its documentation. It does not seem to work reliably in all situations, though, especially when running a Spring Boot application from an IDE or so.
Anyway, the AspectJ weaver 1.8.7 and more recent can be hot-attached. I explained how to do that in a Spring setup here. If you want a simpler solution with less boilerplate but one more dependency to a tiny helper library called byte-buddy-agent, you can use this solution as a shortcut. I have not tried it, but I know the helper library and am using it myself in other contexts when hot-attaching bytecode instrumentation agents, avoiding the fuss to cater to different JVM versions and configuration situations. But in order for that to work on JVM 9+, you might need to manually activate auto-attachment for the JVM, which would be another modification for your start-up script, and you would be back to square 1.

How to make a bean discoverable by Quarkus CDI without using annotations

I have a simple Quarkus resource:
#Path("/rosters")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class RosterResource {
private final RosterService rosterService;
public RosterResource(RosterService rosterService){
this.rosterService = rosterService;
}
#GET
#Path("/{rosterId}")
public Response getRoster(#PathParam("rosterId")Long rosterId){
return Response.ok(rosterService.getRosterById(rosterId)).build();
}
}
I am trying to inject the RosterServiceinstance in my resource, but I am getting a javax.enterprise.inject.UnsatisfiedResolutionException. However, if I use the #ApplicationScoped annotation on RosterService, then everything works just fine. Is there a way of injecting the RosterService class in my resource without using annotations? In other words, is there a way of making RosterService discoverable by the Quarkus container without directly annotating the class?
Edit: looking into the CDI docs, it seems that you can manually register beans using a method with a #BuildStep annotation. However, it is not clear to me which class should contain the annotated method)
Another option would be to use a Jandex index
To the best of my knowledge, Quarkus only implements so called annotated bean discovery. That means that all CDI beans in Quarkus have to have a bean defining annotation. #ApplicationScoped is one of them.
EDIT: regarding a Jandex index, that allows you to scan for beans in additional JARs. In other words, it will only expand the set of classes that are scanned for a bean defining annotation.
When it comes to a #BuildStep method -- that is only possible in a Quarkus extension. Extensions are powerful (and indeed they can define additional beans) but also complex. You can start at https://quarkus.io/guides/building-my-first-extension, but it may feel overwhelming. It may also feel like this is not the right thing to do if you want to just make your class a bean -- and that would be true. But if your class comes from an external library that you can't change, extension makes sense.
Is there a specific reason why you don't want to annotate your service class with #ApplicationScoped (or any other of the bean discover/scope annotations)?
The only other way that I'm aware of (instead of annotations) is - as you yourself mentioned - the use of Jandex index.

Using both WebApplicationInitializer and web.xml in spring mvc+spring security+spring session redis web application

I'm trying to implement Spring redis session in an existing Spring MVC (ver 5.1.6) application. In web.xml we have ContextLoaderListener, DispatcherServlet and contextConfigLocation are all defined.
After required dependencies are included and suggested code changes are done, i'm getting below error:
Caused by: java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader definitions in your web.xml!"}}*
As part of code changes i'm extending the class "AbstractHttpSessionApplicationInitializer",(from Spring session core library) which internally implements WebApplicationInitializer. Seems like that is trying to create another context and throwing the above error. We cannot avoid extending this class, as this does the job of registering redisHttpSession to context.
Most of the examples available are all with spring boot. So there they wouldn't have faced this issue.
Any solution, other than completely replacing web.xml and use only WebApplicationInitializer?
Just want to provide an update. Instead of extending AbstractHttpSessionApplicationInitializer abtract class, i have taken a different approach by initializing bean RedisHttpSessionConfiguration thru XML bean definition.
This approach worked.
Followed the steps mentioned in the below thread;
How to configure Spring sessions to work with Redis in xml?
Along with that we need to serialize the cookie as well;
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("SESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}

Injected bean reset to NULL in the Aspect

I am new Spring AOP and Aspectj. I have seen various posts related to injected bean in an aspect being null and I have run into a similar problem. I am still not clear how I should proceed to get past the problem I am currently encountering.
Issue: Currently we are using Spring 3.2.3 and all injection is through Annotation. In my case, the dependent bean is injected properly by Spring but at the point of execution the injected bean is NULL. BTW, this doesn't happen all the time but what I can say is the stack trace when it fails and when it succeeds is slightly different. When the injected bean is not null (I can successfully use the injected bean service), the call to the before advice (in the aspect) always happens before the target method is called as it should.When the injected bean is NULL, the call to the aspect is from the first statement of the target method. At this point, I think another aspect is instantiated and has no reference to the injected bean. Here is the aspect I have created:
#Component
#Aspect
public class Enable{
private NameService nameService;
#Autowired
public void SetNameService(NameSerice service){
// service is injected properly
this.nameSerice = service;
}
#Before("* *.*(..)")
public void callBefore(JoinPoint jp){
//sometimes nameService is null and sometimes it not not
this.nameService.lookup(...);
}
}
Examining the various posts, one way to get around this (as suggested in the post) is to configure the aspect in the XML configuration file and use the factory-method ="aspectOf" and in the configuration inject the reference to the NameService bean as a property. Our whole project uses Annotation based injection (as stated earlier). Assuming I can still configure the above aspect in an XML configuration file, how can I get the reference NameService bean Id so that I can add it to the configuration. I also saw a post related to using Configurable annotation but I assume that is for objects created outside the Spring IOC.
Currently, the aspects are woven using Aspectj compile option in pom.xml. Our root-context.xml contains the entry context:annotation-config and the aspect is injected into Spring IOC because component-scan is turned on for the folder where the aspect resides. Any help will be appreciated
This is well common error when use aspects in spring, you should add
<context:spring-configured/>
and
<aop:aspectj-autoproxy />
also add
#Configurable
#Aspect
public class Enable
To your appContext.xml
aspectOf is another style to do the above but I prefer use the nature of context.
It might be too late to answer this question. But i have come across the same situation and i fixed it as below.
1) Have a setter and getter for "NameService" in your aspect class.
2) Mark "NameService" with #Component ("nameService")
3) Configure "nameService" in xml configuration using setter injection.
4) Re-Start your server after making changes.
This should resolve the problem of getting null for "NameService" in aspect.

#Autowired in bean not in spring context

I am new to springs. Is there an alternative for autowired to be used in a ordinary java bean which is not present in spring context.
You can do so by using Spring #Configurable with some AspectJ magic.
If you need a detailed explanation, here is the link.
And here is a brief overview of how it can be achieved.
First you have some bean that you want injected somewhere:
#Component
public class InjectedClass {
// ...
}
Then, you have a class that is not spring-container managed, that you want to instantiate. You want autowiring to work with this class. You mark it as a #Configurable.
#Configurable
public class NonContainerManagedClass {
#Autowired
private InjectedClass injected;
// ...
}
Now you need to tell spring that you want this non-container managed autowiring to work. So you put the following in your spring configuration.
<context:load-time-weaver />
<context:spring-configured />
Now, since this kind of thing requires modification of the bytecodes of your #Configurable class. So you tell Tomcat to use a different classloader. You can do so by creating a context.xml in your application's META-INF diretory and putting the following in there.
<Context path="/youWebAppName">
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"
useSystemClassLoaderAsParent="false"/>
</Context>
Now, Tomcat needs to find that classloader. You can ensure that by putting Spring's spring-tomcat-weaver.jar (probably named org.springframework.instrument.tomcat-<version>.jar) in your tomcat installation's lib directory, and voila, the aspectj magic starts working. For classes that are annotated with #Configurable annotation, the #Autowired dependencies are resolved automatically; even if the instances are created outside of the spring-container.
This is probably the only way to make that work with Spring, in a clean manner. Make sure that you have appropriate dependencies in your classpath.
Another way would be to use the full AspectJ functionality and providing custom aspects around all your constructors and handling the dependency-injection yourself.

Resources