Thursday, March 17, 2011

JBoss 6 and cluster-wide EJB injection

First a bit about CDI: I tested CDI  and EJB injection with JBoss 6.0.0. Few things were not clear to me and I spent some time figuring out all the details. Here they are:

CDI (@Inject, @Named, @Producer, @Observes) works only within deployment unit scope (EAR file for example). It can not automagically inject remote resources. What you can try to do is to create you own @RemoteEjb annotation and use custom @Producer to look it up in JNDI and inject it for you. Yes you can do this but you will have to create @Producer for every @RemoteEjb type you need. This means that you can not have only one @Producer for following example:

@RemoteEjb(name="service1")
Service1Interface service1;


@RemoteEjb(name="service2")
Service2Interface service2;

Here I want to inject different remote EJB based on name passed in @RemoteEjb annotation. This would make my lookups generic and I could inject any EJB, just by using appropriate interface and appropriate JNDI name. And it would require only one CDI producer method.


But, if Service1Interface and Service2Interface do not have common superinterface you will have to create two different CDI @Producers because CDI producer can not return java.lang.Object - it has to return exact type. It would be great if we could create generic producers which would return objects of any type, performing lookup by name only. Then type check could be performed while casting and injecting.



If you want to inject EJBs and go beyond your deployment unit scope then you can use standard @EJB(mappedName="serviceX") annotation.


By default this will use local JNDI and not HA-JNDI. So, even if your JBoss is a part of cluster @EJB injection will not be able to find anything that is not deployed on local cluster member. Too bad that @EJB is not smart enough to try local JNDI first and then, if it can not find appropriate EJB, try in cluster JNDI.


In order to force @EJB to use HA-JNDI you have to include jndi.properties in the root of your EAR file (root, not META-INF folder). In that jndi.properties file you will have URL(s) pointing to at least one of the members in cluster. You only need one alive member to bootstrap cluster-wide JNDI lookup. InitialContext is smart enough to find out about other members and update itself. So, your jndi.properties might look like this:


java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=jnp://localhost:1099,jnp://localhost:1100

And now, whenever you do @EJB(mappedName="serviceName") it will first try to find it in local JNDI, if it is not there and your JBoss instance is a part of cluster, it will try to find EJB anywhere in cluster. It could also cross cluster boundaries (with appropriate modifications in jndi.properties)

Cool, easy for developers and you do not have to worry about where your EJBs are deployed.