--- src/main/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java (revision 8048) +++ src/main/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java Wed Sep 12 11:40:36 BST 2007 @@ -15,23 +15,35 @@ */ package org.codehaus.groovy.runtime.metaclass; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.Iterator; +import java.util.logging.Logger; +import java.util.logging.Level; + import groovy.lang.GroovyRuntimeException; import groovy.lang.MetaClass; import groovy.lang.MetaClassRegistry; -import org.codehaus.groovy.reflection.CachedMethod; import org.codehaus.groovy.classgen.ReflectorGenerator; +import org.codehaus.groovy.reflection.CachedMethod; import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods; import org.codehaus.groovy.runtime.Reflector; import org.objectweb.asm.ClassWriter; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.LinkedList; -import java.util.List; - /** * A registry of MetaClass instances which caches introspection & * reflection information and allows methods to be dynamically added to @@ -45,6 +57,8 @@ * @version $Revision: 8034 $ */ public class MetaClassRegistryImpl implements MetaClassRegistry{ + protected static final Logger LOG = Logger.getLogger(MetaClassRegistryImpl.class.getName()); + private volatile int constantMetaClassCount = 0; private ConcurrentReaderHashMap constantMetaClasses = new ConcurrentReaderHashMap(); private MemoryAwareConcurrentReadMap weakMetaClasses = new MemoryAwareConcurrentReadMap(); @@ -82,12 +96,86 @@ // lets register the default methods registerMethods(DefaultGroovyMethods.class, true); registerMethods(DefaultGroovyStaticMethods.class, false); + + // let attempt to load any other extension classes... + Collection methods = findExtensionClasses("META-INF/services/groovy/groovyMethods"); + registerMethods(methods, true); + + Collection staticMethods = findExtensionClasses("META-INF/services/groovy/groovyStaticMethods"); + registerMethods(staticMethods, false); } installMetaClassCreationHandle(); - } + } /** + * Finds all the extension classes for the given resourceUri which should point to one or more files on the classpath + * containing a list of classes (comma separated) + */ + protected Collection findExtensionClasses(String resourceUri) { + Set answer = new HashSet(); + try { + findExtensionClasses(answer, resourceUri, Thread.currentThread().getContextClassLoader()); + findExtensionClasses(answer, resourceUri, getClass().getClassLoader()); + } + catch (Exception e) { + LOG.log(Level.WARNING, "Failed to load extension: " + resourceUri + ". Reason: " + e, e); + } + return answer; + } + + private void findExtensionClasses(Set answer, String resourceUri, ClassLoader classLoader) throws IOException { + Enumeration resources = classLoader.getResources(resourceUri); + while (resources.hasMoreElements()) { + URL url = (URL) resources.nextElement(); + if (url != null) { + BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); + try { + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + line = line.trim(); + if (line.startsWith("#") || line.length() == 0) { + continue; + } + tokenize(answer, line, classLoader); + } + } + finally { + try { + reader.close(); + } + catch (IOException e) { + LOG.log(Level.WARNING, "Caught exception closing stream: " + e, e); + } + } + } + } + } + + /** + * Tokenizes the line from the META-IN/services file using commas and + * ignoring whitespace between class names then loading the classes + */ + private void tokenize(Set answer, String line, ClassLoader classLoader) { + StringTokenizer iter = new StringTokenizer(line, ","); + while (iter.hasMoreTokens()) { + String name = iter.nextToken().trim(); + if (name.length() > 0) { + try { + Class aClass = classLoader.loadClass(name); + answer.add(aClass); + } + catch (ClassNotFoundException e) { + LOG.log(Level.WARNING, "Failed to load class: " + name + " due to: " + e, e); + } + } + } + } + + /** * Looks for a class called 'groovy.runtime.metaclass.CustomMetaClassCreationHandle' and if it exists uses it as the MetaClassCreationHandle * otherwise uses the default * @@ -104,8 +192,18 @@ throw new GroovyRuntimeException("Could not instantiate custom Metaclass creation handle: "+ e, e); } } - + + private void registerMethods(Collection types, boolean useInstanceMethods) { + Iterator iterator = types.iterator(); + while (iterator.hasNext()) { + Class type = (Class) iterator.next(); + registerMethods(type, useInstanceMethods); + } + } + private void registerMethods(final Class theClass, final boolean useInstanceMethods) { + LOG.fine("Logging methods for class: " + theClass + " instanceMethods: " + useInstanceMethods); + Method[] methods = theClass.getMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i];