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 import org.codehaus.groovy.runtime.ReflectorLoader;
64
65 /***
66 * A registery of MetaClass instances which caches introspection &
67 * reflection information and allows methods to be dynamically added to
68 * existing classes at runtime
69 *
70 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
71 * @version $Revision: 1.27 $
72 */
73 /***
74 * @author John Wilson
75 *
76 */
77 public class MetaClassRegistry {
78 private Map metaClasses = Collections.synchronizedMap(new WeakHashMap());
79 private boolean useAccessible;
80 private Map loaderMap = Collections.synchronizedMap(new WeakHashMap());
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();}
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 lookup(DefaultGroovyMethods.class);
110 registerMethods(DefaultGroovyMethods.class, true);
111 lookup(DefaultGroovyStaticMethods.class);
112 registerMethods(DefaultGroovyStaticMethods.class, false);
113 checkInitialised();
114 }
115 }
116
117 private void registerMethods(final Class theClass, final boolean instanceMethods) {
118 Method[] methods = theClass.getMethods();
119 for (int i = 0; i < methods.length; i++) {
120 Method method = methods[i];
121 if (MethodHelper.isStatic(method)) {
122 Class[] paramTypes = method.getParameterTypes();
123 if (paramTypes.length > 0) {
124 Class owner = paramTypes[0];
125 if (instanceMethods) {
126 lookup(owner).addNewInstanceMethod(method);
127 } else {
128 lookup(owner).addNewStaticMethod(method);
129 }
130 }
131 }
132 }
133 }
134
135 public MetaClass getMetaClass(Class theClass) {
136 synchronized (theClass) {
137 MetaClass answer = (MetaClass) metaClasses.get(theClass);
138 if (answer == null) {
139 answer = getMetaClassFor(theClass);
140 answer.checkInitialised();
141 metaClasses.put(theClass, answer);
142 }
143 return answer;
144 }
145 }
146
147 public void removeMetaClass(Class theClass) {
148 metaClasses.remove(theClass);
149 }
150
151
152 /***
153 * Registers a new MetaClass in the registry to customize the type
154 *
155 * @param theClass
156 * @param theMetaClass
157 */
158 public void setMetaClass(Class theClass, MetaClass theMetaClass) {
159 metaClasses.put(theClass, theMetaClass);
160 }
161
162 public boolean useAccessible() {
163 return useAccessible;
164 }
165
166 /***
167 * A helper class to load meta class bytecode into the class loader
168 */
169 public Class createReflectorClass(final ClassLoader parent, final String name, final byte[] bytecode) throws ClassNotFoundException {
170 return (Class) AccessController.doPrivileged(new PrivilegedAction() {
171 public Object run() {
172 return getReflectorLoader(parent).defineClass(name, bytecode, getClass().getProtectionDomain());
173 }
174 });
175 }
176
177 private ReflectorLoader getReflectorLoader(final ClassLoader loader) {
178 synchronized (loaderMap) {
179 ReflectorLoader reflectorLoader = (ReflectorLoader) loaderMap.get(loader);
180 if (reflectorLoader == null) {
181 reflectorLoader = (ReflectorLoader) AccessController.doPrivileged(new PrivilegedAction() {
182 public Object run() {
183 return new ReflectorLoader(loader);
184 }
185 });
186 loaderMap.put(loader, reflectorLoader);
187 }
188 return reflectorLoader;
189 }
190 }
191
192 /***
193 * Ensures that all the registered MetaClass instances are initalized
194 */
195 void checkInitialised() {
196
197
198 List list = new ArrayList(metaClasses.values());
199 for (Iterator iter = list.iterator(); iter.hasNext();) {
200 MetaClass metaClass = (MetaClass) iter.next();
201 metaClass.checkInitialised();
202 }
203 }
204
205 /***
206 * Used by MetaClass when registering new methods which avoids initializing the MetaClass instances on lookup
207 */
208 MetaClass lookup(Class theClass) {
209 MetaClass answer = (MetaClass) metaClasses.get(theClass);
210 if (answer == null) {
211 answer = getMetaClassFor(theClass);
212 metaClasses.put(theClass, answer);
213 }
214 return answer;
215 }
216
217 /***
218 * Find a MetaClass for the class
219 * If there is a custom MetaClass then return an instance of that. Otherwise return an instance of the standard MetaClass
220 *
221 * @param theClass
222 * @return An instace of the MetaClass which will handle this class
223 */
224 private MetaClass getMetaClassFor(final Class theClass) {
225 try {
226 final Class customMetaClass = Class.forName("groovy.runtime.metaclass." + theClass.getName() + "MetaClass");
227 final Constructor customMetaClassConstructor = customMetaClass.getConstructor(new Class[]{MetaClassRegistry.class, Class.class});
228
229 return (MetaClass)customMetaClassConstructor.newInstance(new Object[]{this, theClass});
230 } catch (final ClassNotFoundException e) {
231 try {
232 return new MetaClassImpl(this, theClass);
233 } catch (final IntrospectionException e1) {
234 throw new GroovyRuntimeException("Could not introspect class: " + theClass.getName() + ". Reason: " + e1, e1);
235 }
236 } catch (final Exception e) {
237 throw new GroovyRuntimeException("Could not instantiate custom Metaclass for class: " + theClass.getName() + ". Reason: " + e, e);
238 }
239 }
240
241 public MetaMethod getDefinedMethod(Class theClass, String methodName, Class[] args, boolean isStatic) {
242 MetaClass metaclass = this.getMetaClass(theClass);
243 if (metaclass == null) {
244 return null;
245 }
246 else {
247 if (isStatic) {
248 return metaclass.retrieveStaticMethod(methodName, args);
249 }
250 else {
251 return metaclass.retrieveMethod(methodName, args);
252 }
253 }
254 }
255
256 public Constructor getDefinedConstructor(Class theClass, Class[] args) {
257 MetaClass metaclass = this.getMetaClass(theClass);
258 if (metaclass == null) {
259 return null;
260 }
261 else {
262 return metaclass.retrieveConstructor(args);
263 }
264 }
265
266 /***
267 * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance?
268 *
269 * @param includeExtension
270 * @return
271 */
272 public static MetaClassRegistry getIntance(int includeExtension) {
273 if (includeExtension != DONT_LOAD_DEFAULT) {
274 if (instanceInclude == null) {
275 instanceInclude = new MetaClassRegistry();
276 }
277 return instanceInclude;
278 }
279 else {
280 if (instanceExclude == null) {
281 instanceExclude = new MetaClassRegistry(DONT_LOAD_DEFAULT);
282 }
283 return instanceExclude;
284 }
285 }
286 }