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 org.codehaus.groovy.runtime.DefaultGroovyMethods;
49 import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
50
51 import java.beans.IntrospectionException;
52 import java.lang.reflect.Constructor;
53 import java.security.AccessController;
54 import java.security.PrivilegedAction;
55 import java.util.ArrayList;
56 import java.util.Collections;
57 import java.util.Iterator;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.WeakHashMap;
61
62 /***
63 * A registery of MetaClass instances which caches introspection &
64 * reflection information and allows methods to be dynamically added to
65 * existing classes at runtime
66 *
67 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
68 * @version $Revision: 1.19 $
69 */
70 public class MetaClassRegistry {
71 private Map metaClasses = Collections.synchronizedMap(new WeakHashMap());
72 private boolean useAccessible;
73 private Map loaderMap = Collections.synchronizedMap(new WeakHashMap());
74 private GroovyClassLoader loader =
75 (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
76 public Object run() {
77 return new GroovyClassLoader(getClass().getClassLoader());
78 }
79 });
80
81 public static final int LOAD_DEFAULT = 0;
82 public static final int DONT_LOAD_DEFAULT = 1;
83 private static MetaClassRegistry instanceInclude;
84 private static MetaClassRegistry instanceExclude;
85
86
87 public MetaClassRegistry() {
88 this(true);
89 }
90
91 public MetaClassRegistry(int loadDefault) {
92 if (loadDefault == LOAD_DEFAULT) {
93 this.useAccessible = true;
94
95 lookup(DefaultGroovyMethods.class).registerInstanceMethods();
96 lookup(DefaultGroovyStaticMethods.class).registerStaticMethods();
97 checkInitialised();
98 }
99 else {
100 this.useAccessible = true;
101
102 }
103 }
104
105 /***
106 * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject.setAccessible();}
107 * method will be called to enable access to all methods when using reflection
108 */
109 public MetaClassRegistry(boolean useAccessible) {
110 this.useAccessible = useAccessible;
111
112
113 lookup(DefaultGroovyMethods.class).registerInstanceMethods();
114 lookup(DefaultGroovyStaticMethods.class).registerStaticMethods();
115 checkInitialised();
116 }
117
118 public MetaClass getMetaClass(Class theClass) {
119 synchronized (theClass) {
120 MetaClass answer = (MetaClass) metaClasses.get(theClass);
121 if (answer == null) {
122 try {
123 answer = new MetaClass(this, theClass);
124 answer.checkInitialised();
125 }
126 catch (IntrospectionException e) {
127 throw new GroovyRuntimeException("Could not introspect class: " + theClass.getName() + ". Reason: " + e,
128 e);
129 }
130 metaClasses.put(theClass, answer);
131 }
132 return answer;
133 }
134 }
135
136 public void removeMetaClass(Class theClass) {
137 metaClasses.remove(theClass);
138 }
139
140
141 /***
142 * Registers a new MetaClass in the registry to customize the type
143 *
144 * @param theClass
145 * @param theMetaClass
146 */
147 public void setMetaClass(Class theClass, MetaClass theMetaClass) {
148 metaClasses.put(theClass, theMetaClass);
149 }
150
151 public boolean useAccessible() {
152 return useAccessible;
153 }
154
155 /***
156 * A helper class to load meta class bytecode into the class loader
157 */
158 public Class loadClass(final String name, final byte[] bytecode) throws ClassNotFoundException {
159 return (Class) AccessController.doPrivileged(new PrivilegedAction() {
160 public Object run() {
161 return getGroovyLoader(loader).defineClass(name, bytecode, getClass().getProtectionDomain());
162 }
163 });
164 }
165
166 public Class loadClass(final ClassLoader loader, final String name, final byte[] bytecode) throws ClassNotFoundException {
167 return (Class) AccessController.doPrivileged(new PrivilegedAction() {
168 public Object run() {
169 return getGroovyLoader(loader).defineClass(name, bytecode, getClass().getProtectionDomain());
170 }
171 });
172 }
173
174 public Class loadClass(ClassLoader loader, String name) throws ClassNotFoundException {
175 return getGroovyLoader(loader).loadClass(name);
176 }
177
178 public Class loadClass(String name) throws ClassNotFoundException {
179 return getGroovyLoader(loader).loadClass(name);
180 }
181
182 private GroovyClassLoader getGroovyLoader(ClassLoader loader) {
183 if (loader instanceof GroovyClassLoader) {
184 return (GroovyClassLoader) loader;
185 }
186
187 synchronized (loaderMap) {
188 GroovyClassLoader groovyLoader = (GroovyClassLoader) loaderMap.get(loader);
189 if (groovyLoader == null) {
190 if (loader == null || loader == getClass().getClassLoader()) {
191 groovyLoader = this.loader;
192 }
193 else {
194
195
196
197
198
199
200 ClassLoader localLoader = getClass().getClassLoader();
201 ClassLoader parent = loader;
202 while (parent!=localLoader && parent!=null) parent=parent.getParent();
203 if (parent==null) {
204 groovyLoader = new GroovyClassLoader(localLoader);
205 } else {
206 groovyLoader = new GroovyClassLoader(loader);
207 }
208 }
209
210 loaderMap.put(loader, groovyLoader);
211 }
212
213 return groovyLoader;
214 }
215 }
216
217 /***
218 * Ensures that all the registered MetaClass instances are initalized
219 */
220 void checkInitialised() {
221
222
223 List list = new ArrayList(metaClasses.values());
224 for (Iterator iter = list.iterator(); iter.hasNext();) {
225 MetaClass metaClass = (MetaClass) iter.next();
226 metaClass.checkInitialised();
227 }
228 }
229
230 /***
231 * Used by MetaClass when registering new methods which avoids initializing the MetaClass instances on lookup
232 */
233 MetaClass lookup(Class theClass) {
234 MetaClass answer = (MetaClass) metaClasses.get(theClass);
235 if (answer == null) {
236 try {
237 answer = new MetaClass(this, theClass);
238 }
239 catch (IntrospectionException e) {
240 throw new GroovyRuntimeException("Could not introspect class: " + theClass.getName() + ". Reason: " + e,
241 e);
242 }
243 metaClasses.put(theClass, answer);
244 }
245 return answer;
246 }
247
248
249 public MetaMethod getDefinedMethod(Class theClass, String methodName, Class[] args, boolean isStatic) {
250 MetaClass metaclass = this.getMetaClass(theClass);
251 if (metaclass == null) {
252 return null;
253 }
254 else {
255 if (isStatic) {
256 return metaclass.retrieveStaticMethod(methodName, args);
257 }
258 else {
259 return metaclass.retrieveMethod(methodName, args);
260 }
261 }
262 }
263
264 public Constructor getDefinedConstructor(Class theClass, Class[] args) {
265 MetaClass metaclass = this.getMetaClass(theClass);
266 if (metaclass == null) {
267 return null;
268 }
269 else {
270 return metaclass.retrieveConstructor(args);
271 }
272 }
273
274 /***
275 * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance?
276 *
277 * @param includeExtension
278 * @return
279 */
280 public static MetaClassRegistry getIntance(int includeExtension) {
281 if (includeExtension != DONT_LOAD_DEFAULT) {
282 if (instanceInclude == null) {
283 instanceInclude = new MetaClassRegistry();
284 }
285 return instanceInclude;
286 }
287 else {
288 if (instanceExclude == null) {
289 instanceExclude = new MetaClassRegistry(DONT_LOAD_DEFAULT);
290 }
291 return instanceExclude;
292 }
293 }
294 }