David Blevins wrote:
On May 21, 2008, at 5:04 PM, Dain Sundstrom wrote:
I think Rick may be able to provide more insight than me given his
work on vm class loading, but I'll take a shot at this one....
I think the difference between the two exceptions is based on when
the VM discovers it has an invalid class. The second exception shows
us that the bad class is openejb.org.superbiz.cmp2.MovieBean, which
is a class we generated directly using ASM. In "Style 1", I think
something about the code in EjbHomeProxyHandler was causing the VM to
perform a full verify on the class (FWIU the vm only verifies what is
needs to improve VM startup time). Was EjbHomeProxyHandler doing
something like scanning all the Method objects? Anyway, in "Style
1", for some reason the vm didn't have (or didn't provide) additional
information about which class was having the error, which based on my
coding experience is a pretty common situation. In "Style 2", the vm
clearly knows what's going on and tells us everything necessary to
fix the problem.
In the long term, we could catch this problem with the verifier or if
they turn that off, we could just fill in a do nothing implementation.
Huh. Not sure. We're talking about zero code change anywhere in
anything (even the test case) other than the following patch.
I'm not sure I'm buying the verifier answer either. The verifier will
fully validate a class before allowing it ever to be used for any
purpose. Any path that will cause a failure will make the class
non-usable....and result in a verifier error, not something like a class
cast exception.
I'm thinking perhaps the problem might be you're getting a
ClassCastException on the throwable line itself. The line
cause = new EJBException(re.getMessage(), (Exception) detail);
will cause a ClassCastException because detail is an instance of
AbstractMethodError, which is not a subclass of Exception.
Unfortunately, you can't just remove the (Exception) cast, because
that's the type specified on the constructor. It looks like the
initClause() form is the only way to get what you want out of this.
Rick
Index: src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
===================================================================
---
src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
(revision 655683)
+++
src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
(working copy)
@@ -210,7 +210,7 @@
if (cause instanceof RemoteException &&
interfaceType.isLocal()) {
RemoteException re = (RemoteException) cause;
Throwable detail = (re.detail != null) ? re.detail : re;
- cause = new EJBException(re.getMessage(), (Exception)
detail);
+ cause = new
EJBException(re.getMessage()).initCause(detail);
}
throw cause;
/*
I'm not sure how class verification, etc. could be affected. Taking a
fresh look at this, the ClassCastException part of Style 1 looks
awfully fishy...
Yea, that's it. Can't believe I missed it.
-David
-dain
On May 21, 2008, at 4:52 PM, David Blevins wrote:
There seems to be some major difference between these two styles of
EJBException creation. I ran into this working on a CMP example I
intend to use for the whole CMP+JPA->JPA merging logic. The real
error is that I forgot to implement "setEntityContext" which is an
easy mistake to make as the CMP2 bean class is abstract and compiles
just fine without it. I got the first stack trace below, which is
useless, and decided on a whim to tweak EjbHomeProxyHandler line 213
to the second style shown below. I was and am shocked at the
difference in the stack trace.
[Style 1] throw new EJBException(re.getMessage(), (Exception)
detail);
Yields:
java.lang.ClassCastException: java.lang.AbstractMethodError
at
org.apache.openejb.core.ivm.EjbHomeProxyHandler._invoke(EjbHomeProxyHandler.java:213)
at
org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:274)
at org.superbiz.cmp2.$Proxy26.create(Unknown Source)
at org.superbiz.cmp2.MoviesTest.test(MoviesTest.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at
com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:32)
[Style 2] throw new EJBException(re.getMessage()).initCause(detail);
Yields:
javax.ejb.EJBException: The bean encountered a non-application
exception.; nested exception is:
java.lang.AbstractMethodError:
openejb.org.superbiz.cmp2.MovieBean.setEntityContext(Ljavax/ejb/EntityContext;)V
at
org.apache.openejb.core.ivm.EjbHomeProxyHandler._invoke(EjbHomeProxyHandler.java:213)
at
org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:274)
at org.superbiz.cmp2.$Proxy26.create(Unknown Source)
at org.superbiz.cmp2.MoviesTest.test(MoviesTest.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at
com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:32)
Caused by: java.lang.AbstractMethodError:
openejb.org.superbiz.cmp2.MovieBean.setEntityContext(Ljavax/ejb/EntityContext;)V
at
org.apache.openejb.core.cmp.CmpContainer.setEntityContext(CmpContainer.java:318)
at
org.apache.openejb.core.cmp.CmpContainer.createEJBObject(CmpContainer.java:592)
at
org.apache.openejb.core.cmp.CmpContainer.invoke(CmpContainer.java:250)
at
org.apache.openejb.core.ivm.EjbHomeProxyHandler.create(EjbHomeProxyHandler.java:267)
at
org.apache.openejb.core.ivm.EjbHomeProxyHandler._invoke(EjbHomeProxyHandler.java:158)
... 21 more
Able to verify the stack trace behavior is this way in both intellij
and maven. Obviously the stack trace for style #1 completely
obfuscates the problem which is itself a big problem.
Anyone have any idea, what causes this difference?
Needless to say, I'll shortly be switching all our code over....
-David