/**************************************************************************************
* Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.definition;
import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.HashSet;
import java.util.List;
import java.io.File;
/**
* The SystemDefintionContainer maintains all the definition and is aware of the classloader hierarchy. <p/>A
* ThreadLocal structure is used during weaving to store current classloader defintion hierarchy. <p/>Due to
* getResources() API, we maintain a perClassLoader loaded resource list so that it contains only resource defined
* within the classloader and not its parent.
*
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
*/
public class SystemDefinitionContainer {
/**
* Map of SystemDefinition[List] per ClassLoader.
* NOTE: null key is supported
*/
public static final Map s_classLoaderSystemDefinitions = new WeakHashMap();
/**
* Map of SystemDefinition[List] per ClassLoader, with the hierarchy structure
* NOTE: null key is supported
*/
public static Map s_classLoaderHierarchicalSystemDefinitions = new WeakHashMap();
/**
* Map of SystemDefinition location (as URL[List]) per ClassLoader
* NOTE: null key is supported
*/
public static final Map s_classLoaderDefinitionLocations = new WeakHashMap();
/**
* Default location for default AspectWerkz definition file, JVM wide
*/
public static final String URL_JVM_OPTION_SYSTEM = System.getProperty(
"aspectwerkz.definition.file",
"no -Daspectwerkz.definition.file"
);
/**
* The AOP deployment descriptor for any deployed unit Note: Tomcat 5 does not handles war/META-INF
*/
public static final String AOP_META_INF_XML_FILE = "META-INF/aop.xml";
/**
* The AOP deployment descriptor for any deployed unit in a webapp TODO for EAR/EJB/JCA stuff
*/
public static final String AOP_WEB_INF_XML_FILE = "../aop.xml";
public static final String WEB_WEB_INF_XML_FILE = "../web.xml";
/**
* An internal flag to disable registration of the -Daspectwerkz.definition.file definition in the System class
* loader. This is used only in offline mode, where these definitions are registered programmatically at the
* compilation class loader level.
*/
private static boolean s_disableSystemWideDefinition = false;
private static final String VIRTUAL_SYSTEM_ID_PREFIX = "virtual_";
/**
* Register a new ClassLoader in the system and gather all its definition and parents definitions.
*
* @param loader the class loader to register
*/
private static void registerClassLoader(final ClassLoader loader) {
synchronized (s_classLoaderSystemDefinitions) {
if (s_classLoaderSystemDefinitions.containsKey(loader)) {
return;
}
// else - register
// skip boot classloader and ext classloader
if (loader == null) {
// by defaults, there is always the virtual definition, that has lowest precedence
Set defaults = new HashSet();
//TODO AVOPT
defaults.add(SystemDefinition.createVirtualDefinitionAt(loader));
s_classLoaderSystemDefinitions.put(loader, defaults);
s_classLoaderDefinitionLocations.put(loader, new ArrayList());
return;
}
// register parents first
registerClassLoader(loader.getParent());
// then register -D.. if system classloader and then all META-INF/aop.xml
try {
final Set definitions = new HashSet();
final List locationOfDefinitions = new ArrayList();
// early registration to avoid recursion
s_classLoaderSystemDefinitions.put(loader, definitions);
s_classLoaderDefinitionLocations.put(loader, locationOfDefinitions);
// is this system classloader ?
if ((loader == ClassLoader.getSystemClassLoader()) && !s_disableSystemWideDefinition) {
// -D..file=... sysdef
definitions.addAll(DefinitionLoader.getDefaultDefinition(loader));
locationOfDefinitions.add(new File(URL_JVM_OPTION_SYSTEM).toURL());
}
if (loader.getResource(WEB_WEB_INF_XML_FILE) != null) {
Enumeration webres = loader.getResources(AOP_WEB_INF_XML_FILE);
while (webres.hasMoreElements()) {
URL def = (URL) webres.nextElement();
if (isDefinedBy(loader, def)) {
;
} else {
definitions.addAll(XmlParser.parseNoCache(loader, def));
locationOfDefinitions.add(def);
}
}
}
Enumeration res = loader.getResources(AOP_META_INF_XML_FILE);
while (res != null && res.hasMoreElements()) {
URL def = (URL) res.nextElement();
if (isDefinedBy(loader, def)) {
;
} else {
definitions.addAll(XmlParser.parseNoCache(loader, def));
locationOfDefinitions.add(def);
}
}
// there is always the virtual definition, that has lowest precedence
//TODO AVOPT
definitions.add(SystemDefinition.createVirtualDefinitionAt(loader));
dump(loader);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
/**
* Hotdeploy a list of SystemDefintions as defined at the level of the given ClassLoader
* <p/>
* Note: this is used for Offline mode.
*
* @param loader ClassLoader
* @param definitions SystemDefinitions list
*/
public static void deployDefinitions(final ClassLoader loader, final Set definitions) {
synchronized (s_classLoaderSystemDefinitions) {
// make sure the classloader is known
registerClassLoader(loader);
//unchanged: s_classLoaderDefinitionLocations
// propagate change by flushing hierachical cache in all childs
flushHierarchicalSystemDefinitionsBelow(loader);
// update
Set defs = (Set) s_classLoaderSystemDefinitions.get(loader);
defs.addAll(definitions);
dump(loader);
}
}
private static void flushHierarchicalSystemDefinitionsBelow(ClassLoader loader) {
// lock already owned
//synchronized (s_classLoaderSystemDefinitions) {
Map classLoaderHierarchicalSystemDefinitions = new WeakHashMap();
for (Iterator iterator = s_classLoaderHierarchicalSystemDefinitions.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
ClassLoader currentLoader = (ClassLoader) entry.getKey();
if (isChildOf(currentLoader, loader)) {
;// flushed
} else {
classLoaderHierarchicalSystemDefinitions.put(currentLoader, entry.getValue());
}
}
s_classLoaderHierarchicalSystemDefinitions = classLoaderHierarchicalSystemDefinitions;
//}
}
/**
* Lookup for a given SystemDefinition by uuid within a given ClassLoader.
* <p/>
* The lookup does go thru the ClassLoader hierarchy
*
* @param loader ClassLoader
* @param uuid system uuid
* @return SystemDefinition or null if no such defined definition
*/
public static SystemDefinition getDefinitionFor(final ClassLoader loader, final String uuid) {
for (Iterator defs = getDefinitionsFor(loader).iterator(); defs.hasNext();) {
SystemDefinition def = (SystemDefinition) defs.next();
if (def.getUuid().equals(uuid)) {
return def;
}
}
return null;
}
/**
* Return the list of SystemDefinitions visible at the given ClassLoader level.
* <p/>
* It does handle the ClassLoader hierarchy.
*
* @param loader
* @return SystemDefinitions list
*/
public static Set getDefinitionsFor(final ClassLoader loader) {
return getHierarchicalDefinitionsFor(loader);
}
/**
* Return the list of SystemDefinitions defined at the given ClassLoader level.
* <p/>
* It does NOT handle the ClassLoader hierarchy.
*
* @param loader
* @return SystemDefinitions list
*/
public static Set getDefinitionsAt(final ClassLoader loader) {
// make sure the classloader is registered
registerClassLoader(loader);
return (Set) s_classLoaderSystemDefinitions.get(loader);
}
// /**
// * Returns all the system definitions, including the virtual system.
// *
// * @param loader
// * @return
// */
// public static Set getRegularAndVirtualDefinitionsFor(final ClassLoader loader) {
// final Set allDefs = new HashSet();
// allDefs.addAll(getDefinitionsFor(loader));
// allDefs.add(getVirtualDefinitionFor(loader));
// return allDefs;
// }
/**
* Returns the virtual system for the class loader specified.
* <p/>
* There is ONE and ONLY ONE virtual system per classloader ie several per classloader
* hierachy. This definition hosts hotdeployed aspects. This method returns the
* one corresponding to the given classloader only.
*
* @param loader the class loader
* @return the virtual system
*/
public static SystemDefinition getVirtualDefinitionAt(final ClassLoader loader) {
// since virtual uuid is mapped to a classloader, a direct lookup on uuid is enough
return getDefinitionFor(loader, getVirtualDefinitionUuid(loader));
}
/**
* Returns the uuid for the virtual system definition for the given classloader
*
* @param loader
* @return
*/
public static String getVirtualDefinitionUuid(ClassLoader loader) {
// handle bootclassloader with care
int hash = loader == null ? 0 : loader.hashCode();
StringBuffer sb = new StringBuffer(VIRTUAL_SYSTEM_ID_PREFIX);
return sb.append(hash).toString();
}
// /**
// * Returns the list of all ClassLoaders registered so far Note: when a child ClassLoader is registered, all its
// * parent hierarchy is registered
// *
// * @return ClassLoader Set
// */
// public static Set getAllRegisteredClassLoaders() {
// return s_classLoaderSystemDefinitions.keySet();
// }
/**
* Turns on the option to avoid -Daspectwerkz.definition.file handling.
*/
public static void disableSystemWideDefinition() {
s_disableSystemWideDefinition = true;
}
/**
* Returns the gathered SystemDefinition visible from a classloader.
* <p/>
* This method is using a cache. Caution when
* modifying this method since when an aop.xml is loaded, the aspect classes gets loaded as well, which triggers
* this cache, while the system is in fact not yet initialized properly. </p>
*
* @param loader
* @return set with the system definitions
*/
private static Set getHierarchicalDefinitionsFor(final ClassLoader loader) {
synchronized (s_classLoaderSystemDefinitions) {
// check cache
if (s_classLoaderHierarchicalSystemDefinitions.containsKey(loader)) {
return (Set) s_classLoaderHierarchicalSystemDefinitions.get(loader);
} else {
// make sure the classloader is known
registerClassLoader(loader);
Set defs = new HashSet();
// put it in the cache now since this method is recursive
s_classLoaderHierarchicalSystemDefinitions.put(loader, defs);
if (loader == null) {
; // go on to put in the cache at the end
} else {
ClassLoader parent = loader.getParent();
defs.addAll(getHierarchicalDefinitionsFor(parent));
}
defs.addAll((Set) s_classLoaderSystemDefinitions.get(loader));
return defs;
}
}
}
/**
* Check if a given resource has already been registered to a classloader and its parent hierachy
*
* @param loader the classloader which might define the resource
* @param def the resource
* @return true if classloader or its parent defines the resource
* @TODO what if child shares parent path?
* @TODO What happens with smylinking and xml in jars etc ?
* @TODO Needs test
* @TODO No need for the s_ map
* @TODO KICK the def map and crawl up the CL parents and redo a getResources check instead
*/
private static boolean isDefinedBy(final ClassLoader loader, final URL def) {
if (loader == null) {
return false;
}
ArrayList defLocation = (ArrayList) s_classLoaderDefinitionLocations.get(loader);
if (defLocation != null) {
for (Iterator it = defLocation.iterator(); it.hasNext();) {
URL definedDef = (URL) it.next();
if (definedDef.sameFile(def)) {
return true;
}
}
}
return isDefinedBy(loader.getParent(), def);
}
/**
* Pretty dump a classloader
*
* @param loader
*/
private static void dump(final ClassLoader loader) {
if (!AspectWerkzPreProcessor.VERBOSE) {
return;
}
StringBuffer dump = new StringBuffer("******************************************************************");
dump.append("\n* ClassLoader = ");
//Note: Tomcat classLoader.toString is too verbose so we allow 120 chars.
if ((loader != null) && (loader.toString().length() < 120)) {
dump.append(loader.toString()).append("@").append(loader.hashCode());
} else if (loader != null) {
dump.append(loader.getClass().getName()).append("@").append(loader.hashCode());
} else {
dump.append("null");
}
Set defs = (Set) s_classLoaderSystemDefinitions.get(loader);
for (Iterator it = defs.iterator(); it.hasNext();) {
SystemDefinition def = (SystemDefinition) it.next();
dump.append("\n* SystemID = ").append(def.getUuid());
dump.append(", ").append(def.getAspectDefinitions().size()).append(" aspects.");
}
for (Iterator it = ((List) s_classLoaderDefinitionLocations.get(loader)).iterator(); it.hasNext();) {
dump.append("\n* ").append(it.next());
}
dump.append("\n******************************************************************");
System.out.println(dump.toString());
}
/**
* Returns true if the given classloader is a child of the given parent classloader
*
* @param loader
* @param parentLoader
* @return
*/
public static boolean isChildOf(ClassLoader loader, ClassLoader parentLoader) {
if (loader == null) {
if (parentLoader == null) {
return true;
} else {
return false;
}
} else if (loader.equals(parentLoader)) {
return true;
} else {
return isChildOf(loader.getParent(), parentLoader);
}
}
}