My use case is the following:
I'm currently developing an application in which there is an EventModule which is responsible for firing all sorts of events. One of those events is a TimeEvent. These time events should be fired every second. It should also be possible to add support for new events when my application is up and running. For the latter requirement I'm using OSGI.
For generating the time events themselves, I found out that spring already provides such functionality for me, namely by placing #Scheduled annotations on a method that will be invoked periodally (in my case every second).
So therefore I had this code:
/**
* Generator class that periodically generates {#link TimeEvent}s.
*/
#Component
#EnableScheduling
public class TimeEventGenerator {
#Scheduled( cron = "*/1 * * * * ?" )
public void testSchedule() {
// fire a time event
}
}
This works fine and events are fired every second. However, this requires the TimeEventGenerator class to be instantiated on start up (since it is annotated with #Component). In my use case, namely the OSGI part, I should be able to create an instance of this class in the start(BundleContext) method of my BundleActivator whenever I decide to plug in the support for TimeEvents, so having the #Component annotation is not an option.
This is the part where I'm having some troubles. I learned that I can inject beans myself in a spring application context by using a BeanFactoryPostProcessor, which is exactly what I did and this resulted in the following code:
/**
* Class that allows to add spring beans to a running application context.
*/
#Component
public class RuntimeBeanFactory implements ApplicationContextAware,
BeanFactoryPostProcessor {
private ApplicationContext applicationContext;
private ConfigurableListableBeanFactory beanFactory;
#Override
#Autowired
public void setApplicationContext( ApplicationContext aApplicationContext ) throws BeansException {
applicationContext = aApplicationContext;
}
#Override
#Autowired
public void postProcessBeanFactory( ConfigurableListableBeanFactory aBeanFactory ) throws BeansException {
beanFactory = aBeanFactory;
}
public <T> T createBean( Class<T> aBeanClass, String aBeanName ) throws IOException {
BeanDefinitionRegistry registry = ( ( BeanDefinitionRegistry ) beanFactory );
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass( aBeanClass );
beanDefinition.setLazyInit( false );
beanDefinition.setAbstract( false );
beanDefinition.setAutowireCandidate( true );
if ( !registry.isBeanNameInUse( aBeanName ) ) {
registry.registerBeanDefinition( aBeanName, beanDefinition );
}
return applicationContext.getBean( aBeanClass );
}
}
The problem here is that whenever I create a TimeEventGenerator (cf. the first code snippet) with the createBean method, the testSchedule() method is never invoked, while I expected it to be the case, since the bean is managed by spring.
EDIT: I forgot to add that I have also tried the above code with two sub classes of GenericBeanDefinition, namely the ScannedGenericBeanDefinition and the AnnotatedGenericBeanDefinition, without success either.
Looking at the Spring documentation, I think you should try AnnotatedGenericBeanDefinition instead of GenericBeanDefinition. See the documents here.
EDIT
I was looking at the code for ScheduledAnnotationBeanPostProcessor and my annotations were being processed. The issue I ran into was that even though the post processor recognized the scheduled task, it only schedules the tasks with the task executor one time. By the time I registered my scheduled class, this had already happened, to it was not getting invoked. Passing a refresh event to the post processor forced it to schedule, but it also rescheduled every other task. Looking at the code (at least in Spring 3.2), what you want to do is not possible using the annotations. You could create a custom annotation processor and register the tasks with a task executor.
If you simply need to switch the TimeEventGenerator on and off then simply put it in a separate bundle and start and stop the bundle to switch it on and off.
Related
I have an application that uses Spring Batch to define a preset number of jobs, which are currently all defined in the XML.
We add more jobs over time which requires updating the XML, however these jobs are always based on the same parent and can easily be predetermined using a simple SQL query.
So I've been trying to switch to use some combination of XML configuration and Java-based configuration but am quickly getting confused.
Even though we have many jobs, each job definition falls into essentially one of two categories. All of the jobs inherit from one or the other parent job and are effectively identical, besides having different names. The job name is used in the process to select different data from the database.
I've come up with some code much like the following but have run into problems getting it to work.
Full disclaimer that I'm also not entirely sure I'm going about this in the right way. More on that in a second; first, the code:
#Configuration
#EnableBatchProcessing
public class DynamicJobConfigurer extends DefaultBatchConfigurer implements InitializingBean {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private JobRegistry jobRegistry;
#Autowired
private DataSource dataSource;
#Autowired
private CustomJobDefinitionService customJobDefinitionService;
private Flow injectedFlow1;
private Flow injectedFlow2;
public void setupJobs() throws DuplicateJobException {
List<JobDefinition> jobDefinitions = customJobDefinitionService.getAllJobDefinitions();
for (JobDefinition jobDefinition : jobDefinitions) {
Job job = null;
if (jobDefinition.getType() == 1) {
job = jobBuilderFactory.get(jobDefinition.getName())
.start(injectedFlow1).build()
.build();
} else if (jobDefinition.getType() == 2) {
job = jobBuilderFactory.get(jobDefinition.getName())
.start(injectedFlow2).build()
.build();
}
if (job != null) {
jobRegistry.register(new ReferenceJobFactory(job));
}
}
}
#Override
public void afterPropertiesSet() throws Exception {
setupJobs();
}
public void setInjectedFlow1(Flow injectedFlow1) {
this.injectedFlow1 = injectedFlow1;
}
public void setInjectedFlow2(Flow injectedFlow2) {
this.injectedFlow2 = injectedFlow2;
}
}
I have the flows that get injected defined in the XML, much like this:
<batch:flow id="injectedFlow1">
<batch:step id="InjectedFlow1.Step1" next="InjectedFlow1.Step2">
<batch:flow parent="InjectedFlow.Step1" />
</batch:step>
<batch:step id="InjectedFlow1.Step2">
<batch:flow parent="InjectedFlow.Step2" />
</batch:step>
</batch:flow>
So as you can see, I'm effectively kicking off the setupJobs() method (which is intended to dynamically create these job definitions) from the afterPropertiesSet() method of InitializingBean. I'm not sure that's right. It is running, but I'm not sure if there's a different entry point that's better intended for this purpose. Also I'm not sure what the point of the #Configuration annotation is to be honest.
The problem I'm currently running into is as soon as I call register() from JobRegistry, it throws the following IllegalStateException:
To use the default BatchConfigurer the context must contain no more than one DataSource, found 2.
Note: my project actually has two data sources defined. The first is the default dataSource bean which connects to the database that Spring Batch uses. The second data source is an external database, and this second one contains all the information I need to define my list of jobs. But the main one does use the default name "dataSource" so I'm not quite sure how else I can tell it to use that one.
First of all - I don't recommend using a combination of XML as well as Java Configuration. Use only one, preferably Java one as its not much of an effort to convert XML config to Java config. (Unless you have some very good reasons to do it - that you haven't explained)
I haven't used Spring Batch alone as I have always used it with Spring Boot and I have a project where I have defined multiple jobs and it always worked well for similar code that you have shown.
For your issue, there are some answers on SO like this OR this which are basically trying to say that you need to write your own BatchConfigurer and not rely on default one.
Now coming to solution using Spring Boot
With Spring Boot, You should try segregate job definitions and job executions.
You should first try to just define jobs and initialize Spring context without enabling jobs (spring.batch.job.enabled=false)
In your Spring Boot main method, when you start app with something like - SpringApplication.run(Application.class, args); ...you will get ApplicationContext ctx
Now you can get your relevant beans from this context & launch specif jobs by getting names from property or command line etc & using JobLauncher.run(...) method.
You can refer my this answer if willing to order job executions. You can also write job schedulers using Java.
Point being, you separate your job building / bean configurations & job execution concerns.
Challenge
Keeping multiple jobs in a single project can be challenging when you try to have different settings for each job as application.properties file is environment specific and not job specific i.e. spring boot properties will apply to all jobs.
In my particular case, the solution was to actually eliminate the #Configuration
and #EnableBatchProcessing annotations from my class above. Something about these caused it to try and use the DefaultBatchConfigurer which fails when you have more than one data source defined (even if you've identified them clearly with "dataSource" as the primary and some other name for the secondary).
The #Configuration class in particular wasn't necessary because all it really does is lets your class get auto-instantiated without having to define it as a bean in the app context. But since I was doing that anyway this one was superfluous.
One of the downsides of removing #EnableBatchProcessing was that I could no longer auto-wire the JobBuilderFactory bean. So instead I just had to do to create it:
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.afterPropertiesSet();
jobRepository = factory.getObject();
jobBuilderFactory = new JobBuilderFactory(jobRepository);
Then it seems I was on the right track already by using jobRegistry.register(...) to define my jobs. So essentially once I removed those annotations above everything started working. I'm going to mark Sabir's answer as the correct one however because it helped me out.
In what circumstances CommandLineRunner is preferred instead of writing additional code in the main method of SpringBoot application.
I understand that CommandLineRunner gets executed before main gets completed.
In simple cases, there is no difference.
But if the code need to access features provided by spring, such as ioc or only interface repositories/services, you need to wait for the complete application startup. And the call of the overrided run method after completion is garanteed.
Besides, CommandLineRunner has other advantages:
Can be implemented multiple times
The capability to start any scheduler or log any message before application starts to run
I have used it to decouple code. Instead of placing a bunch of code into main method, the CommandLineRunner lets you distribute it more evenly around the codebase. It really depends on what kind of flags you are passing in and why you need to pass them in. Spring offers a lot of flexibility for you to get the job done in the easiest way.
For a full on command line tool, you can decouple initialization and config a little bit by dividing your code between init and core behavior.
A spring boot server can overwrite configuration based on args passed in from the command line.
I would suggest all time time. It adds a lot of flexibility to your "bootstrapping code".
1) For example, command line runners are spring #Beans, so you can activate/deactivate them at run-time.
2) You can use them in an ordered fashion by appending the #Order annotation
3) You can unit test them just like regular classes
4) You can inject dependencies into them. Each runner can then have its own dependencies.
All of the above are more difficult, if not impossible to achieve if you add all your bootstrapping logic in the main() method of the Spring Application class.
Hope my answer helps,
Cheers
I haven't found any good reason for using it over just writing code after starting the application.
The only thing I can think of is that the command line runners are called before any SpringApplicationRunListeners have #finished called.
I have seen people use them to perform main application logic, but I think this is an antipattern.
One of the annoying things about doing so is that the application start timer is still running and when the task completes you will see a log entry like Started DemoApplication in 5.626 seconds (JVM running for 0.968).
It's confusing to see a message about your application starting despite, in reality it having just finished.
I encountered a scenario where i had to keep a certain data from the db loaded into the cache before the method was hit from the controller end point the first time. In this scenario it was desirable to hit the method for populating the cache using the run method after extending CommandLineRunner class so that before the application even starts up the data is already available in the cache.
I use this to populate my Default Data. I usually create ApplicationInitializer class that extends CommandLineRunner.
I have methods like createDefaultUser(), createDefaultSytemData() etc.
This way I do not rely on sql files to populate database for me. :)
ApplicationRunner and CommandLineRunner:
two of them can execute some custom code before your application finished starting up.
ComandLineRunner:
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(CommandLineAppStartupRunner.class);
#Override
public void run(String...args) throws Exception {
logger.info(Arrays.toString(args));
}
}
you can get the args directly
ApplicationRunner:
#Component
public class AppStartupRunner implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(AppStartupRunner.class);
#Override
public void run(ApplicationArguments args) throws Exception {
logger.info(args.getOptionNames());
}
}
ApplicationRunner has many methods to get params
ComandLineRunner can get params directly
if you custom two runner class, you can use annotation #Order to Specify the order of execution
public class Phone {
#Autowired
BeanExample beanExample;
public void print(){
beanExample.fn();
}
}
public class BeansCreatorClass {
#Bean
public BeanExample getBeanExample(){
return new BeanExample();
}
#Bean
public Phone getPhone(){
return new Phone();
}
}
#SpringBootApplication
public class SpringBootRunnerConfigurationPropertiesApplication implements CommandLineRunner, ApplicationRunner {
public static void main(String[] args){
SpringApplication.run(SpringBootRunnerConfigurationPropertiesApplication.class, args);
System.out.println("==== spring boot commandLine is running === ");
// beans creator class is the class contains all beans needed
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeansCreatorClass.class);
Phone phone = applicationContext.getBean(Phone.class);
phone.print();
}
// commandLineRunner
public void run(String... args) throws Exception {
System.out.println("=== commandLine Runner is here ==== ");
}
// application runner
#Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("=== application runner is here ====");
}
}
I mostly use CommandLineRunner to:
Apply initial migrations
Run a code that is independent of REST/SOAP calls.
Got a pretty standard Spring Boot (1.3.5) application.
Enabled scheduling with #EnableScheduling (tried on main application entry point and a #Configuration annotated class.
Created a simple class with a #Scheduled method (simple fixedDelay schedule).
Scheduled task executes twice (always).
From what I have gathered so far, it is probably because two contexts are being loaded, and thusly picking up my beans twice.
Ok.
So how do I fix/prevent this double execution, since all the config is basically hidden Spring Boot magic?
Framework versions:
Spring Boot 1.3.5
Spring Cloud Brixton SR1
Main application:
#SpringBootApplication
#EnableDiscoveryClient
#EnableAsync
#EnableCircuitBreaker
public class AlertsApplication {
public static void main(final String[] args) {
SpringApplication.run(AlertsApplication.class, args);
}
}
My task class (HookCreateRequest list is pulled in from application.yml - I do not believe that to be relevant currently, but if required, can be provided):
#ConditionalOnProperty(name = "init.runner", havingValue = "InitRunner")
#ConfigurationProperties(prefix = "webhook")
public class InitRunner /*implements CommandLineRunner*/ {
private final List<HookCreateRequest> receivers = new ArrayList<>();
#Autowired
private WebHookService hookService;
#Scheduled (fixedRate = 300000)
public void run() throws Exception {
getReceivers().stream().forEach(item -> {
log.debug("Request : {}", item);
hookService.create(item);
});
}
public List<HookCreateRequest> getReceivers() {
return receivers;
}
}
There is zero xml configuration.
Not sure what else might be relevant?
EDIT 2016/07/04
I have modified to output the scheduled instance when it runs (I suspected that two different instances were being created). However, the logs seem to indicate it is the SAME instance of the task object.
logs:
15:01:16.170 DEBUG - scheduled.ScheduleHookRecreation - Schedule task running: scheduled.ScheduleHookRecreation#705a651b
...task stuff happening
...first run completes, then:
15:01:39.050 DEBUG - scheduled.ScheduleHookRecreation - Schedule task running: scheduled.ScheduleHookRecreation#705a651b
So it would seem it is the same task instance (#705a651b). Now why would in the name of sweet things would it be executed twice?
EDIT 2016/07/05
I added a #PostConstruct method to the class that carries the scheduled method, with just some logging output in. By doing that I could verify that the #PostConstruct method is being called twice - which seems to confirm that the bean is being picked up twice, which which presumably means it is fed to the scheduler twice. So how to prevent this?
Had the same problem, in my case the reason was in #Scheduled annotation's initialDelay parameter absence - method was called on application start.
Ok. We have the need to #Autowire a different webservice on-the-fly (preferably by toggling a JNDI setting on the webserver) and I'm at a loss on how to do this. This is the way I was approaching the problems..
Two packages:
com.mycomp.service.stub
com.mycomp.service.impl
One package contains MyServiceStub.java while implement MyService
The other package contains MyServiceImpl.java, which implements same
My controller, which requires MyService, has the bean defined as such
#Autowire
private MyService communicator;
My spring-context.xml has the following:
<context:component-scan base-package="com.mycomp" />
At this point I get a DuplicateBean exception during autowiring. Now, I can statically define which bean to autowire in spring-context.xml:
<bean id="communicator" class="com.mycomp.service.impl.MyServiceImpl" />
and everything works fine... But then, how to 'flip' the switch and change over to the Stub method on our QA server? It has no connection to that service, so we need to run with stubs enabled. A JNDI property would be best for this.. but I just can't get my head around how to toggle what bean spring autowires at runtime.
Any help??
Cheers,
Chris
#Profile solution
You definitely have to try Spring 3.1 #Profile:
#Autowire
private MyService communicator;
//...
#Service
#Profile("prd")
class MyServiceImpl //...
#Service
#Profile("qa")
class MyServiceStub //...
Now depending on which profile is enabled, either DefaultMyService will be initialized or MyServiceStub.
You can choose between profile in various ways:
How to set active spring 3.1 environment profile via a properites file and not via an env variable or system property
using system property
programmatically
...
Spring AOP (explicit around every method)
In this example the aspect wraps around every single MyService method separately and returns stubbed value:
#Aspect
#Service
public class StubAspect {
#Around("execution(public * com.blogspot.nurkiewicz.MyService.foo(..))")
public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
if (stubMode()) {
return //stub foo() result
}
return pjp.proceed();
}
#Around("execution(public * com.blogspot.nurkiewicz.MyService.bar(..))")
public Object aroundBar(ProceedingJoinPoint pjp) throws Throwable {
if (stubMode()) {
return //stub bar() result
}
return pjp.proceed();
}
private boolean stubMode() {
//whatever condition you want here
return true;
}
}
The code is pretty straightforward, unfortunately the return values are buried inside the aspect and you need a separate #Around for every target method. Finally, there is no place for MyServiceStub.
Spring AOP (automatically around all methods)
#Aspect
#Service
public class StubAspect {
private MyServiceStub stub = //obtain stub somehow
#Around("execution(public * com.blogspot.nurkiewicz.MyService.*(..))")
public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
if (stubMode()) {
MethodSignature signature = (MethodSignature)pjp.getSignature();
Method method = signature.getMethod();
return method.invoke(stub, pjp.getArgs());
}
return pjp.proceed();
}
private boolean stubMode() {
//whatever condition you want here
return true;
}
}
This approach is more implicit as it automatically wraps every target method, including new methods added in the future. The idea is simple: if stubMode() is off, run the standard method (pjp.proceed()). If it is on - run the exact same method with exact same parameters - but on a different object (stub in this case).
This solution is much better as it involves less manual work (at the price of using raw reflection).
Note that if both MyService implementations are Spring beans (even when one is annotated with #Primary), you might run into weird troubles. But it should be a good start.
See also:
Spring 3.1 M1: Introducing #Profile
Maybe you can replace the class with a property and deploy your application with different property files. The production version would contain the name of the real class while the QA version would contain the name of a stub.
Maybe this http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-extension-factory-postprocessors can help you.
I've got a project using Spring IoC for dependency injection, and I'm trying to slightly simplify my model by having one central location for accessing most of my beans.
I'm running into a problem using the #PostConstruct mechanism, or alternatively when implementing the InitializingBean interface. While all the dependencies in that particular bean may have been resolved, the dependencies in injected beans may not have been. For example I have:
public class A {
public void doSomething() {};
}
public class B {
private A objectA;
#Required
public void setObjectA(A objectA) {
this.objectA = objectA;
}
public A getObjectA() {
return objectA;
}
}
public class C implements InitializingBean {
private B objectB;
#Required
public void setObjectB(B objectB) {
this.objectB = objectB;
}
public void afterPropertiesSet() throws Exception {
objectB.getObjectA().doSomething();
}
}
My context.xml file defines these three beans and injects the appropriate properties, but I get a NullPointerException when the object of class C gets instantiated and the afterPropertiesSet method is called, debugging shows me that the call to getObjectA() returns null. If I inject object A directly into class C I get no errors.
Is there a mechanism I can use to ensure that all beans have been completely instantiated before my afterPropertiesSet method / any method annotated with #PostConstruct is called?
Thanks,
Joseph.
The afterPropertiesSet() is little too early to call methods on injected dependencies. Indeed, the init-method (if you have that in XML) is called after afterPropertiesSet() and then postProcessAfterInitialization() of BeanPostProcessors are called. You have #Required annotation and so, ofcourse, RequiredAnnotationBeanPostProcessor is executed.
Lifecycle callback methods are what they are: they inform you about lifecycle events and their purpose is not to enable you to hijack the task that Spring is performing. (Though you can do it as you did when directly injecting object A in C- but it is not recommended).
If you want to use object A (or any other spring bean for that matter) in class C, then I would recommend to use ApplicationContextAware (or BeanFactoryAware as the case may be) and use getBean() method to get fully baked and ready to serve bean!
Implement ApplicationListener[ContextRefreshedEvent] and do your work in onApplicationEvent(). Word of caution - ContextRefreshedEvent is sometimes published multiple times, so you may want to set a flag after the first time you get it to ignore the additional events.
Use #DependsOn to ensure that A is instantiated before C.