Using Spring AOP with Quartz scheduler - spring

I am using Quartz scheduler for scheduling purposes in my project. I need to gather statistics like when, for how long, and how many times a job was run. I want to use Spring AOP for the same. For this, I am making Job classes spring-managed beans. Spring creates a Proxy class for each of the Job classes. But now when Quartz tries to execute this spring-managed Job, I am getting InstantiationException for the Proxy class created for the Job by Spring.
org.quartz.SchedulerException: Problem instantiating class '$Proxy6'
[See nested exception: java.lang.InstantiationException: $Proxy6]
Can anybody please suggest a solution for this problem?

If you use quarz directly (not via Spring Schedule annotation), you can ask quarz directly for the statistics. -- Many of them are already implemented in quarz.

Because Quartz Job class is managed by Quartz container not Spring container, Spring AOP can not achieve your goal. For your purpose, there are 2 ways that you can work on this:
Quartz has listener mechanism builtin, you can use a global listener to do want you want, as the AOP works. For more information about listener, refer to: Quartz document.
If you insist on Spring AOP, you have to customize the job class instantiation process, so that the job class is managed by Spring Container. One approach is write your own JobFactory, which extends SpringBeanJobFactory then override the createJobInstance() method. If you want more things about this, please comment on this, I will write more detail on this.

Related

Is there any valid reason for declaring a Quartz Job as a spring bean?

By looking at some examples of running quartz jobs in a spring boot app, I see many of them are actually declaring the Job with the #Component annotation :
Baeldung example
Dzone example
Medium example
It seems to be completly useless as the job factory will create a new instance every time the job is triggered.
I can see in my app that the method SpringBeanJobFactory.createJobInstance is called each time the job is executed. I removed the #Component annotation and it works perfectly, so is there any advantage of declaring a Job as a spring bean ?

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.

Run Mongock before #Configuration annotated class

I want to use Mongock migration tool to initialize my app's configuration that is stored in database.
The problem I have is that one of my configs is used in class that is annotated with #Configuration. As Mongock changesets are executed after #Configuration it cannot retrieve not existing yet value from database and that results in a crash of application. Is there a way to postpone creating #Configuration class? Or should I initialize this one config without using mongock?
I don't fully understand your issue. I think that you need Mongock to run before your class annotated with #Configuration is processed. As you mention, SpringMongock requires the configuration class to be processed, as it requires the Spring ApplicationContext. However, you can run Mongock as standalone runner and use it(run it) wherever you want, as it doesn't depend on Spring context.
Mongock documentation
I hope it helps.

Spring AMQP ListenerContainer lifecycle management

We are using Spring AMQP to connect to RabbitMQ in our Spring based web application.
When we declare our listener-containers as beans (using rabbit:listener-container) in application context, their lifecycle is managed by Spring.
If we declare a listener-container in a component inside a #PostConstruct method, or we create a bean with class org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer as a prototype scoped bean, we then have to manage the lifecycle i.e. start and stop the listener-container ourselves.
My question is, if we declare new queues, bindings and listener-containers inside a #PostConstruct method, just calling listener.stop/shutdown/destroy method inside the corresponding #PreDestroy method would be enough for a graceful shutdown? Or else what do I need to do?
Note: I am guessing I don't have to do anything for the new queues and bindings created in the #PostContruct, but I would be very glad if you also confirm this for me.
I would not recommend starting a listener container or declaring queues/bindings in an #PostConstruct method; the context is only half-baked at that time. It might work but it's not recommended to do stuff like that while the context is being initialized.
It's better to implement SmartLifecycle and start/stop them in the start()/stop() methods.
Then, the container lifecycles would be indirectly managed by the spring context.
You can also control exactly when your bean is started/stopped by putting it in a phase.

Testing injected dependencies into your struts2 actions

I am wondering how others might have accomplished this. I found in the Spring documentation #required which I attempted to use in a 'test' but got this stmt INFO XmlConfigurationProvider:380 - Unable to verify action class [xxx] exists at initialization
I have found another way in spring to do this is to write custom init methods and declare those on the bean but it almost seems like this is a strange thing to do to a struts2 action.
In my application I inject through spring different web services (via setter injection) into different actions. I want to ensure on startup these are not null and set.
Use request or session spring scopes.
Then you can do the checks in a #PostConstruct method, what will you do there? Throw an exception? Well, something isn't set you will eventually get a NullPointerException.

Resources