1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package groovy.lang;
47
48 import java.lang.reflect.Constructor;
49 import java.lang.reflect.Method;
50 import java.security.AccessController;
51 import java.security.PrivilegedAction;
52 import java.util.LinkedList;
53 import java.util.List;
54
55 import org.codehaus.groovy.classgen.ReflectorGenerator;
56 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
57 import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
58 import org.codehaus.groovy.runtime.MethodHelper;
59 import org.codehaus.groovy.runtime.ReferenceMap;
60 import org.codehaus.groovy.runtime.Reflector;
61 import org.codehaus.groovy.runtime.ReflectorLoader;
62 import org.objectweb.asm.ClassWriter;
63
64 /***
65 * A registery of MetaClass instances which caches introspection &
66 * reflection information and allows methods to be dynamically added to
67 * existing classes at runtime
68 *
69 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
70 * @author John Wilson
71 * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
72 * @version $Revision: 4554 $
73 */
74 public class MetaClassRegistry {
75 private ReferenceMap metaClasses = new ReferenceMap();
76 private ReferenceMap loaderMap = new ReferenceMap();
77 private boolean useAccessible;
78
79 private LinkedList instanceMethods = new LinkedList();
80 private LinkedList staticMethods = new LinkedList();
81
82 public static final int LOAD_DEFAULT = 0;
83 public static final int DONT_LOAD_DEFAULT = 1;
84 private static MetaClassRegistry instanceInclude;
85 private static MetaClassRegistry instanceExclude;
86
87
88 public MetaClassRegistry() {
89 this(LOAD_DEFAULT, true);
90 }
91
92 public MetaClassRegistry(int loadDefault) {
93 this(loadDefault, true);
94 }
95
96 /***
97 * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
98 * method will be called to enable access to all methods when using reflection
99 */
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
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
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 }