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.21 $
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 try {
197 loader.loadClass(getClass().getName());
198
199
200 groovyLoader = new GroovyClassLoader(loader);
201 }
202 catch (ClassNotFoundException e) {
203
204
205
206 final ClassLoader localLoader = getClass().getClassLoader();
207 groovyLoader = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
208 public Object run() {
209 return new GroovyClassLoader(localLoader);
210 }
211 });
212 }
213 }
214 loaderMap.put(loader, groovyLoader);
215 }
216
217 return groovyLoader;
218 }
219 }
220
221 /***
222 * Ensures that all the registered MetaClass instances are initalized
223 */
224 void checkInitialised() {
225
226
227 List list = new ArrayList(metaClasses.values());
228 for (Iterator iter = list.iterator(); iter.hasNext();) {
229 MetaClass metaClass = (MetaClass) iter.next();
230 metaClass.checkInitialised();
231 }
232 }
233
234 /***
235 * Used by MetaClass when registering new methods which avoids initializing the MetaClass instances on lookup
236 */
237 MetaClass lookup(Class theClass) {
238 MetaClass answer = (MetaClass) metaClasses.get(theClass);
239 if (answer == null) {
240 try {
241 answer = new MetaClass(this, theClass);
242 }
243 catch (IntrospectionException e) {
244 throw new GroovyRuntimeException("Could not introspect class: " + theClass.getName() + ". Reason: " + e,
245 e);
246 }
247 metaClasses.put(theClass, answer);
248 }
249 return answer;
250 }
251
252
253 public MetaMethod getDefinedMethod(Class theClass, String methodName, Class[] args, boolean isStatic) {
254 MetaClass metaclass = this.getMetaClass(theClass);
255 if (metaclass == null) {
256 return null;
257 }
258 else {
259 if (isStatic) {
260 return metaclass.retrieveStaticMethod(methodName, args);
261 }
262 else {
263 return metaclass.retrieveMethod(methodName, args);
264 }
265 }
266 }
267
268 public Constructor getDefinedConstructor(Class theClass, Class[] args) {
269 MetaClass metaclass = this.getMetaClass(theClass);
270 if (metaclass == null) {
271 return null;
272 }
273 else {
274 return metaclass.retrieveConstructor(args);
275 }
276 }
277
278 /***
279 * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance?
280 *
281 * @param includeExtension
282 * @return
283 */
284 public static MetaClassRegistry getIntance(int includeExtension) {
285 if (includeExtension != DONT_LOAD_DEFAULT) {
286 if (instanceInclude == null) {
287 instanceInclude = new MetaClassRegistry();
288 }
289 return instanceInclude;
290 }
291 else {
292 if (instanceExclude == null) {
293 instanceExclude = new MetaClassRegistry(DONT_LOAD_DEFAULT);
294 }
295 return instanceExclude;
296 }
297 }
298 }