I am trying to work out how to develop a reasonably scalable batch-processing framework for a webapp I am writing.
I am using Spring MVC for the webapp, with a custom DAO layer (to access the database a UnitOfWork instance is needed to be constructed from a UnitOfWorkFactory that is set as @Autowired and injected at runtime by Spring).
I am using the Spring Scheduler annotations (@Scheduled) to schedule tasks, however I want these tasks to run on different machines in my cluster. Each batch job should be picked up by one of the cluster machines and then executed.
Hazelcast seemed like a natural fit for this as the Distributed Execution design appeared really simple and elegant for this purpose.
I am running into a problem that doesn't seem to be covered by the documentation. I have read the documentation about Spring Integration however this seems focused at how to configure Hazelcast using Spring (which I have done already).
When the scheduler indicates that the task is to start I want to create a new instance of the task (a Callable instance) and submit it to the DistributedExecutor. When a cluster machine receives the task to run I need the Spring container on the cluster machine to inject the UnitOfWorkFactory instance into the batch task before the task attempts to execute it. Each of the clusters starts with Spring and will have the UnitOfWorkFactory already instantiated with the correct details, the problem is with getting the UnitOfWorkFactory instance injected into my task.
Does anybody know how I can configure my application so that Hazelcast can have the UnitOfWorkFactory injected automatically when a Callable is started? I have tried marking the Callable as Serializable and ApplicationContextAware but I still get NullPointerException when trying to run the task.
I could access the ApplicationContext directly, however I would rather not as it will restrict the testability of my tasks and introduce a hard-dependency on Spring for my batch jobs.
By version 2.1 Hazelcast can inject Spring context and/or Spring beans into Hazelcast managed objects.
If you configure Hazelcast using Hazelcast Spring configuration and annotate a bean using @SpringAware
, Hazelcast will ask Spring to inject dependencies of that bean.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:hz="http://www.hazelcast.com/schema/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.hazelcast.com/schema/spring
http://www.hazelcast.com/schema/spring/hazelcast-spring-2.1.xsd">
<hz:hazelcast id="instance">
<hz:config>
<hz:group name="dev" password="password"/>
<hz:network port="5701" port-auto-increment="false">
<hz:join>
<hz:multicast enabled="false" />
<hz:tcp-ip enabled="true">
<hz:members>10.10.1.2, 10.10.1.3</hz:members>
</hz:tcp-ip>
</hz:join>
</hz:network>
...
</hz:config>
</hz:hazelcast>
<bean id="someBean" class="com.hazelcast.examples.spring.SomeBean"
scope="singleton" />
...
</beans>
@SpringAware
public class SomeTask implements Callable<Long>, ApplicationContextAware, Serializable {
private transient ApplicationContext context;
private transient SomeBean someBean;
public Long call() throws Exception {
return someBean.value;
}
public void setApplicationContext(final ApplicationContext applicationContext)
throws BeansException {
context = applicationContext;
}
@Autowired
public void setSomeBean(final SomeBean someBean) {
this.someBean = someBean;
}
}
For older versions than 2.1:
Versions before 2.1 of Hazelcast are not Spring aware, so it is not possible to inject Spring context or any Spring bean into a Hazelcast managed object for pre-2.1 versions.
There is a post asking about this feature on Hazelcast group.
Hazelcast / Dependency injection for Callable
As you may already know and suggested in Hazelcast group you can access Spring ApplicationContext using;
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context = null;
public synchronized void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
if(context == null) {
context = applicationContext;
}
}
public static <T> T getBean(String name) {
return (T) context.getBean(name);
}
}
class MyCallable implements Callable {
....
public Object call() throws Exception {
SomeServiceBean bean = ApplicationContextProvider.getBean("serviceBean");
....
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With