001 /* 002 $Id: MetaClassRegistry.java 4554 2006-12-21 23:54:28Z blackdrag $ 003 004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 006 Redistribution and use of this software and associated documentation 007 ("Software"), with or without modification, are permitted provided 008 that the following conditions are met: 009 010 1. Redistributions of source code must retain copyright 011 statements and notices. Redistributions must also contain a 012 copy of this document. 013 014 2. Redistributions in binary form must reproduce the 015 above copyright notice, this list of conditions and the 016 following disclaimer in the documentation and/or other 017 materials provided with the distribution. 018 019 3. The name "groovy" must not be used to endorse or promote 020 products derived from this Software without prior written 021 permission of The Codehaus. For written permission, 022 please contact info@codehaus.org. 023 024 4. Products derived from this Software may not be called "groovy" 025 nor may "groovy" appear in their names without prior written 026 permission of The Codehaus. "groovy" is a registered 027 trademark of The Codehaus. 028 029 5. Due credit should be given to The Codehaus - 030 http://groovy.codehaus.org/ 031 032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 043 OF THE POSSIBILITY OF SUCH DAMAGE. 044 045 */ 046 package groovy.lang; 047 048 import java.lang.reflect.Constructor; 049 import java.lang.reflect.Method; 050 import java.security.AccessController; 051 import java.security.PrivilegedAction; 052 import java.util.LinkedList; 053 import java.util.List; 054 055 import org.codehaus.groovy.classgen.ReflectorGenerator; 056 import org.codehaus.groovy.runtime.DefaultGroovyMethods; 057 import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods; 058 import org.codehaus.groovy.runtime.MethodHelper; 059 import org.codehaus.groovy.runtime.ReferenceMap; 060 import org.codehaus.groovy.runtime.Reflector; 061 import org.codehaus.groovy.runtime.ReflectorLoader; 062 import org.objectweb.asm.ClassWriter; 063 064 /** 065 * A registery of MetaClass instances which caches introspection & 066 * reflection information and allows methods to be dynamically added to 067 * existing classes at runtime 068 * 069 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 070 * @author John Wilson 071 * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a> 072 * @version $Revision: 4554 $ 073 */ 074 public class MetaClassRegistry { 075 private ReferenceMap metaClasses = new ReferenceMap(); 076 private ReferenceMap loaderMap = new ReferenceMap(); 077 private boolean useAccessible; 078 079 private LinkedList instanceMethods = new LinkedList(); 080 private LinkedList staticMethods = new LinkedList(); 081 082 public static final int LOAD_DEFAULT = 0; 083 public static final int DONT_LOAD_DEFAULT = 1; 084 private static MetaClassRegistry instanceInclude; 085 private static MetaClassRegistry instanceExclude; 086 087 088 public MetaClassRegistry() { 089 this(LOAD_DEFAULT, true); 090 } 091 092 public MetaClassRegistry(int loadDefault) { 093 this(loadDefault, true); 094 } 095 096 /** 097 * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} 098 * method will be called to enable access to all methods when using reflection 099 */ 100 public MetaClassRegistry(boolean useAccessible) { 101 this(LOAD_DEFAULT, useAccessible); 102 } 103 104 public MetaClassRegistry(final int loadDefault, final boolean useAccessible) { 105 this.useAccessible = useAccessible; 106 107 if (loadDefault == LOAD_DEFAULT) { 108 // lets register the default methods 109 registerMethods(DefaultGroovyMethods.class, true); 110 registerMethods(DefaultGroovyStaticMethods.class, false); 111 } 112 } 113 114 private void registerMethods(final Class theClass, final boolean useInstanceMethods) { 115 Method[] methods = theClass.getMethods(); 116 for (int i = 0; i < methods.length; i++) { 117 Method method = methods[i]; 118 if (MethodHelper.isStatic(method)) { 119 Class[] paramTypes = method.getParameterTypes(); 120 if (paramTypes.length > 0) { 121 if (useInstanceMethods) { 122 instanceMethods.add(method); 123 } else { 124 staticMethods.add(method); 125 } 126 } 127 } 128 } 129 } 130 131 public MetaClass getMetaClass(Class theClass) { 132 synchronized (theClass) { 133 MetaClass answer = (MetaClass) metaClasses.get(theClass); 134 if (answer == null) { 135 answer = getMetaClassFor(theClass); 136 answer.initialize(); 137 metaClasses.put(theClass, answer); 138 } 139 return answer; 140 } 141 } 142 143 public void removeMetaClass(Class theClass) { 144 synchronized (theClass) { 145 metaClasses.remove(theClass); 146 } 147 } 148 149 150 /** 151 * Registers a new MetaClass in the registry to customize the type 152 * 153 * @param theClass 154 * @param theMetaClass 155 */ 156 public void setMetaClass(Class theClass, MetaClass theMetaClass) { 157 synchronized(theClass) { 158 metaClasses.putStrong(theClass, theMetaClass); 159 } 160 } 161 162 public boolean useAccessible() { 163 return useAccessible; 164 } 165 166 private ReflectorLoader getReflectorLoader(final ClassLoader loader) { 167 synchronized (loaderMap) { 168 ReflectorLoader reflectorLoader = (ReflectorLoader) loaderMap.get(loader); 169 if (reflectorLoader == null) { 170 reflectorLoader = (ReflectorLoader) AccessController.doPrivileged(new PrivilegedAction() { 171 public Object run() { 172 return new ReflectorLoader(loader); 173 } 174 }); 175 loaderMap.put(loader, reflectorLoader); 176 } 177 return reflectorLoader; 178 } 179 } 180 181 /** 182 * Used by MetaClass when registering new methods which avoids initializing the MetaClass instances on lookup 183 */ 184 MetaClass lookup(Class theClass) { 185 synchronized (theClass) { 186 MetaClass answer = (MetaClass) metaClasses.get(theClass); 187 if (answer == null) { 188 answer = getMetaClassFor(theClass); 189 metaClasses.put(theClass, answer); 190 } 191 return answer; 192 } 193 } 194 195 /** 196 * Find a MetaClass for the class 197 * If there is a custom MetaClass then return an instance of that. Otherwise return an instance of the standard MetaClass 198 * 199 * @param theClass 200 * @return An instace of the MetaClass which will handle this class 201 */ 202 private MetaClass getMetaClassFor(final Class theClass) { 203 try { 204 final Class customMetaClass = Class.forName("groovy.runtime.metaclass." + theClass.getName() + "MetaClass"); 205 final Constructor customMetaClassConstructor = customMetaClass.getConstructor(new Class[]{MetaClassRegistry.class, Class.class}); 206 207 return (MetaClass)customMetaClassConstructor.newInstance(new Object[]{this, theClass}); 208 } catch (final ClassNotFoundException e) { 209 return new MetaClassImpl(this, theClass); 210 } catch (final Exception e) { 211 throw new GroovyRuntimeException("Could not instantiate custom Metaclass for class: " + theClass.getName() + ". Reason: " + e, e); 212 } 213 } 214 215 /** 216 * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance? 217 * 218 * @param includeExtension 219 */ 220 public static MetaClassRegistry getInstance(int includeExtension) { 221 if (includeExtension != DONT_LOAD_DEFAULT) { 222 if (instanceInclude == null) { 223 instanceInclude = new MetaClassRegistry(); 224 } 225 return instanceInclude; 226 } 227 else { 228 if (instanceExclude == null) { 229 instanceExclude = new MetaClassRegistry(DONT_LOAD_DEFAULT); 230 } 231 return instanceExclude; 232 } 233 } 234 235 public synchronized Reflector loadReflector(final Class theClass, List methods) { 236 final String name = getReflectorName(theClass); 237 ClassLoader loader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() { 238 public Object run() { 239 ClassLoader loader = theClass.getClassLoader(); 240 if (loader == null) loader = this.getClass().getClassLoader(); 241 return loader; 242 } 243 }); 244 final ReflectorLoader rloader = getReflectorLoader(loader); 245 Class ref = rloader.getLoadedClass(name); 246 if (ref == null) { 247 /* 248 * Lets generate it && load it. 249 */ 250 ReflectorGenerator generator = new ReflectorGenerator(methods); 251 ClassWriter cw = new ClassWriter(true); 252 generator.generate(cw, name); 253 final byte[] bytecode = cw.toByteArray(); 254 ref = (Class) AccessController.doPrivileged(new PrivilegedAction() { 255 public Object run() { 256 return rloader.defineClass(name, bytecode, getClass().getProtectionDomain()); 257 } 258 }); 259 } 260 try { 261 return (Reflector) ref.newInstance(); 262 } catch (Exception e) { 263 throw new GroovyRuntimeException("Could not generate and load the reflector for class: " + name + ". Reason: " + e, e); 264 } 265 } 266 267 private String getReflectorName(Class theClass) { 268 String className = theClass.getName(); 269 String packagePrefix = "gjdk."; 270 String name = packagePrefix + className + "_GroovyReflector"; 271 if (theClass.isArray()) { 272 Class clazz = theClass; 273 name = packagePrefix; 274 int level = 0; 275 while (clazz.isArray()) { 276 clazz = clazz.getComponentType(); 277 level++; 278 } 279 String componentName = clazz.getName(); 280 name = packagePrefix + componentName + "_GroovyReflectorArray"; 281 if (level>1) name += level; 282 } 283 return name; 284 } 285 286 List getInstanceMethods() { 287 return instanceMethods; 288 } 289 290 List getStaticMethods() { 291 return staticMethods; 292 } 293 }