Reputation: 117
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.
Upvotes: 3
Views: 2618
Reputation: 4380
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.
Upvotes: 1
Reputation: 19606
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.
Upvotes: 0