001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.definition;
008:
009: import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
010:
011: import java.net.URL;
012: import java.util.ArrayList;
013: import java.util.Enumeration;
014: import java.util.Iterator;
015: import java.util.Map;
016: import java.util.Set;
017: import java.util.WeakHashMap;
018: import java.util.HashSet;
019: import java.util.List;
020: import java.io.File;
021:
022: /**
023: * The SystemDefintionContainer maintains all the definition and is aware of the classloader hierarchy. <p/>A
024: * ThreadLocal structure is used during weaving to store current classloader defintion hierarchy. <p/>Due to
025: * getResources() API, we maintain a perClassLoader loaded resource list so that it contains only resource defined
026: * within the classloader and not its parent.
027: *
028: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
029: */
030: public class SystemDefinitionContainer {
031:
032: /**
033: * Map of SystemDefinition[List] per ClassLoader.
034: * NOTE: null key is supported
035: */
036: public static final Map s_classLoaderSystemDefinitions = new WeakHashMap();
037: /**
038: * Map of SystemDefinition[List] per ClassLoader, with the hierarchy structure
039: * NOTE: null key is supported
040: */
041: public static Map s_classLoaderHierarchicalSystemDefinitions = new WeakHashMap();
042:
043: /**
044: * Map of SystemDefinition location (as URL[List]) per ClassLoader
045: * NOTE: null key is supported
046: */
047: public static final Map s_classLoaderDefinitionLocations = new WeakHashMap();
048:
049: /**
050: * Default location for default AspectWerkz definition file, JVM wide
051: */
052: public static final String URL_JVM_OPTION_SYSTEM = System
053: .getProperty("aspectwerkz.definition.file",
054: "no -Daspectwerkz.definition.file");
055:
056: /**
057: * The AOP deployment descriptor for any deployed unit Note: Tomcat 5 does not handles war/META-INF
058: */
059: public static final String AOP_META_INF_XML_FILE = "META-INF/aop.xml";
060:
061: /**
062: * The AOP deployment descriptor for any deployed unit in a webapp TODO for EAR/EJB/JCA stuff
063: */
064: public static final String AOP_WEB_INF_XML_FILE = "../aop.xml";
065:
066: public static final String WEB_WEB_INF_XML_FILE = "../web.xml";
067:
068: /**
069: * An internal flag to disable registration of the -Daspectwerkz.definition.file definition in the System class
070: * loader. This is used only in offline mode, where these definitions are registered programmatically at the
071: * compilation class loader level.
072: */
073: private static boolean s_disableSystemWideDefinition = false;
074:
075: private static final String VIRTUAL_SYSTEM_ID_PREFIX = "virtual_";
076:
077: /**
078: * Register a new ClassLoader in the system and gather all its definition and parents definitions.
079: *
080: * @param loader the class loader to register
081: */
082: private static void registerClassLoader(final ClassLoader loader) {
083: synchronized (s_classLoaderSystemDefinitions) {
084: if (s_classLoaderSystemDefinitions.containsKey(loader)) {
085: return;
086: }
087:
088: // else - register
089:
090: // skip boot classloader and ext classloader
091: if (loader == null) {
092: // by defaults, there is always the virtual definition, that has lowest precedence
093: Set defaults = new HashSet();
094: defaults.add(SystemDefinition
095: .createVirtualDefinitionAt(loader));
096: s_classLoaderSystemDefinitions.put(loader, defaults);
097: s_classLoaderDefinitionLocations.put(loader,
098: new ArrayList());
099:
100: return;
101: }
102:
103: // register parents first
104: registerClassLoader(loader.getParent());
105:
106: // then register -D.. if system classloader and then all META-INF/aop.xml
107: try {
108: final Set definitions = new HashSet();
109: final List locationOfDefinitions = new ArrayList();
110:
111: // early registration to avoid recursion
112: s_classLoaderSystemDefinitions.put(loader, definitions);
113: s_classLoaderDefinitionLocations.put(loader,
114: locationOfDefinitions);
115:
116: // is this system classloader ?
117: if ((loader == ClassLoader.getSystemClassLoader())
118: && !s_disableSystemWideDefinition) {
119: // -D..file=... sysdef
120: definitions.addAll(DefinitionLoader
121: .getDefaultDefinition(loader));
122: locationOfDefinitions.add(new File(
123: URL_JVM_OPTION_SYSTEM).toURL());
124: }
125: if (loader.getResource(WEB_WEB_INF_XML_FILE) != null) {
126: Enumeration webres = loader
127: .getResources(AOP_WEB_INF_XML_FILE);
128: while (webres.hasMoreElements()) {
129: URL def = (URL) webres.nextElement();
130: if (isDefinedBy(loader, def)) {
131: ;
132: } else {
133: definitions.addAll(XmlParser.parseNoCache(
134: loader, def));
135: locationOfDefinitions.add(def);
136: }
137: }
138: }
139: Enumeration res = loader
140: .getResources(AOP_META_INF_XML_FILE);
141: while (res.hasMoreElements()) {
142: URL def = (URL) res.nextElement();
143: if (isDefinedBy(loader, def)) {
144: ;
145: } else {
146: definitions.addAll(XmlParser.parseNoCache(
147: loader, def));
148: locationOfDefinitions.add(def);
149: }
150: }
151:
152: // there is always the virtual definition, that has lowest precedence
153: definitions.add(SystemDefinition
154: .createVirtualDefinitionAt(loader));
155:
156: dump(loader);
157: } catch (Throwable t) {
158: t.printStackTrace();
159: }
160: }
161: }
162:
163: /**
164: * Hotdeploy a list of SystemDefintions as defined at the level of the given ClassLoader
165: * <p/>
166: * Note: this is used for Offline mode.
167: *
168: * @param loader ClassLoader
169: * @param definitions SystemDefinitions list
170: */
171: public static void deployDefinitions(final ClassLoader loader,
172: final Set definitions) {
173: synchronized (s_classLoaderSystemDefinitions) {
174:
175: // make sure the classloader is known
176: registerClassLoader(loader);
177:
178: //unchanged: s_classLoaderDefinitionLocations
179:
180: // propagate change by flushing hierachical cache in all childs
181: flushHierarchicalSystemDefinitionsBelow(loader);
182:
183: // update
184: Set defs = (Set) s_classLoaderSystemDefinitions.get(loader);
185: defs.addAll(definitions);
186: dump(loader);
187: }
188: }
189:
190: private static void flushHierarchicalSystemDefinitionsBelow(
191: ClassLoader loader) {
192: // lock already owned
193: //synchronized (s_classLoaderSystemDefinitions) {
194: Map classLoaderHierarchicalSystemDefinitions = new WeakHashMap();
195: for (Iterator iterator = s_classLoaderHierarchicalSystemDefinitions
196: .entrySet().iterator(); iterator.hasNext();) {
197: Map.Entry entry = (Map.Entry) iterator.next();
198: ClassLoader currentLoader = (ClassLoader) entry.getKey();
199: if (isChildOf(currentLoader, loader)) {
200: ;// flushed
201: } else {
202: classLoaderHierarchicalSystemDefinitions.put(
203: currentLoader, entry.getValue());
204: }
205: }
206: s_classLoaderHierarchicalSystemDefinitions = classLoaderHierarchicalSystemDefinitions;
207: //}
208: }
209:
210: /**
211: * Lookup for a given SystemDefinition by uuid within a given ClassLoader.
212: * <p/>
213: * The lookup does go thru the ClassLoader hierarchy
214: *
215: * @param loader ClassLoader
216: * @param uuid system uuid
217: * @return SystemDefinition or null if no such defined definition
218: */
219: public static SystemDefinition getDefinitionFor(
220: final ClassLoader loader, final String uuid) {
221: for (Iterator defs = getDefinitionsFor(loader).iterator(); defs
222: .hasNext();) {
223: SystemDefinition def = (SystemDefinition) defs.next();
224: if (def.getUuid().equals(uuid)) {
225: return def;
226: }
227: }
228: return null;
229: }
230:
231: /**
232: * Return the list of SystemDefinitions visible at the given ClassLoader level.
233: * <p/>
234: * It does handle the ClassLoader hierarchy.
235: *
236: * @param loader
237: * @return SystemDefinitions list
238: */
239: public static Set getDefinitionsFor(final ClassLoader loader) {
240: return getHierarchicalDefinitionsFor(loader);
241: }
242:
243: /**
244: * Return the list of SystemDefinitions defined at the given ClassLoader level.
245: * <p/>
246: * It does NOT handle the ClassLoader hierarchy.
247: *
248: * @param loader
249: * @return SystemDefinitions list
250: */
251: public static Set getDefinitionsAt(final ClassLoader loader) {
252: // make sure the classloader is registered
253: registerClassLoader(loader);
254: return (Set) s_classLoaderSystemDefinitions.get(loader);
255: }
256:
257: // /**
258: // * Returns all the system definitions, including the virtual system.
259: // *
260: // * @param loader
261: // * @return
262: // */
263: // public static Set getRegularAndVirtualDefinitionsFor(final ClassLoader loader) {
264: // final Set allDefs = new HashSet();
265: // allDefs.addAll(getDefinitionsFor(loader));
266: // allDefs.add(getVirtualDefinitionFor(loader));
267: // return allDefs;
268: // }
269:
270: /**
271: * Returns the virtual system for the class loader specified.
272: * <p/>
273: * There is ONE and ONLY ONE virtual system per classloader ie several per classloader
274: * hierachy. This definition hosts hotdeployed aspects. This method returns the
275: * one corresponding to the given classloader only.
276: *
277: * @param loader the class loader
278: * @return the virtual system
279: */
280: public static SystemDefinition getVirtualDefinitionAt(
281: final ClassLoader loader) {
282: // since virtual uuid is mapped to a classloader, a direct lookup on uuid is enough
283: return getDefinitionFor(loader,
284: getVirtualDefinitionUuid(loader));
285: }
286:
287: /**
288: * Returns the uuid for the virtual system definition for the given classloader
289: *
290: * @param loader
291: * @return
292: */
293: public static String getVirtualDefinitionUuid(ClassLoader loader) {
294: // handle bootclassloader with care
295: int hash = loader == null ? 0 : loader.hashCode();
296: StringBuffer sb = new StringBuffer(VIRTUAL_SYSTEM_ID_PREFIX);
297: return sb.append(hash).toString();
298: }
299:
300: // /**
301: // * Returns the list of all ClassLoaders registered so far Note: when a child ClassLoader is registered, all its
302: // * parent hierarchy is registered
303: // *
304: // * @return ClassLoader Set
305: // */
306: // public static Set getAllRegisteredClassLoaders() {
307: // return s_classLoaderSystemDefinitions.keySet();
308: // }
309:
310: /**
311: * Turns on the option to avoid -Daspectwerkz.definition.file handling.
312: */
313: public static void disableSystemWideDefinition() {
314: s_disableSystemWideDefinition = true;
315: }
316:
317: /**
318: * Returns the gathered SystemDefinition visible from a classloader.
319: * <p/>
320: * This method is using a cache. Caution when
321: * modifying this method since when an aop.xml is loaded, the aspect classes gets loaded as well, which triggers
322: * this cache, while the system is in fact not yet initialized properly. </p>
323: *
324: * @param loader
325: * @return set with the system definitions
326: */
327: private static Set getHierarchicalDefinitionsFor(
328: final ClassLoader loader) {
329: synchronized (s_classLoaderSystemDefinitions) {
330: // check cache
331: if (s_classLoaderHierarchicalSystemDefinitions
332: .containsKey(loader)) {
333: return (Set) s_classLoaderHierarchicalSystemDefinitions
334: .get(loader);
335: } else {
336: // make sure the classloader is known
337: registerClassLoader(loader);
338:
339: Set defs = new HashSet();
340: // put it in the cache now since this method is recursive
341: s_classLoaderHierarchicalSystemDefinitions.put(loader,
342: defs);
343: if (loader == null) {
344: ; // go on to put in the cache at the end
345: } else {
346: ClassLoader parent = loader.getParent();
347: defs.addAll(getHierarchicalDefinitionsFor(parent));
348: }
349: defs.addAll((Set) s_classLoaderSystemDefinitions
350: .get(loader));
351:
352: return defs;
353: }
354: }
355: }
356:
357: /**
358: * Check if a given resource has already been registered to a classloader and its parent hierachy
359: *
360: * @param loader the classloader which might define the resource
361: * @param def the resource
362: * @return true if classloader or its parent defines the resource
363: * @TODO what if child shares parent path?
364: * @TODO What happens with smylinking and xml in jars etc ?
365: * @TODO Needs test
366: * @TODO No need for the s_ map
367: * @TODO KICK the def map and crawl up the CL parents and redo a getResources check instead
368: */
369: private static boolean isDefinedBy(final ClassLoader loader,
370: final URL def) {
371: if (loader == null) {
372: return false;
373: }
374: ArrayList defLocation = (ArrayList) s_classLoaderDefinitionLocations
375: .get(loader);
376: if (defLocation != null) {
377: for (Iterator it = defLocation.iterator(); it.hasNext();) {
378: URL definedDef = (URL) it.next();
379: if (definedDef.sameFile(def)) {
380: return true;
381: }
382: }
383: }
384: return isDefinedBy(loader.getParent(), def);
385: }
386:
387: /**
388: * Pretty dump a classloader
389: *
390: * @param loader
391: */
392: private static void dump(final ClassLoader loader) {
393: if (!AspectWerkzPreProcessor.VERBOSE) {
394: return;
395: }
396:
397: StringBuffer dump = new StringBuffer(
398: "******************************************************************");
399: dump.append("\n* ClassLoader = ");
400:
401: //Note: Tomcat classLoader.toString is too verbose so we allow 120 chars.
402: if ((loader != null) && (loader.toString().length() < 120)) {
403: dump.append(loader.toString()).append("@").append(
404: loader.hashCode());
405: } else if (loader != null) {
406: dump.append(loader.getClass().getName()).append("@")
407: .append(loader.hashCode());
408: } else {
409: dump.append("null");
410: }
411:
412: Set defs = (Set) s_classLoaderSystemDefinitions.get(loader);
413: for (Iterator it = defs.iterator(); it.hasNext();) {
414: SystemDefinition def = (SystemDefinition) it.next();
415: dump.append("\n* SystemID = ").append(def.getUuid());
416: dump.append(", ").append(def.getAspectDefinitions().size())
417: .append(" aspects.");
418: }
419: for (Iterator it = ((List) s_classLoaderDefinitionLocations
420: .get(loader)).iterator(); it.hasNext();) {
421: dump.append("\n* ").append(it.next());
422: }
423: dump
424: .append("\n******************************************************************");
425: System.out.println(dump.toString());
426: }
427:
428: /**
429: * Returns true if the given classloader is a child of the given parent classloader
430: *
431: * @param loader
432: * @param parentLoader
433: * @return
434: */
435: private static boolean isChildOf(ClassLoader loader,
436: ClassLoader parentLoader) {
437: if (loader == null) {
438: if (parentLoader == null) {
439: return true;
440: } else {
441: return false;
442: }
443: } else if (loader.equals(parentLoader)) {
444: return true;
445: } else {
446: return isChildOf(loader.getParent(), parentLoader);
447: }
448: }
449: }
|