1 /***************************************************************************************
2 * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
3 * http://aspectwerkz.codehaus.org *
4 * ---------------------------------------------------------------------------------- *
5 * The software in this package is published under the terms of the LGPL license *
6 * a copy of which has been included with this distribution in the license.txt file. *
7 **************************************************************************************/
8 package org.codehaus.aspectwerkz.definition;
9
10 import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
11
12 import java.net.URL;
13 import java.util.ArrayList;
14 import java.util.Enumeration;
15 import java.util.Iterator;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.WeakHashMap;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.io.File;
22
23 /***
24 * The SystemDefintionContainer maintains all the definition and is aware of the classloader hierarchy. <p/>A
25 * ThreadLocal structure is used during weaving to store current classloader defintion hierarchy. <p/>Due to
26 * getResources() API, we maintain a perClassLoader loaded resource list so that it contains only resource defined
27 * within the classloader and not its parent.
28 *
29 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
30 */
31 public class SystemDefinitionContainer {
32
33 /***
34 * Map of SystemDefinition[List] per ClassLoader.
35 * NOTE: null key is supported
36 */
37 public static final Map s_classLoaderSystemDefinitions = new WeakHashMap();
38 /***
39 * Map of SystemDefinition[List] per ClassLoader, with the hierarchy structure
40 * NOTE: null key is supported
41 */
42 public static Map s_classLoaderHierarchicalSystemDefinitions = new WeakHashMap();
43
44 /***
45 * Map of SystemDefinition location (as URL[List]) per ClassLoader
46 * NOTE: null key is supported
47 */
48 public static final Map s_classLoaderDefinitionLocations = new WeakHashMap();
49
50 /***
51 * Default location for default AspectWerkz definition file, JVM wide
52 */
53 public static final String URL_JVM_OPTION_SYSTEM = System.getProperty(
54 "aspectwerkz.definition.file",
55 "no -Daspectwerkz.definition.file"
56 );
57
58 /***
59 * The AOP deployment descriptor for any deployed unit Note: Tomcat 5 does not handles war/META-INF
60 */
61 public static final String AOP_META_INF_XML_FILE = "META-INF/aop.xml";
62
63 /***
64 * The AOP deployment descriptor for any deployed unit in a webapp TODO for EAR/EJB/JCA stuff
65 */
66 public static final String AOP_WEB_INF_XML_FILE = "../aop.xml";
67
68 public static final String WEB_WEB_INF_XML_FILE = "../web.xml";
69
70 /***
71 * An internal flag to disable registration of the -Daspectwerkz.definition.file definition in the System class
72 * loader. This is used only in offline mode, where these definitions are registered programmatically at the
73 * compilation class loader level.
74 */
75 private static boolean s_disableSystemWideDefinition = false;
76
77 private static final String VIRTUAL_SYSTEM_ID_PREFIX = "virtual_";
78
79 /***
80 * Register a new ClassLoader in the system and gather all its definition and parents definitions.
81 *
82 * @param loader the class loader to register
83 */
84 private static void registerClassLoader(final ClassLoader loader) {
85 synchronized (s_classLoaderSystemDefinitions) {
86 if (s_classLoaderSystemDefinitions.containsKey(loader)) {
87 return;
88 }
89
90
91
92
93 if (loader == null) {
94
95 Set defaults = new HashSet();
96 defaults.add(SystemDefinition.createVirtualDefinitionAt(loader));
97 s_classLoaderSystemDefinitions.put(loader, defaults);
98 s_classLoaderDefinitionLocations.put(loader, new ArrayList());
99
100 return;
101 }
102
103
104 registerClassLoader(loader.getParent());
105
106
107 try {
108 final Set definitions = new HashSet();
109 final List locationOfDefinitions = new ArrayList();
110
111
112 s_classLoaderSystemDefinitions.put(loader, definitions);
113 s_classLoaderDefinitionLocations.put(loader, locationOfDefinitions);
114
115
116 if ((loader == ClassLoader.getSystemClassLoader()) && !s_disableSystemWideDefinition) {
117
118 definitions.addAll(DefinitionLoader.getDefaultDefinition(loader));
119 locationOfDefinitions.add(new File(URL_JVM_OPTION_SYSTEM).toURL());
120 }
121 if (loader.getResource(WEB_WEB_INF_XML_FILE) != null) {
122 Enumeration webres = loader.getResources(AOP_WEB_INF_XML_FILE);
123 while (webres.hasMoreElements()) {
124 URL def = (URL) webres.nextElement();
125 if (isDefinedBy(loader, def)) {
126 ;
127 } else {
128 definitions.addAll(XmlParser.parseNoCache(loader, def));
129 locationOfDefinitions.add(def);
130 }
131 }
132 }
133 Enumeration res = loader.getResources(AOP_META_INF_XML_FILE);
134 while (res.hasMoreElements()) {
135 URL def = (URL) res.nextElement();
136 if (isDefinedBy(loader, def)) {
137 ;
138 } else {
139 definitions.addAll(XmlParser.parseNoCache(loader, def));
140 locationOfDefinitions.add(def);
141 }
142 }
143
144
145 definitions.add(SystemDefinition.createVirtualDefinitionAt(loader));
146
147 dump(loader);
148 } catch (Throwable t) {
149 t.printStackTrace();
150 }
151 }
152 }
153
154 /***
155 * Hotdeploy a list of SystemDefintions as defined at the level of the given ClassLoader
156 * <p/>
157 * Note: this is used for Offline mode.
158 *
159 * @param loader ClassLoader
160 * @param definitions SystemDefinitions list
161 */
162 public static void deployDefinitions(final ClassLoader loader, final Set definitions) {
163 synchronized (s_classLoaderSystemDefinitions) {
164
165
166 registerClassLoader(loader);
167
168
169
170
171 flushHierarchicalSystemDefinitionsBelow(loader);
172
173
174 Set defs = (Set) s_classLoaderSystemDefinitions.get(loader);
175 defs.addAll(definitions);
176 dump(loader);
177 }
178 }
179
180 private static void flushHierarchicalSystemDefinitionsBelow(ClassLoader loader) {
181
182
183 Map classLoaderHierarchicalSystemDefinitions = new WeakHashMap();
184 for (Iterator iterator = s_classLoaderHierarchicalSystemDefinitions.entrySet().iterator(); iterator.hasNext();) {
185 Map.Entry entry = (Map.Entry) iterator.next();
186 ClassLoader currentLoader = (ClassLoader) entry.getKey();
187 if (isChildOf(currentLoader, loader)) {
188 ;
189 } else {
190 classLoaderHierarchicalSystemDefinitions.put(currentLoader, entry.getValue());
191 }
192 }
193 s_classLoaderHierarchicalSystemDefinitions = classLoaderHierarchicalSystemDefinitions;
194
195 }
196
197 /***
198 * Lookup for a given SystemDefinition by uuid within a given ClassLoader.
199 * <p/>
200 * The lookup does go thru the ClassLoader hierarchy
201 *
202 * @param loader ClassLoader
203 * @param uuid system uuid
204 * @return SystemDefinition or null if no such defined definition
205 */
206 public static SystemDefinition getDefinitionFor(final ClassLoader loader, final String uuid) {
207 for (Iterator defs = getDefinitionsFor(loader).iterator(); defs.hasNext();) {
208 SystemDefinition def = (SystemDefinition) defs.next();
209 if (def.getUuid().equals(uuid)) {
210 return def;
211 }
212 }
213 return null;
214 }
215
216 /***
217 * Return the list of SystemDefinitions visible at the given ClassLoader level.
218 * <p/>
219 * It does handle the ClassLoader hierarchy.
220 *
221 * @param loader
222 * @return SystemDefinitions list
223 */
224 public static Set getDefinitionsFor(final ClassLoader loader) {
225 return getHierarchicalDefinitionsFor(loader);
226 }
227
228 /***
229 * Return the list of SystemDefinitions defined at the given ClassLoader level.
230 * <p/>
231 * It does NOT handle the ClassLoader hierarchy.
232 *
233 * @param loader
234 * @return SystemDefinitions list
235 */
236 public static Set getDefinitionsAt(final ClassLoader loader) {
237
238 registerClassLoader(loader);
239 return (Set) s_classLoaderSystemDefinitions.get(loader);
240 }
241
242 /***
243 // * Returns all the system definitions, including the virtual system.
244 // *
245 // * @param loader
246 // * @return
247 // */
248
249
250
251
252
253
254
255 /***
256 * Returns the virtual system for the class loader specified.
257 * <p/>
258 * There is ONE and ONLY ONE virtual system per classloader ie several per classloader
259 * hierachy. This definition hosts hotdeployed aspects. This method returns the
260 * one corresponding to the given classloader only.
261 *
262 * @param loader the class loader
263 * @return the virtual system
264 */
265 public static SystemDefinition getVirtualDefinitionAt(final ClassLoader loader) {
266
267 return getDefinitionFor(loader, getVirtualDefinitionUuid(loader));
268 }
269
270 /***
271 * Returns the uuid for the virtual system definition for the given classloader
272 *
273 * @param loader
274 * @return
275 */
276 public static String getVirtualDefinitionUuid(ClassLoader loader) {
277
278 int hash = loader == null ? 0 : loader.hashCode();
279 StringBuffer sb = new StringBuffer(VIRTUAL_SYSTEM_ID_PREFIX);
280 return sb.append(hash).toString();
281 }
282
283 /***
284 // * Returns the list of all ClassLoaders registered so far Note: when a child ClassLoader is registered, all its
285 // * parent hierarchy is registered
286 // *
287 // * @return ClassLoader Set
288 // */
289
290
291
292
293 /***
294 * Turns on the option to avoid -Daspectwerkz.definition.file handling.
295 */
296 public static void disableSystemWideDefinition() {
297 s_disableSystemWideDefinition = true;
298 }
299
300 /***
301 * Returns the gathered SystemDefinition visible from a classloader.
302 * <p/>
303 * This method is using a cache. Caution when
304 * modifying this method since when an aop.xml is loaded, the aspect classes gets loaded as well, which triggers
305 * this cache, while the system is in fact not yet initialized properly. </p>
306 *
307 * @param loader
308 * @return set with the system definitions
309 */
310 private static Set getHierarchicalDefinitionsFor(final ClassLoader loader) {
311 synchronized (s_classLoaderSystemDefinitions) {
312
313 if (s_classLoaderHierarchicalSystemDefinitions.containsKey(loader)) {
314 return (Set) s_classLoaderHierarchicalSystemDefinitions.get(loader);
315 } else {
316
317 registerClassLoader(loader);
318
319 Set defs = new HashSet();
320
321 s_classLoaderHierarchicalSystemDefinitions.put(loader, defs);
322 if (loader == null) {
323 ;
324 } else {
325 ClassLoader parent = loader.getParent();
326 defs.addAll(getHierarchicalDefinitionsFor(parent));
327 }
328 defs.addAll((Set) s_classLoaderSystemDefinitions.get(loader));
329
330 return defs;
331 }
332 }
333 }
334
335 /***
336 * Check if a given resource has already been registered to a classloader and its parent hierachy
337 *
338 * @param loader the classloader which might define the resource
339 * @param def the resource
340 * @return true if classloader or its parent defines the resource
341 * @TODO what if child shares parent path?
342 * @TODO What happens with smylinking and xml in jars etc ?
343 * @TODO Needs test
344 * @TODO No need for the s_ map
345 * @TODO KICK the def map and crawl up the CL parents and redo a getResources check instead
346 */
347 private static boolean isDefinedBy(final ClassLoader loader, final URL def) {
348 if (loader == null) {
349 return false;
350 }
351 ArrayList defLocation = (ArrayList) s_classLoaderDefinitionLocations.get(loader);
352 if (defLocation != null) {
353 for (Iterator it = defLocation.iterator(); it.hasNext();) {
354 URL definedDef = (URL) it.next();
355 if (definedDef.sameFile(def)) {
356 return true;
357 }
358 }
359 }
360 return isDefinedBy(loader.getParent(), def);
361 }
362
363 /***
364 * Pretty dump a classloader
365 *
366 * @param loader
367 */
368 private static void dump(final ClassLoader loader) {
369 if (!AspectWerkzPreProcessor.VERBOSE) {
370 return;
371 }
372
373 StringBuffer dump = new StringBuffer("******************************************************************");
374 dump.append("\n* ClassLoader = ");
375
376
377 if ((loader != null) && (loader.toString().length() < 120)) {
378 dump.append(loader.toString()).append("@").append(loader.hashCode());
379 } else if (loader != null) {
380 dump.append(loader.getClass().getName()).append("@").append(loader.hashCode());
381 } else {
382 dump.append("null");
383 }
384
385 Set defs = (Set) s_classLoaderSystemDefinitions.get(loader);
386 for (Iterator it = defs.iterator(); it.hasNext();) {
387 SystemDefinition def = (SystemDefinition) it.next();
388 dump.append("\n* SystemID = ").append(def.getUuid());
389 dump.append(", ").append(def.getAspectDefinitions().size()).append(" aspects.");
390 }
391 for (Iterator it = ((List) s_classLoaderDefinitionLocations.get(loader)).iterator(); it.hasNext();) {
392 dump.append("\n* ").append(it.next());
393 }
394 dump.append("\n******************************************************************");
395 System.out.println(dump.toString());
396 }
397
398 /***
399 * Returns true if the given classloader is a child of the given parent classloader
400 *
401 * @param loader
402 * @param parentLoader
403 * @return
404 */
405 private static boolean isChildOf(ClassLoader loader, ClassLoader parentLoader) {
406 if (loader == null) {
407 if (parentLoader == null) {
408 return true;
409 } else {
410 return false;
411 }
412 } else if (loader.equals(parentLoader)) {
413 return true;
414 } else {
415 return isChildOf(loader.getParent(), parentLoader);
416 }
417 }
418 }