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();
35
36
37
38 /***
39 * Map of SystemDefinition[List] per ClassLoader, with the hierarchy structure
40 */
41 public static final Map s_classLoaderHierarchicalSystemDefinitions = new WeakHashMap();
42
43
44
45
46 /***
47 * Map of SystemDefinition location (as URL[List]) per ClassLoader
48 */
49 public static final Map s_classLoaderDefinitionLocations = new WeakHashMap();
50
51
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
91 if (loader == null) {
92 return;
93 }
94 if (loader != null) {
95
96 registerClassLoader(loader.getParent());
97
98
99 try {
100 List defs = new ArrayList();
101 List defsLocation = new ArrayList();
102
103
104 s_classLoaderSystemDefinitions.put(loader, defs);
105 s_classLoaderDefinitionLocations.put(loader, defsLocation);
106
107
108 if ((loader == ClassLoader.getSystemClassLoader()) && !s_disableSystemWideDefinition) {
109
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
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
215 List defs;
216 if (!s_classLoaderHierarchicalSystemDefinitions.containsKey(loader)) {
217
218 if (!s_classLoaderSystemDefinitions.containsKey(loader)) {
219 registerClassLoader(loader);
220 }
221 defs = new ArrayList();
222
223
224 s_classLoaderHierarchicalSystemDefinitions.put(loader, defs);
225 if (loader == null) {
226 ;
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 }