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.beans.IntrospectionException;
49 import java.lang.reflect.Constructor;
50 import java.lang.reflect.Method;
51 import java.security.AccessController;
52 import java.security.PrivilegedAction;
53 import java.util.ArrayList;
54 import java.util.Collections;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.WeakHashMap;
59
60 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
61 import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
62 import org.codehaus.groovy.runtime.MethodHelper;
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 * @version $Revision: 1.26 $
71 */
72 /***
73 * @author John Wilson
74 *
75 */
76 public class MetaClassRegistry {
77 private Map metaClasses = Collections.synchronizedMap(new WeakHashMap());
78 private boolean useAccessible;
79 private Map loaderMap = Collections.synchronizedMap(new WeakHashMap());
80 private GroovyClassLoader loader =
81 (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
82 public Object run() {
83 return new GroovyClassLoader(getClass().getClassLoader());
84 }
85 });
86
87 public static final int LOAD_DEFAULT = 0;
88 public static final int DONT_LOAD_DEFAULT = 1;
89 private static MetaClassRegistry instanceInclude;
90 private static MetaClassRegistry instanceExclude;
91
92
93 public MetaClassRegistry() {
94 this(LOAD_DEFAULT, true);
95 }
96
97 public MetaClassRegistry(int loadDefault) {
98 this(loadDefault, true);
99 }
100
101 /***
102 * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject.setAccessible();}
103 * method will be called to enable access to all methods when using reflection
104 */
105 public MetaClassRegistry(boolean useAccessible) {
106 this(LOAD_DEFAULT, useAccessible);
107 }
108
109 public MetaClassRegistry(final int loadDefault, final boolean useAccessible) {
110 this.useAccessible = useAccessible;
111
112 if (loadDefault == LOAD_DEFAULT) {
113
114 lookup(DefaultGroovyMethods.class);
115 registerMethods(DefaultGroovyMethods.class, true);
116 lookup(DefaultGroovyStaticMethods.class);
117 registerMethods(DefaultGroovyStaticMethods.class, false);
118 checkInitialised();
119 }
120 }
121
122 private void registerMethods(final Class theClass, final boolean instanceMethods) {
123 Method[] methods = theClass.getMethods();
124 for (int i = 0; i < methods.length; i++) {
125 Method method = methods[i];
126 if (MethodHelper.isStatic(method)) {
127 Class[] paramTypes = method.getParameterTypes();
128 if (paramTypes.length > 0) {
129 Class owner = paramTypes[0];
130 if (instanceMethods) {
131 lookup(owner).addNewInstanceMethod(method);
132 } else {
133 lookup(owner).addNewStaticMethod(method);
134 }
135 }
136 }
137 }
138 }
139
140 public MetaClass getMetaClass(Class theClass) {
141 synchronized (theClass) {
142 MetaClass answer = (MetaClass) metaClasses.get(theClass);
143 if (answer == null) {
144 answer = getMetaClassFor(theClass);
145 answer.checkInitialised();
146 metaClasses.put(theClass, answer);
147 }
148 return answer;
149 }
150 }
151
152 public void removeMetaClass(Class theClass) {
153 metaClasses.remove(theClass);
154 }
155
156
157 /***
158 * Registers a new MetaClass in the registry to customize the type
159 *
160 * @param theClass
161 * @param theMetaClass
162 */
163 public void setMetaClass(Class theClass, MetaClass theMetaClass) {
164 metaClasses.put(theClass, theMetaClass);
165 }
166
167 public boolean useAccessible() {
168 return useAccessible;
169 }
170
171 /***
172 * A helper class to load meta class bytecode into the class loader
173 */
174 public Class loadClass(final String name, final byte[] bytecode) throws ClassNotFoundException {
175 return (Class) AccessController.doPrivileged(new PrivilegedAction() {
176 public Object run() {
177 return getGroovyLoader(loader).defineClass(name, bytecode, getClass().getProtectionDomain());
178 }
179 });
180 }
181
182 public Class loadClass(final ClassLoader loader, final String name, final byte[] bytecode) throws ClassNotFoundException {
183 return (Class) AccessController.doPrivileged(new PrivilegedAction() {
184 public Object run() {
185 return getGroovyLoader(loader).defineClass(name, bytecode, getClass().getProtectionDomain());
186 }
187 });
188 }
189
190 public Class loadClass(ClassLoader loader, String name) throws ClassNotFoundException {
191 return getGroovyLoader(loader).loadClass(name);
192 }
193
194 public Class loadClass(String name) throws ClassNotFoundException {
195 return getGroovyLoader(loader).loadClass(name);
196 }
197
198 private GroovyClassLoader getGroovyLoader(ClassLoader loader) {
199 if (loader instanceof GroovyClassLoader) {
200 return (GroovyClassLoader) loader;
201 }
202
203 synchronized (loaderMap) {
204 GroovyClassLoader groovyLoader = (GroovyClassLoader) loaderMap.get(loader);
205 if (groovyLoader == null) {
206 if (loader == null || loader == getClass().getClassLoader()) {
207 groovyLoader = this.loader;
208 }
209 else {
210
211
212 try {
213 loader.loadClass(getClass().getName());
214
215
216 groovyLoader = new GroovyClassLoader(loader);
217 }
218 catch (ClassNotFoundException e) {
219
220
221
222 final ClassLoader localLoader = getClass().getClassLoader();
223 groovyLoader = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
224 public Object run() {
225 return new GroovyClassLoader(localLoader);
226 }
227 });
228 }
229 }
230 loaderMap.put(loader, groovyLoader);
231 }
232
233 return groovyLoader;
234 }
235 }
236
237 /***
238 * Ensures that all the registered MetaClass instances are initalized
239 */
240 void checkInitialised() {
241
242
243 List list = new ArrayList(metaClasses.values());
244 for (Iterator iter = list.iterator(); iter.hasNext();) {
245 MetaClass metaClass = (MetaClass) iter.next();
246 metaClass.checkInitialised();
247 }
248 }
249
250 /***
251 * Used by MetaClass when registering new methods which avoids initializing the MetaClass instances on lookup
252 */
253 MetaClass lookup(Class theClass) {
254 MetaClass answer = (MetaClass) metaClasses.get(theClass);
255 if (answer == null) {
256 answer = getMetaClassFor(theClass);
257 metaClasses.put(theClass, answer);
258 }
259 return answer;
260 }
261
262 /***
263 * Find a MetaClass for the class
264 * If there is a custom MetaClass then return an instance of that. Otherwise return an instance of the standard MetaClass
265 *
266 * @param theClass
267 * @return An instace of the MetaClass which will handle this class
268 */
269 private MetaClass getMetaClassFor(final Class theClass) {
270 try {
271 final Class customMetaClass = Class.forName("groovy.runtime.metaclass." + theClass.getName() + "MetaClass");
272 final Constructor customMetaClassConstructor = customMetaClass.getConstructor(new Class[]{MetaClassRegistry.class, Class.class});
273
274 return (MetaClass)customMetaClassConstructor.newInstance(new Object[]{this, theClass});
275 } catch (final ClassNotFoundException e) {
276 try {
277 return new MetaClassImpl(this, theClass);
278 } catch (final IntrospectionException e1) {
279 throw new GroovyRuntimeException("Could not introspect class: " + theClass.getName() + ". Reason: " + e1, e1);
280 }
281 } catch (final Exception e) {
282 throw new GroovyRuntimeException("Could not instantiate custom Metaclass for class: " + theClass.getName() + ". Reason: " + e, e);
283 }
284 }
285
286 public MetaMethod getDefinedMethod(Class theClass, String methodName, Class[] args, boolean isStatic) {
287 MetaClass metaclass = this.getMetaClass(theClass);
288 if (metaclass == null) {
289 return null;
290 }
291 else {
292 if (isStatic) {
293 return metaclass.retrieveStaticMethod(methodName, args);
294 }
295 else {
296 return metaclass.retrieveMethod(methodName, args);
297 }
298 }
299 }
300
301 public Constructor getDefinedConstructor(Class theClass, Class[] args) {
302 MetaClass metaclass = this.getMetaClass(theClass);
303 if (metaclass == null) {
304 return null;
305 }
306 else {
307 return metaclass.retrieveConstructor(args);
308 }
309 }
310
311 /***
312 * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance?
313 *
314 * @param includeExtension
315 * @return
316 */
317 public static MetaClassRegistry getIntance(int includeExtension) {
318 if (includeExtension != DONT_LOAD_DEFAULT) {
319 if (instanceInclude == null) {
320 instanceInclude = new MetaClassRegistry();
321 }
322 return instanceInclude;
323 }
324 else {
325 if (instanceExclude == null) {
326 instanceExclude = new MetaClassRegistry(DONT_LOAD_DEFAULT);
327 }
328 return instanceExclude;
329 }
330 }
331 }