The amount of complexity I'm willing to tolerate is proportional to the size of the problem being solved. There will come a point where the accumulated complexity of our existing systems is greater than the complexity of creating a new one.
Es könnte alles so einfach sein, ist es aber nicht.
Getting EJB Remoting to work in JBoss 7 was probably the most pitfall-ridden path I have travelled so far on my journeys through the swamp of frameworks…
Note: The example and discussion in this post is based on JBoss EAP 6.1.
It all started out well with a simple quick-start: ejb-remote.
But the pitfalls lured …
TL;DR: Finally I got an illustrative example running, which will be the base for the following discussion. The example can be found on my Github repo.
The EJB is simple: [Gist]
It's in the client where we find the pitfalls: [Gist]
Let's discuss the pitfalls:
Pitfall 1: Determining the JNDI-name for the EJB lookup
When you deploy the EJB into JBoss you get a nice output in the server-log:
[0m23:18:42,226 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-12) JNDI bindings for session bean named HelloWorldBean in deployment unit subdeployment "ejbremote-ejb.jar" of deployment "ejbremote-ear.ear" are as follows: java:global/ejbremote-ear/ejbremote-ejb/HelloWorldBean!net.jonasbandi.ejbremote.server.HelloWorld java:app/ejbremote-ejb/HelloWorldBean!net.jonasbandi.ejbremote.server.HelloWorld java:module/HelloWorldBean!net.jonasbandi.ejbremote.server.HelloWorld java:jboss/exported/ejbremote-ear/ejbremote-ejb/HelloWorldBean!net.jonasbandi.ejbremote.server.HelloWorld java:global/ejbremote-ear/ejbremote-ejb/HelloWorldBean java:app/ejbremote-ejb/HelloWorldBean java:module/HelloWorldBean
It looks easy … but actually none of those JNDI bindings work for remote lookup!
Pitfall 2: There are two approaches for accessing a remote EJB
You can use the "standard" JNDI lookup (which uses the "remote-naming project") or the the (proprietary) EJB Client API. Both approaches differ in the initialisation properties (key-value pairs) that have to be configured and in the JNDI lookup name that must be used for a given bean.
The EJB Client API is recommended. Loadbalancing, clustering and client interceptors are only available with the EJB Client API. Also it is performance optimised. [see here]
Pitfall 3: The initialisation properties are cryptic
I do not understand the following properties (except that they all have something to do with client, context and naming):
jndiProperties.put("jboss.naming.client.ejb.context", true); ejbProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); ejbProperties.put("org.jboss.ejb.client.scoped.context", "true");
Yet you have to use the correct ones for the correct lookup approach, else it won't work…
There are also a lot of other properties floating around in different documentations, wikis, forums and JIRA issues … I did not find a authoritative documentation about them.
Pitfall 4: Use the correct runtime dependencies
Runtime dependencies are tricky. You can get you code compiling, but it will not work unless you have the correct runtime dependencies configure. Getting the EJB client API running, you need:
<dependency> <groupId>org.jboss</groupId> <artifactId>jboss-ejb-client</artifactId> <scope>runtime</scope> </dependency>
Getting the "standard" JNDI lookup running (including JNDI tree traversal) you need:
<dependency> <groupId>org.jboss</groupId> <artifactId>jboss-remote-naming</artifactId> <scope>runtime</scope> </dependency>
In both cases you need some more runtime dependencies which are listed in the client pom.xml of the example.
Pitfall 5: Error messages are mostly not helpful or even misleading
Example 1: If you configure the wrong version of the ejb-client dependency like:
<dependency> <groupId>org.jboss</groupId> <artifactId>jboss-ejb-client</artifactId> <scope>runtime</scope> <version>1.0.5.Final</version> </dependency>
Then you get an exception that makes you beleave you have made a mistake somewhere in the security-configuration:
javax.ejb.EJBAccessException: JBAS014502: Invocation on method: [] is not allowed
Example 2: If you pass a number instead of a string for the port configuration like this:
ejbProperties.put("remote.connection.1.port", 4447);
Then you get the same exception as when you use a wrong JNDI lookup name:
java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:ejbremote-ear, moduleName:ejbremote-ejb, distinctName:]
Pitfall 6: Client interceptor registration does not work with scoped context
Since JBoss 7.2 you can use scoped contexts and then you would not need do programmatic initialisation of the EJBClientContext.
So if you do this:
ejbProperties.put("org.jboss.ejb.client.scoped.context", "true");
… then you do not need to do the following:
final EJBClientConfiguration ejbClientConfiguration = new PropertiesBasedEJBClientConfiguration(ejbProperties); final ConfigBasedEJBClientContextSelector selector = new ConfigBasedEJBClientContextSelector(ejbClientConfiguration); EJBClientContext.setSelector(selector);
However if you do the above, then the programmatic registration of a client interceptor does not work any more. I suspect this is a bug?
In a later post I plan to elaborate on how to get the above example running with a custom login module and with a cluster setup… be prepared for more complexity and more magic strings!