View Javadoc

    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              // else - register
    91
    92              // skip boot classloader and ext classloader
    93              if (loader == null) {
    94                  // by defaults, there is always the virtual definition, that has lowest precedence
    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             // 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, locationOfDefinitions);
    114
    115                 // is this system classloader ?
    116                 if ((loader == ClassLoader.getSystemClassLoader()) && !s_disableSystemWideDefinition) {
    117                     // -D..file=... sysdef
    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                 // there is always the virtual definition, that has lowest precedence
    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             // make sure the classloader is known
    166             registerClassLoader(loader);
    167
    168             //unchanged: s_classLoaderDefinitionLocations
    169
    170             // propagate change by flushing hierachical cache in all childs
    171             flushHierarchicalSystemDefinitionsBelow(loader);
    172
    173             // update
    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         // lock already owned
    182         //synchronized (s_classLoaderSystemDefinitions) {
    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                 ;// flushed
    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         // make sure the classloader is registered
    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 //    public static Set getRegularAndVirtualDefinitionsFor(final ClassLoader loader) {
    249 //        final Set allDefs = new HashSet();
    250 //        allDefs.addAll(getDefinitionsFor(loader));
    251 //        allDefs.add(getVirtualDefinitionFor(loader));
    252 //        return allDefs;
    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         // since virtual uuid is mapped to a classloader, a direct lookup on uuid is enough
    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         // handle bootclassloader with care
    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 //    public static Set getAllRegisteredClassLoaders() {
    290 //        return s_classLoaderSystemDefinitions.keySet();
    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             // check cache
    313             if (s_classLoaderHierarchicalSystemDefinitions.containsKey(loader)) {
    314                 return (Set) s_classLoaderHierarchicalSystemDefinitions.get(loader);
    315             } else {
    316                 // make sure the classloader is known
    317                 registerClassLoader(loader);
    318
    319                 Set defs = new HashSet();
    320                 // put it in the cache now since this method is recursive
    321                 s_classLoaderHierarchicalSystemDefinitions.put(loader, defs);
    322                 if (loader == null) {
    323                     ; // go on to put in the cache at the end
    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         //Note: Tomcat classLoader.toString is too verbose so we allow 120 chars.
    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 }