Using Spring ApplicationContext from a Task

601 views
Skip to first unread message

Barry Lagerweij

unread,
Mar 7, 2012, 7:20:12 PM3/7/12
to haze...@googlegroups.com
We are using Hazelcast to execute remote tasks. These task sometimes need access to some Spring managed beans (e.g. transaction-manager and JDBC datasources). However, since the remote Task is executed from a different threadpool (and probably on a remote server), it is very difficult to get hold of Spring's ApplicationContext. With GridGain (which we used before switching to Hazelcast), we could use the @GridSpringApplicationContextResource annotation, and GridGain would inject the resource into the Job.

We use the following to workaround for Hazelcast:


@Service
public class MyService {



    public void afterPropertiesSet() {
        hazelcast.getConfig().getProperties().put(AbstractBatchTask.APPLICATION_CONTEXT_KEY,applicationContext);
    }


Barry Lagerweij

unread,
Mar 7, 2012, 7:24:18 PM3/7/12
to haze...@googlegroups.com
Oops, pressed Send too soon ;-)

In the Hazelcast Task, we use the following to retrieve the reference:

    public R call() {
        AbstractApplicationContext ctx = (AbstractApplicationContext)hazelcast.getConfig().getProperties().get(APPLICATION_CONTEXT_KEY);
        /// now you can access transaction-manager, JDBC resources, etc.
    }

I hope other might find this useful, perhaps in the future Hazelcast will support dependency injection (e.g. Spring context, or other beans)

With kind regards,

Barry

Tim Peierls

unread,
Mar 7, 2012, 11:18:42 PM3/7/12
to haze...@googlegroups.com
I had to do something similar using Guice, injecting static members of classes used with DistributedTasks. It's not hard to do, and it works fine, but it does feel a little bit sleazy. 

I mentioned this in conversation with Talip and Fuad, and Talip briefly sketched a potential solution where (and I hope I've got this right) data that can't be serialized but instead can be replicated on every node could be registered with a Hazelcast-managed context, presumably with the help of the dependency injection machinery of your choice. This context could then be made accessible somehow to DistributedTasks, which could then retrieve the local registered versions of replicated data.

I've got things working, though, so it's not at all urgent to me.

--tim

--
You received this message because you are subscribed to the Google Groups "Hazelcast" group.
To post to this group, send email to haze...@googlegroups.com.
To unsubscribe from this group, send email to hazelcast+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/hazelcast?hl=en.

Mehmet Dogan

unread,
Apr 11, 2012, 2:34:49 AM4/11/12
to haze...@googlegroups.com
By version 2.1, we have now @SpringAware annotation. If a HazelcastInstance is created using Hazelcast Spring configuration (using hazelcast namespace in spring xml), Hazelcast will automatically call Spring to inject dependencies during deserialization of objects annotated with @SpringAware. 

For instance this callable's dependencies will be injected before it is executed;

@SpringAware
public class SomeTask implements Callable, ApplicationContextAware, Serializable {

    private transient ApplicationContext context;

    private transient SomeBean someBean;

    public Object call() throws Exception {
        // do something
        return null;
    }

    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    @Autowired
    public void setSomeBean(final SomeBean someBean) {
        this.someBean = someBean;
    }
}


For other DI frameworks we don't have built-in support yet. But all required to get them work is implementing ManagedContext and set it to Hazelcast configuration. Hazelcast will call ManagedContext.initialize(object) during deserialization.

public interface ManagedContext {
    /**
     * Initialize the given object instance.
     * This is intended for repopulating select fields and methods for deserialized instances.
     */
    void initialize(Object obj);
}

Config config = new Config();
config.setManagedContext(managedContext);


What do you think?

@mmdogan

Barry Lagerweij

unread,
Apr 12, 2012, 2:46:10 AM4/12/12
to haze...@googlegroups.com
That is great news !

I presume that the injected beans are referenced on the local node, right ? In case a task needs access to a DataSource or to a Hibernate session, that task would actually get the reference to a bean defined on the remote peer (on which the task will be executed), right ?

Barry

Mehmet Dogan

unread,
Apr 12, 2012, 3:03:02 AM4/12/12
to haze...@googlegroups.com
I presume that the injected beans are referenced on the local node, right ? In case a task needs access to a DataSource or to a Hibernate session, that task would actually get the reference to a bean defined on the remote peer (on which the task will be executed), right ?


Yes, right. Actually references to be injected should marked as transient, we do not want that local beans references to be serialized. On the remote node (on which the task will be executed), Hazelcast will ask Spring context to inject task's dependencies and to do required initializations (such as @PostConstruct or InitializingBean).

So, all needed is create an empty task and submit it to a remote node, on that node all injection and initialization will be performed.

Tim Peierls

unread,
Apr 12, 2012, 8:05:29 AM4/12/12
to haze...@googlegroups.com
The ManagedContext approach looks good to me. The Guice version would look something like:

@Singleton
public class GuiceManagedContext implements ManagedContext {

    final Injector injector;

    @Inject GuiceManagedContext(Injector injector) {
        this.injector = injector;
    }
    
    @Inject void addToConfig(Config config) {
        config.setManagedContext(this);
    }

    public void initialize(Object obj) {
        injector.injectMembers(obj);
    }
}

This assumes the Config itself is bound somewhere, and that GuiceManagedContext is bound as an eager singleton.

Because I'm using a custom DataSerializable base class (using Jackson's binary Smile format!), I already have the opportunity to do member injection there.

--tim
Reply all
Reply to author
Forward
0 new messages