
| Key: |
GROOVY-375
|
| Type: |
Improvement
|
| Status: |
Closed
|
| Resolution: |
Fixed
|
| Priority: |
Minor
|
| Assignee: |
Unassigned
|
| Reporter: |
Scott Ferguson
|
| Votes: |
12
|
| Watchers: |
5
|
|
If you were logged in you would be able to see more operations.
|
|
|
|
Environment:
|
Any application-server environment with multiple classloaders. In particular, Resin.
|
|
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() {
|
|
Description
|
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() { |
Show » |
Sort Order:
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
]
|
|
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