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 }