On May 13, 2008, at 2:58 PM, Bård Magnus Kvalheim wrote:

My little test was finally a success!

For my setup I have (names abbreviated):

*common.jar *
This library have the Local and Remote interfaces

*core.war*
Collapsed war which implements Local and Remote interfaces and creates EJB3
bean

*client.war*
uses ejb beans configured through spring


*Classloading issue*
Initially I included the common.jar in both of my war files. Doing this I
got ClassCastException trying to cast EJB to interface.
I realized that this could be related to Classloading conflicts.
Reading a previous post by David in the developer mailing list I found that
OpenEJB will load the ejb's in Tomcat's common classloader.
So I removed common.jar from the war's lib and moved it into tomcat's
common/lib. That resolved the Classloading issue.
(Please let me know if there is an alternative approach to this as it adds
complexity to deployment)


*Spring configuration*
In my client.war I took David's suggestion and configured jndiEnvironment.
<bean id="calculator"
       class="org.springframework.jndi.JndiObjectFactoryBean">
       <property name="jndiName" value="CalculatorLocal"/>
       <property name="jndiEnvironment">
           <props>
               <prop key="java.naming.factory.initial">
org.apache.openejb.client.LocalInitialContextFactory
               </prop>
           </props>
       </property>
</bean>

I've used JndiObjectFactoryBean in this example, but
LocalStatelessSessionProxyFactoryBean could also be used.


Thanks David for your help. I'm sure something new will come up shortly
though ;-)


That's great news! Having something working to improve upon is a good spot to be in :)

Certainly having to put the interfaces in a separate jar in the lib/ directory is a pain. There's nothing we can do about that for @Local interfaces, but there might be something we can do for @Remote interfaces.

If you pack the remote interface into both war files, put the ejb itself in the core.war, then in client.war make a reference to your CalculatorRemote interface either by:

- annotation. adding something like the following to a servlet (the servlet doesn't even have to be used) @EJB(name="jndiNameOfYourChoice") CalculatorRemote calculatorRemote;

 - or xml. adding a ref to your web.xml
      <ejb-ref>
        <ejb-ref-name>jndiNameOfChoice</ejb-ref-name>
        <remote>org.magnus.CalculatorRemote</remote>
      </ejb-ref>

What should happen is that when we resolve the ref for CalculatorRemote we see it's not in your "ear", which is just your war file in this case, and assume the classloaders are different. The resulting proxy should work the right magic to handle having the client and bean in different classloaders. From inside the client.war, you can lookup your bean as follows:

   InitialContext context = new InitialContext();
CalculatorRemote calculator = (CalculatorRemote) context.lookup("java:comp/env/jndiNameOfChoice");

You'd have to rig up things on the spring side to do the above lookup. They might require you to use a different FactoryBean, but I can't see any reason why the LocalStatelessSessionProxyFactoryBean wouldn't work as there's no difference between looking up a remote interface vs a local.

Anyway, give it a try and let us know.

-David

Reply via email to