Issue Details (XML | Word | Printable)

Key: GROOVY-375
Type: Improvement Improvement
Status: Closed Closed
Resolution: Fixed
Priority: Minor Minor
Assignee: Unassigned
Reporter: Scott Ferguson
Votes: 12
Watchers: 5
Operations

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

classloader enhancements to MetaClass

Created: 08/Apr/04 03:07 PM   Updated: 27/Dec/05 10:51 AM   Resolved: 24/Feb/05 02:30 AM
Return to search
Component/s: None
Affects Version/s: 1.0-beta-4
Fix Version/s: 1.0-beta-10

Time Tracking:
Not Specified

Environment: Any application-server environment with multiple classloaders. In particular, Resin.


 Description  « Hide

The current MetaClass and MetaClassRegistry do not integrate as cleanly in application server environments as they could.

In particular, groovy classes loaded by an application server classloader are stored in a static hashmap, which can prevent GC when the classloaders are dropped.

In addition, dynamically created classes are not loaded properly, but are attempted to be loaded in a global classloader context (which, of course does not work.)

The solutions are:
1) Make the MetaClassRegistry.metaClasses Map as WeakHashMap to allow dropped classes to be garbage collected.
2) Add a MetaClassRegistry.loaderMap to create GroovyClassLoaders on top of the context ClassLoader, instead of dropping to a global GroovyClassLoader.

Assuming this change is implemented, Resin can use groovy generated classes exactly as any other class. (Which would be very, very cool.)

— MetaClassRegistry.java.orig 2004-04-02 19:21:52.000000000 -0800
+++ MetaClassRegistry.java 2004-04-02 19:17:41.000000000 -0800
@@ -48,7 +48,7 @@
import java.beans.IntrospectionException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.WeakHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -64,9 +64,10 @@

  • @version $Revision: 1.10 $
    */
    public class MetaClassRegistry {
  • private Map metaClasses = Collections.synchronizedMap(new HashMap());
    + private Map metaClasses = Collections.synchronizedMap(new WeakHashMap());
    private boolean useAccessible;
    private GroovyClassLoader loader = new GroovyClassLoader(getClass().getClassLoader());
    + private Map loaderMap = Collections.synchronizedMap(new WeakHashMap());

public MetaClassRegistry() {
this(true);
@@ -118,12 +119,33 @@
/**

  • A helper class to load meta class bytecode into the class loader
    */
  • public Class loadClass(String name, byte[] bytecode) throws ClassNotFoundException {
  • return loader.loadClass(name, bytecode);
    + public Class loadClass(ClassLoader loader, String name, byte[] bytecode) throws ClassNotFoundException { + return getGroovyLoader(loader).loadClass(name, bytecode); }
  • public Class loadClass(String name) throws ClassNotFoundException {
  • return loader.loadClass(name);
    + public Class loadClass(ClassLoader loader, String name) throws ClassNotFoundException { + return getGroovyLoader(loader).loadClass(name); + }
    +
    + private GroovyClassLoader getGroovyLoader(ClassLoader loader)
    + {
    + if (loader instanceof GroovyClassLoader)
    + return (GroovyClassLoader) loader;
    +
    + synchronized (loaderMap)
    Unknown macro: {+ GroovyClassLoader groovyLoader;+ groovyLoader = (GroovyClassLoader) loaderMap.get(loader);+ if (groovyLoader == null) { + if (loader == null || loader == getClass().getClassLoader()) + groovyLoader = this.loader; + else + groovyLoader = new GroovyClassLoader(loader); + + loaderMap.put(loader, groovyLoader); + }++ return groovyLoader;+ }

    }

/**

— MetaClass.java.orig 2004-04-02 19:21:41.000000000 -0800
+++ MetaClass.java 2004-04-02 18:53:43.000000000 -0800
@@ -1448,7 +1448,7 @@
GroovyClassLoader gloader = (GroovyClassLoader) loader;
return gloader.loadClass(name, bytecode);
}

  • return registry.loadClass(name, bytecode);
    + return registry.loadClass(loader, name, bytecode);
    }

protected Class loadReflectorClass(String name) throws ClassNotFoundException { @@ -1457,7 +1457,7 @@ GroovyClassLoader gloader = (GroovyClassLoader) loader; return gloader.loadClass(name); }

  • return registry.loadClass(name);
    + return registry.loadClass(loader, name);
    }

public List getMethods() {



Andrey Alexeyenko added a comment - 30/Nov/04 08:11 AM

Unfortunately, it's still true in beta7.

The situation is:

1) class loader A knows about class pkg.X
2) creating GS = new GroovyShell(A)
3) creating Script instance by means of
S1=GS.parse("import pkg.X; ...") - parsing succeeds
4) now running S1.run() fails with NoClassDefFoundError:

java.lang.NoClassDefFoundError: pkg.X
at Script1.class$(Script1.groovy)
at Script1.run(Script1.groovy:1)

Seems it uses default MetaClassRegistry.loader - GroovyClassLoader(getClass().getClassLoader())

while getClass().getClassLoader() apparently knows nothing about pkg.X

It may be alternative issue to include classloader info into Binding like it's done in OGNL


Boris Bokowski added a comment - 14/Feb/05 02:38 PM

I'm hitting the same problem while trying to use Groovy as a scripting engine for my Eclipse RCP application.


james strachan added a comment - 24/Feb/05 02:30 AM

Many thanks for this patch Scott - humblest apologies for not applying it sooner!


james strachan made changes - 24/Feb/05 02:30 AM
Field Original Value New Value
Resolution Fixed [ 1 ]
Status Open [ 1 ] Closed [ 6 ]
Fix Version/s 1.0-beta-10 [ 11467 ]
Peter Severin added a comment - 27/Dec/05 10:51 AM

Please see GROOVY-1194 for a followup to this issue.