Wednesday, August 21, 2013

JBoss Remote EJB Invocation: An Unexpected Journey

Complexity sign6

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.

- Ryan Dahl on Software

Es k├Ânnte alles so einfach sein, ist es aber nicht.

- Die Fantastischen Vier

 

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!

9 comments:

  1. Thank you, Jonas! I have been wrestling with the various ways to access EJBs from clients over the last few months. At least I know that others have felt (and will continue to feel, as other versions of JBoss come out) the pain of accessing EJBs in JBoss 7.

    We've got ours working, but believe you me, we don't plan on upgrading anytime soon because we know that as soon as we do, we WILL break and we'll have to spend a week or two getting it working again...

    ReplyDelete
  2. To even make this more interesting (and frustrating), check out this article, if you can get to it...https://access.redhat.com/site/solutions/405633

    It appears that the initial context that you get on line 44 of your code is NOT the Context that holds the reference to the EJB. The call to do the lookup of the EJB actually returns a second context. That's the one that needs to be closed to keep the number of connections to the JBoss server under control.

    ReplyDelete
    Replies
    1. In case you don't have access (like me), they talk about closing scoped contexts in this article: https://docs.jboss.org/author/display/AS72/Scoped+EJB+client+contexts

      Delete
  3. I have been fighting this for over a week. Got it to work for a non-clustered environment, but it is a complete failure for a clustered one using a scoped context. Have you tried to get the scoped context working?

    ReplyDelete
  4. @Anonymous:
    Yes. Scoped context was working. I guess also with clustering. However with the scoped context we could not register a client interceptor. So we abandoned that...

    ReplyDelete
  5. Hi,

    I'm fighting with migratiing our EAP5 project to EAP6.

    How do local EJB calls work with JNDI in EAP6/AS7?

    Regards,
    Rob

    ReplyDelete
  6. @Rob

    To my knowledge JBoss 7 only supports the standardized JNDI names. This standard cou can find in the EJB 3.1 Spec.
    Spec: https://jcp.org/aboutJava/communityprocess/mrel/jsr318/index.html
    Example: http://javahowto.blogspot.ch/2009/12/portable-jndi-names-in-ejb-31.html

    I have an example here:
    https://github.com/jbandi/EADJ/blob/master/EnterpriseMusicStore/EnterpriseMusicStore-web-jsfunit/src/test/java/org/musicstore/tests/jsfunit/OrderCreationTest.java

    ReplyDelete
  7. Thanks for the great tutorial!
    Finally i got it working in JBoss 7.1.3
    BTW, i found a small error in your example in one of the commented lines.
    //ejbProperties.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
    musst be changed to:
    //ejbProperties.put("remote.connection.1.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
    to match the other configuration.

    and one other thing which might help others. remote authentification via JAAS and DatabaseLoginmodule is broken in JBoss 7.1.1.final but works in 7.1.3:
    https://issues.jboss.org/browse/AS7-5825
    took me quite long to find this...

    ReplyDelete
  8. Bar none, this blog post was the most helpful resource when setting up the ejb client. Thank you!

    ReplyDelete

Related Posts Plugin for WordPress, Blogger...