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.List;
17  import java.util.Map;
18  import java.util.Set;
19  import java.util.WeakHashMap;
20  import java.io.File;
21  
22  /***
23   * The SystemDefintionContainer maintains all the definition and is aware of the classloader hierarchy. <p/>A
24   * ThreadLocal structure is used during weaving to store current classloader defintion hierarchy. <p/>Due to
25   * getResources() API, we maintain a perClassLoader loaded resource list so that it contains only resource defined
26   * within the classloader and not its parent.
27   *
28   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
29   */
30  public class SystemDefinitionContainer {
31      /***
32       * Map of SystemDefinition[List] per ClassLoader
33       */
34      public static final Map s_classLoaderSystemDefinitions = new WeakHashMap(); //note: null key is
35  
36      // supported
37  
38      /***
39       * Map of SystemDefinition[List] per ClassLoader, with the hierarchy structure
40       */
41      public static final Map s_classLoaderHierarchicalSystemDefinitions = new WeakHashMap(); //note: null
42  
43      // key is
44      // supported
45  
46      /***
47       * Map of SystemDefinition location (as URL[List]) per ClassLoader
48       */
49      public static final Map s_classLoaderDefinitionLocations = new WeakHashMap(); //note: null key is
50  
51      // supported
52  
53      /***
54       * Default location for default AspectWerkz definition file, JVM wide
55       */
56      public static final String URL_JVM_OPTION_SYSTEM = System.getProperty(
57              "aspectwerkz.definition.file",
58              "no -Daspectwerkz.definition.file"
59      );
60  
61      /***
62       * The AOP deployment descriptor for any deployed unit Note: Tomcat 5 does not handles war/META-INF
63       */
64      public static final String AOP_META_INF_XML_FILE = "META-INF/aop.xml";
65  
66      /***
67       * The AOP deployment descriptor for any deployed unit in a webapp TODO for EAR/EJB/JCA stuff
68       */
69      public static final String AOP_WEB_INF_XML_FILE = "../aop.xml";
70  
71      public static final String WEB_WEB_INF_XML_FILE = "../web.xml";
72  
73      /***
74       * An internal flag to disable registration of the -Daspectwerkz.definition.file definition in the System class
75       * loader. This is used only in offline mode, where these definitions are registered programmatically at the
76       * compilation class loader level.
77       */
78      private static boolean s_disableSystemWideDefinition = false;
79  
80      /***
81       * Register a new ClassLoader in the system and gather all its definition and parents definitions.
82       *
83       * @param loader the class loader to register
84       */
85      public static void registerClassLoader(final ClassLoader loader) {
86          if (s_classLoaderSystemDefinitions.containsKey(loader)) {
87              return;
88          }
89  
90          // skip boot classloader and ext classloader
91          if (loader == null) {
92              return;
93          }
94          if (loader != null) {
95              // register parents first
96              registerClassLoader(loader.getParent());
97  
98              // then register -D.. if system classloader and then all META-INF/aop.xml
99              try {
100                 List defs = new ArrayList();
101                 List defsLocation = new ArrayList();
102 
103                 // early registration to avoid recursion
104                 s_classLoaderSystemDefinitions.put(loader, defs);
105                 s_classLoaderDefinitionLocations.put(loader, defsLocation);
106 
107                 // is this system classloader ?
108                 if ((loader == ClassLoader.getSystemClassLoader()) && !s_disableSystemWideDefinition) {
109                     // -D..file=... sysdef
110                     defs.addAll(DefinitionLoader.getDefaultDefinition(loader));
111                     defsLocation.add(new File(URL_JVM_OPTION_SYSTEM).toURL());
112                 }
113                 if (loader.getResource(WEB_WEB_INF_XML_FILE) != null) {
114                     Enumeration webres = loader.getResources(AOP_WEB_INF_XML_FILE);
115                     while (webres.hasMoreElements()) {
116                         URL def = (URL)webres.nextElement();
117                         if (isDefinedBy(loader, def)) {
118                             ;
119                         } else {
120                             defs.addAll(XmlParser.parseNoCache(loader, def));
121                             defsLocation.add(def);
122                         }
123                     }
124                 }
125 
126                 Enumeration res = loader.getResources(AOP_META_INF_XML_FILE);
127                 while (res.hasMoreElements()) {
128                     URL def = (URL)res.nextElement();
129                     if (isDefinedBy(loader, def)) {
130                         ;
131                     } else {
132                         defs.addAll(XmlParser.parseNoCache(loader, def));
133                         defsLocation.add(def);
134                     }
135                 }
136                 dump(loader);
137             } catch (Throwable t) {
138                 t.printStackTrace();
139             }
140         }
141     }
142 
143     /***
144      * Check if a given resource has already been registered to a classloader and its parent hierachy
145      *
146      * @param loader the classloader which might define the resource
147      * @param def    the resource
148      * @return true if classloader or its parent defines the resource
149      * @TODO what if child shares parent path?
150      * @TODO What happens with smylinking and xml in jars etc ?
151      * @TODO Needs test
152      * @TODO No need for the s_ map
153      * @TODO KICK the def map and crawl up the CL parents and redo a getResources check instead
154      */
155     public static boolean isDefinedBy(final ClassLoader loader, final URL def) {
156         if (loader == null) {
157             return false;
158         }
159         ArrayList defLocation = (ArrayList)s_classLoaderDefinitionLocations.get(loader);
160         if (defLocation != null) {
161             for (Iterator it = defLocation.iterator(); it.hasNext();) {
162                 URL definedDef = (URL)it.next();
163                 if (definedDef.sameFile(def)) {
164                     return true;
165                 }
166             }
167         }
168         return isDefinedBy(loader.getParent(), def);
169     }
170 
171     /***
172      * Pretty dump a classloader
173      *
174      * @param loader
175      */
176     public static void dump(final ClassLoader loader) {
177         if (!AspectWerkzPreProcessor.VERBOSE) {
178             return;
179         }
180         StringBuffer dump = new StringBuffer("******************************************************************");
181         dump.append("\n* ClassLoader = ");
182 
183         //Note: Tomcat classLoader.toString is too verbose so we allow 120 chars.
184         if ((loader != null) && (loader.toString().length() < 120)) {
185             dump.append(loader.toString());
186         } else if (loader != null) {
187             dump.append(loader.getClass().getName()).append("@").append(loader.hashCode());
188         } else {
189             dump.append("null");
190         }
191 
192         List defs = (List)s_classLoaderSystemDefinitions.get(loader);
193         for (Iterator it = defs.iterator(); it.hasNext();) {
194             SystemDefinition def = (SystemDefinition)it.next();
195             dump.append("\n* SystemID = ").append(def.getUuid());
196             dump.append(", ").append(def.getAspectDefinitions().size()).append(" aspects.");
197         }
198         for (Iterator it = ((List)s_classLoaderDefinitionLocations.get(loader)).iterator(); it.hasNext();) {
199             dump.append("\n* ").append(it.next());
200         }
201         dump.append("\n******************************************************************");
202         System.out.println(dump.toString());
203     }
204 
205     /***
206      * Returns the gathered SystemDefinition visible from a classloader. <p/>This method is using a cache. Caution when
207      * modifying this method since when an aop.xml is loaded, the aspect classes gets loaded as well, which triggers
208      * this cache, while the system is in fact not yet initialized properly. </p>
209      *
210      * @param loader
211      * @return List of SystemDefinition
212      */
213     public static synchronized List getHierarchicalDefs(final ClassLoader loader) {
214         // check cache
215         List defs;
216         if (!s_classLoaderHierarchicalSystemDefinitions.containsKey(loader)) {
217             // if runtime access before load time
218             if (!s_classLoaderSystemDefinitions.containsKey(loader)) {
219                 registerClassLoader(loader);
220             }
221             defs = new ArrayList();
222 
223             // put it in the cache now since this method is recursive
224             s_classLoaderHierarchicalSystemDefinitions.put(loader, defs);
225             if (loader == null) {
226                 ; // go on to put in the cache at the end
227             } else {
228                 ClassLoader parent = loader.getParent();
229                 defs.addAll(getHierarchicalDefs(parent));
230                 defs.addAll((List)s_classLoaderSystemDefinitions.get(loader));
231             }
232         } else {
233             defs = (List)s_classLoaderHierarchicalSystemDefinitions.get(loader);
234         }
235         return defs;
236     }
237 
238     /***
239      * Hotdeploy a list of SystemDefintions as defined at the level of the given ClassLoader <p/>Note: this is used for
240      * Offline mode TODO: sync StartupManager TODO: flush sub systems defs or allow different organization if wished so
241      * ?
242      *
243      * @param loader      ClassLoader
244      * @param definitions SystemDefinitions list
245      */
246     public static void deploySystemDefinitions(final ClassLoader loader, final List definitions) {
247         registerClassLoader(loader);
248         List defs = (List)s_classLoaderSystemDefinitions.get(loader);
249         defs.addAll(definitions);
250         dump(loader);
251     }
252 
253     /***
254      * Return the list of SystemDefinitions defined at the given ClassLoader level. Does not handles the ClassLoader
255      * hierarchy.
256      *
257      * @param loader
258      * @return SystemDefinitions list
259      */
260     public static List getSystemDefinitions(final ClassLoader loader) {
261         getHierarchicalDefs(loader);
262         return (List)s_classLoaderSystemDefinitions.get(loader);
263     }
264 
265     /***
266      * Lookup for a given SystemDefinition by uuid within a given ClassLoader The lookup does not go thru the
267      * ClassLoader hierarchy
268      *
269      * @param loader ClassLoader
270      * @param uuid   system uuid
271      * @return SystemDefinition or null if no such defined definition
272      */
273     public static SystemDefinition getSystemDefinition(final ClassLoader loader, final String uuid) {
274         getHierarchicalDefs(loader);
275         for (Iterator defs = getSystemDefinitions(loader).iterator(); defs.hasNext();) {
276             SystemDefinition def = (SystemDefinition)defs.next();
277             if (def.getUuid().equals(uuid)) {
278                 return def;
279             }
280         }
281         return null;
282     }
283 
284     /***
285      * Returns the list of all ClassLoaders registered so far Note: when a child ClassLoader is registered, all its
286      * parent hierarchy is registered
287      *
288      * @return ClassLoader Set
289      */
290     public static Set getAllRegisteredClassLoaders() {
291         return s_classLoaderSystemDefinitions.keySet();
292     }
293 
294     /***
295      * Turns on the option to avoid -Daspectwerkz.definition.file handling.
296      */
297     public static void disableSystemWideDefinition() {
298         s_disableSystemWideDefinition = true;
299     }
300 }