I'm trying to work the example in HeadFirst EJB, the Advisor one... I'm using the Reference Implementation (1.4). I compile, package, and deploy happily enough (after a couple of hours searching these archives to discover the need for "Context.INITIAL_CONTEXT_FACTORY" and stuff), but when I try to run the client, I get a class cast exception. This exception is being created by the narrow method itself. I separated out the narrow from the Java-level cast so I could distinguish. The error looks like this:
looking for a Advisor got something back, it's a com.sun.corba.se.internal.iiop.CDRInputStream_1_0 $1:IOR:000000000000002a524d493a6865616466697273742e416476696365486f6d653a303 ...snip snip, lots of boring numbers 01010000000f java.lang.ClassCastException at com.sun.corba.se.internal.javax.rmi.PortableRemoteObject.narrow (PortableRemoteObject.java:293) at javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:134) at AdviceClient.go(AdviceClient.java:26) at AdviceClient.main(AdviceClient.java:10)
The stuff about "Looking for an Advisor" and "got something back, it's a blah blah" are debug messages I added so I could see confidently what the lookup was looking for, and what was returned. I just do a S.o.p on the returned object from the lookup.
As you see, the narrow then explodes, apparently claiming that what the lookup returned can't be turned into an AdviceHome.
Any suggestions? I have undeployed and redeployed till I'm blue in the face, and I'm currently reinstalling the RI from scrach in case there was anything stupid left behind.
I see from the archives that this has been asked several times in the past, but there don't seem to be any definitive statements on what fixed it. I've tried the ideas that were hinted at in those discussions, but to no avail. Did those folks just give up, run away, and join the circus instead?
You may be probably missing the client jar i.e the stubs of the Home and the Remote on the client side Check the jdk version on the client side For me i have jdk1.4.2 and the initial context factory is com.sun.jndi.cosnaming.CNCtxFactory
java.util.Properties env = new java.util.Properties(); env.put("java.naming.factory.initial", "desisoft.ejb.client.JRMPFactory"); env.put( "desisoft.ejb.nameServer1", "jimshipc2:2050" ); Context ctx = new InitialContext(env); ut.println(ctx.getNameInNamespace()); PropsHome home = (PropsHome) PortableRemoteObject.narrow(ctx.lookup("java:comp/env/ejb/Props"),PropsHome.class);
works fine from the command line (java clientTest xxx xxxx) but I get a ClassCastException from a testjsp. I have uninstalled every thing java related and stripped the computer down to just one sdk 1.4.2_08 trying to eliminate and class_path problems. Two days trying to get this tiny little jsp going. What is up with ejbs? Do they ever really work? I have googled nad googled and seems a lot of people are having the same problem and no one ever posts a solution. Very discouraging.
I had this problem after changing the scope of classloaders in my deployments under JBoss 4.
I was attempting to override the usual classloading behaviour in the J2EE spec so that individual deployments didn't share a classloader - to make hot-redeployment possible.
If they share classloaders, when you redeploy an EAR you have to redeploy all other EARs that use the same classes... or risk dangling class references.
What had happened was that the object I got back from JNDI had been created using a different classloader than the reference in my JSP implementation. I verified this by printing out to the server log (using System.out.println, which is a bit hacky!) just before the cast:
Note that you don't normally get such informative output from ClassLoader's toString method - these are JBoss classloaders. If you want the code source of a class you call <class>.getProtectionDomain().getCodeSource(). There are good discussions of classloading issues in appservers in the JBoss documentation and on the JBoss wiki http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossClassLoadingUseCases
I can see from the above that I've got an object created using the classloader of the original EJB's EAR. I was expecting to deserialize this object into my JSP's classloader context, when all would have been well. This happens when you are using JNDI from a different host to your EJB. But here I'm on the same host, and the appserver has optimised things by giving me a direct reference instead of serializing and deserializing the object for me. I'm going to have to find a way to convince it that I really want to detype the home object - or alternatively share classloaders between EJB and WAR and give up hope of clean redeployment of the EJB or WAR on their own.
Joined: May 11, 2005
This is JBoss specific, but part of the solution to my problem was to change the lookup to use the full schema, so <context>.lookup("app/ejb.home.class") became <context>.lookup("jnp://app/ejb.home.class"). This gives JBoss 4 a hint that you want to do the same thing that a remote client does using JNDI. This is wasteful in time and memory because two classes running in the same JVM (but using different classloaders) communicate an object by serializing it to a byte buffer then deserializing it into a new object loaded with the second classloader. If they shared classloaders they could just pass a Java reference...
Joined: May 11, 2005
JBoss specific but...
I also had to fix the invoker stack for the EJB... there may be an easier way to do this! This is in <server>/conf/standardjboss.xml
You look for whichever <invoker-proxy-binding> covers the EJB type you are interested in. In my case this was stateless beans. I commented out the invoker interceptor and the marshalling invoker interceptor, and replaced them with:
so that whatever the EJB's META-INF/jboss.xml said I always use remote call-by-value (i.e. serialization/deserialization) in calling EJBs.
This affects all EJBs of that type (i.e. stateless for me) and will SLOW down your apps if JBoss habitually optimises calls between locally hosted EJBs - because this change prevents JBoss from doing any such optimisations.
You can fix this sort of thing on an individual EJB by putting a similar bit of XML in the bean's jboss.xml... but I haven't had to do this so I can't post details. Take a look in the docs relating to the client interceptor stack.