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