Issue Details (XML | Word | Printable)

Key: GROOVY-2980
Type: Bug Bug
Status: Open Open
Priority: Major Major
Assignee: Unassigned
Reporter: Alexander Veit
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
groovy

NoClassDefFoundError as a side effect of a POJO method invocation

Created: 29/Jul/08 03:24 PM   Updated: 31/Jul/09 12:10 PM
Return to search
Component/s: None
Affects Version/s: 1.5.6
Fix Version/s: 1.7.X

Time Tracking:
Not Specified


 Description  « Hide

As a result of a POJO method call groovy.lang.MetaClassImpl.initialize() indirectly calls java.lang.Class.getDeclaredMethods().

This leads to a NoClassDefFoundError if one of the class' method signatures references a type that is not in the classpath - event though the method names are different.

The same code works in Java with the same classpath.



Jochen Theodorou added a comment - 29/Jul/08 03:29 PM

can you give an example for the POJO and explain which classes are in the classpath and which classes are not?


Alexander Veit added a comment - 29/Jul/08 03:37 PM

The class that was in the classpath is
com.sap.mw.jco.JCO.Client
http://help.sap.com/javadocs/NW04S/current/jc/com/sap/mw/jco/JCO.Client.html

The class that was not in the classpath is
com.sap.jdsr.writer.DsrIPassport


Jochen Theodorou added a comment - 29/Jul/08 03:41 PM

so it is the parameter class of a method, that is missing...


Alexander Veit added a comment - 29/Jul/08 03:45 PM

Yes.


Jochen Theodorou added a comment - 29/Jul/08 03:47 PM

ok, verified the problem, and we have that since 1.0 already. I am not sure what we can do about that. But we can probably catch the exception and react accordingly.


Alexander Veit added a comment - 29/Jul/08 04:04 PM

I suspect that a lazy method population algorith - if possible - could slow down the Groovy runtime.

I don't know how severe the impact is in practice. Has a custumer who had licensed lib1.jar normally also had licensed the missing lib2.jar?

It's maybe a good idea to leave this issue open until it's relevance becomes more clear.


Jochen Theodorou added a comment - 29/Jul/08 04:11 PM

it doesn't need to be more lazy that it is atm... we just have to mark the method somehow, so the runtime knows that for this method not all classes are resolved. Any call to such a method results in an error anyway.


Alexander Veit added a comment - 29/Jul/08 04:23 PM

Hmm, here's part of the stack trace we received:

Caused by: java.lang.NoClassDefFoundError: com/sap/jdsr/writer/DsrIPassport
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
at java.lang.Class.getDeclaredMethods(Class.java:1791)
at org.codehaus.groovy.reflection.CachedClass$1.run(CachedClass.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at org.codehaus.groovy.reflection.CachedClass.getMethods(CachedClass.java:171)
at groovy.lang.MetaClassImpl.populateMethods(MetaClassImpl.java:235)
at groovy.lang.MetaClassImpl.fillMethodIndex(MetaClassImpl.java:214)
at groovy.lang.MetaClassImpl.initialize(MetaClassImpl.java:2478)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getGlobalMetaClass(MetaClassRegistryImpl.java:253)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.access$100(MetaClassRegistryImpl.java:45)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$LocallyKnownClasses.getFromGlobal(MetaClassRegistryImpl.java:112)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$LocallyKnownClasses.getMetaClass(MetaClassRegistryImpl.java:88)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$MyThreadLocal.getMetaClass(MetaClassRegistryImpl.java:361)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:265)
at org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:765)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:754)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
at script1217180211687.run(script1217180211687.groovy:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
... 38 more
Caused by: java.lang.ClassNotFoundException: com.sap.jdsr.writer.DsrIPassport
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
... 62 more

From this i concluded that if java.lang.Class.getDeclaredMethods() would throw an error no method of the POJO would be visible from Groovy.


Jochen Theodorou added a comment - 29/Jul/08 04:40 PM

hmm.. that's true... I forgot that you can not simply get the method without getting all of them or by knowing the name... in that case I doubt we will find a solution for this.. unless the MetaClass is created in a very different way... like for example by using pre-calculated meta class information as some kind of database.


Alexander Veit added a comment - 29/Jul/08 05:02 PM

... or using a different reflection library, if feasible, maybe objectweb asm.


Jochen Theodorou added a comment - 29/Jul/08 05:12 PM

objectweb asm is not a reflection library. You need the bytecode for the asm lib, if you do not have it, then you have a problem. Of course the bytecode exists somewhere and there is a way to get it if the class is loaded froma file and the classloader is generous enough to place the informations you need to find the .jar/.class and of course the asm lib has all this builtin already... but there are also cases when there is no file. If the class is generated at runtime or if the class comes from a db or whatever, then it will be a problem...

Well unless you know a way to get the bytecode as bye[] at runtime without the need of having the class as file somewhere. I would be very interested in knowing


Alexander Veit added a comment - 30/Jul/08 03:54 PM

The only place where the original class bytes can be seen is probably within ClassLoaders (where calls to the defineClass methods are made) or instrumentation agents (before defineClass calls). I doubt that the original class byte array is kept in memory by common ClassLoaders.

So, given current Groovy byte code, there's probably no chance to fix this bug in an efficient way unless the Java reflection API will provide a way to do so (e.g. by an alternative method iteration pattern, or by lazy instantiation of parameter, return and exception types within the Method class).