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