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.aspect.management;
9
10 import org.codehaus.aspectwerkz.AspectSystem;
11 import org.codehaus.aspectwerkz.ConstructorTuple;
12 import org.codehaus.aspectwerkz.ContextClassLoader;
13 import org.codehaus.aspectwerkz.CrossCuttingInfo;
14 import org.codehaus.aspectwerkz.DeploymentModel;
15 import org.codehaus.aspectwerkz.AdviceInfo;
16 import org.codehaus.aspectwerkz.MethodTuple;
17 import org.codehaus.aspectwerkz.Mixin;
18 import org.codehaus.aspectwerkz.annotation.AspectAnnotationParser;
19 import org.codehaus.aspectwerkz.aspect.AspectContainer;
20 import org.codehaus.aspectwerkz.definition.AspectDefinition;
21 import org.codehaus.aspectwerkz.definition.StartupManager;
22 import org.codehaus.aspectwerkz.definition.SystemDefinition;
23 import org.codehaus.aspectwerkz.expression.ExpressionContext;
24 import org.codehaus.aspectwerkz.util.Util;
25
26 import java.lang.reflect.Field;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.WeakHashMap;
33
34 /***
35 * Manages the aspects. <p/>Handles deployment, redeployment, management, configuration or redefinition of the aspects.
36 *
37 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
38 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
39 * @TODO: Must handle : - undeployment of the aspects - notification of all the pointcuts that it should remove a
40 * certain advice from the pointcut - notification of the JoinPoinManager.
41 */
42 public final class AspectManager {
43 /***
44 * The system this AspectManager is defined in.
45 */
46 public final AspectSystem m_system;
47
48 /***
49 * The definition.
50 */
51 public SystemDefinition m_definition;
52
53 /***
54 * The aspect registry.
55 */
56 private final AspectRegistry m_aspectRegistry;
57
58 /***
59 * Cache for the pointcuts.
60 *
61 * @TODO: when unweaving (and reordering) of aspects is supported then this cache must have a way of being
62 * invalidated.
63 */
64 private final Map m_pointcutCache = new WeakHashMap();
65
66 /***
67 * Cache for the cflow pointcuts.
68 *
69 * @TODO: when unweaving (and reordering) of aspects is supported then this cache must have a way of being
70 * invalidated.
71 */
72 private final Map m_cflowPointcutCache = new WeakHashMap();
73
74 /***
75 * Creates a new aspect manager.
76 *
77 * @param system the system
78 * @param definition the system definition
79 */
80 public AspectManager(final AspectSystem system, final SystemDefinition definition) {
81 m_system = system;
82 m_definition = definition;
83 m_aspectRegistry = new AspectRegistry(this, m_definition);
84 }
85
86 /***
87 * Initializes the manager. The initialization needs to be separated fromt he construction of the manager, and is
88 * triggered by the runtime system.
89 */
90 public void initialize() {
91 m_aspectRegistry.initialize();
92 }
93
94 /***
95 * Registers a new aspect.
96 *
97 * @param container the containern for the aspect to register
98 * @param aspectMetaData the aspect meta-data
99 */
100 public void register(final AspectContainer container, final PointcutManager aspectMetaData) {
101 m_aspectRegistry.register(container, aspectMetaData);
102 }
103
104 /***
105 * Creates and registers new aspect at runtime.
106 *
107 * @param name the name of the aspect
108 * @param aspectClassName the class name of the aspect
109 * @param deploymentModel the deployment model for the aspect (constants in the DeploymemtModel class, e.g. f.e.
110 * DeploymentModel.PER_JVM)
111 * @param loader an optional class loader (if null it uses the context classloader)
112 */
113 public void createAspect(
114 final String name,
115 final String aspectClassName,
116 final int deploymentModel,
117 final ClassLoader loader) {
118 if (name == null) {
119 throw new IllegalArgumentException("aspect name can not be null");
120 }
121 if (aspectClassName == null) {
122 throw new IllegalArgumentException("class name can not be null");
123 }
124 if ((deploymentModel < 0) || (deploymentModel > 3)) {
125 throw new IllegalArgumentException(deploymentModel + " is not a valid deployment model type");
126 }
127 Class aspectClass = null;
128 try {
129 if (loader == null) {
130 aspectClass = ContextClassLoader.loadClass(aspectClassName);
131 } else {
132 aspectClass = loader.loadClass(aspectClassName);
133 }
134 } catch (Exception e) {
135 StringBuffer msg = new StringBuffer();
136 msg.append("could not load aspect class [");
137 msg.append(aspectClassName);
138 msg.append("] with name ");
139 msg.append(name);
140 msg.append(": ");
141 msg.append(e.toString());
142 throw new RuntimeException(msg.toString());
143 }
144
145
146 AspectDefinition aspectDef = new AspectDefinition(aspectClassName, aspectClassName, m_definition.getUuid());
147 aspectDef.setDeploymentModel(DeploymentModel.getDeploymentModelAsString(deploymentModel));
148
149
150 AspectAnnotationParser.parse(aspectClass, aspectDef, m_definition);
151 m_definition.addAspect(aspectDef);
152 CrossCuttingInfo crossCuttingInfo = new CrossCuttingInfo(
153 null,
154 aspectClass,
155 aspectDef.getName(),
156 deploymentModel,
157 aspectDef,
158 new HashMap());
159 AspectContainer container = StartupManager.createAspectContainer(crossCuttingInfo);
160 crossCuttingInfo.setContainer(container);
161 m_aspectRegistry.register(container, new PointcutManager(name, deploymentModel));
162 }
163
164 /***
165 * Returns the UUID for the system.
166 *
167 * @return the UUID
168 */
169 public String getUuid() {
170 return m_definition.getUuid();
171 }
172
173 /***
174 * Returns the aspect container by its index.
175 *
176 * @param index the index of the aspect
177 * @return the aspect
178 */
179 public AspectContainer getAspectContainer(final int index) {
180 return m_aspectRegistry.getAspectContainer(index);
181 }
182
183 /***
184 * Returns the aspect container for a specific name.
185 *
186 * @param name the name of the aspect
187 * @return the the aspect prototype
188 */
189 public AspectContainer getAspectContainer(final String name) {
190 return m_aspectRegistry.getAspectContainer(name);
191 }
192
193 /***
194 * Returns an array with all the aspect containers.
195 *
196 * @return the aspect containers
197 */
198 public AspectContainer[] getAspectContainers() {
199 return m_aspectRegistry.getAspectContainers();
200 }
201
202 /***
203 * Returns the aspect for a specific name, deployed as perJVM.
204 *
205 * @param name the name of the aspect
206 * @return the the aspect
207 */
208 public Object getCrossCuttingInfo(final String name) {
209 return m_aspectRegistry.getCrossCuttingInfo(name);
210 }
211
212 /***
213 * Returns an array with all the cross-cutting infos.
214 *
215 * @return the cross-cutting infos
216 */
217 public CrossCuttingInfo[] getCrossCuttingInfos() {
218 AspectContainer[] aspectContainers = m_aspectRegistry.getAspectContainers();
219 CrossCuttingInfo[] infos = new CrossCuttingInfo[aspectContainers.length];
220 for (int i = 0; i < aspectContainers.length; i++) {
221 AspectContainer aspectContainer = aspectContainers[i];
222 infos[i] = aspectContainer.getCrossCuttingInfo();
223 }
224 return infos;
225 }
226
227 /***
228 * Retrieves a specific mixin based on its index.
229 *
230 * @param index the index of the introduction (aspect in this case)
231 * @return the the mixin (aspect in this case)
232 */
233 public Mixin getMixin(final int index) {
234 return m_aspectRegistry.getMixin(index);
235 }
236
237 /***
238 * Returns the mixin implementation for a specific name.
239 *
240 * @param name the name of the introduction (aspect in this case)
241 * @return the the mixin (aspect in this case)
242 */
243 public Mixin getMixin(final String name) {
244 return m_aspectRegistry.getMixin(name);
245 }
246
247 /***
248 * Returns the index for a specific name to aspect mapping.
249 *
250 * @param name the name of the aspect
251 * @return the index of the aspect
252 */
253 public int getAspectIndexFor(final String name) {
254 return m_aspectRegistry.getAspectIndexFor(name);
255 }
256
257 /***
258 * Returns the index for a specific name to advice mapping.
259 *
260 * @param name the name of the advice
261 * @return the index of the advice
262 */
263 public AdviceInfo getAdviceIndexFor(final String name) {
264 return m_aspectRegistry.getAdviceIndexFor(name);
265 }
266
267 /***
268 * Returns the pointcut manager for the name specified.
269 *
270 * @param name the name of the aspect
271 * @return thepointcut manager
272 */
273 public PointcutManager getPointcutManager(final String name) {
274 return m_aspectRegistry.getPointcutManager(name);
275 }
276
277 /***
278 * Returns a list with all the pointcut managers.
279 *
280 * @return thepointcut managers
281 */
282 public Collection getPointcutManagers() {
283 return m_aspectRegistry.getPointcutManagers();
284 }
285
286 /***
287 * Returns the pointcut list for the context specified. <p/>Caches the list, needed since the actual method call is
288 * expensive and is made each time a new instance of an advised class is created.
289 *
290 * @param ctx the expression context
291 * @return the pointcuts for this join point
292 */
293 public List getPointcuts(final ExpressionContext ctx) {
294 if (ctx == null) {
295 throw new IllegalArgumentException("expression context can not be null");
296 }
297 initialize();
298 List pointcuts;
299 if (m_pointcutCache.containsKey(ctx)) {
300 pointcuts = (List) m_pointcutCache.get(ctx);
301 if (pointcuts == null) {
302 System.out.println("AspectManager.getPointcuts " + "**IS NULL**");
303 pointcuts = new ArrayList();
304 }
305 } else {
306 pointcuts = m_aspectRegistry.getPointcuts(ctx);
307 synchronized (m_pointcutCache) {
308 m_pointcutCache.put(ctx, pointcuts);
309 }
310 }
311 return pointcuts;
312 }
313
314 /***
315 * Returns the cflow pointcut list for the context specified. <p/>Caches the list, needed since the actual method
316 * call is expensive and is made each time a new instance of an advised class is created.
317 *
318 * @param ctx the expression context
319 * @return the pointcuts for this join point
320 */
321 public List getCflowPointcuts(final ExpressionContext ctx) {
322 if (ctx == null) {
323 throw new IllegalArgumentException("expression context can not be null");
324 }
325 initialize();
326 List pointcuts;
327 if (m_cflowPointcutCache.containsKey(ctx)) {
328 pointcuts = (List) m_cflowPointcutCache.get(ctx);
329 if (pointcuts == null) {
330 pointcuts = new ArrayList();
331 }
332 } else {
333 pointcuts = m_aspectRegistry.getCflowPointcuts(ctx);
334 synchronized (m_cflowPointcutCache) {
335 m_cflowPointcutCache.put(ctx, pointcuts);
336 }
337 }
338 return pointcuts;
339 }
340
341 /***
342 * Checks if a specific class has an aspect defined.
343 *
344 * @param name the name of the aspect
345 * @return boolean true if the class has an aspect defined
346 */
347 public boolean hasAspect(final String name) {
348 return m_aspectRegistry.hasAspect(name);
349 }
350
351 /***
352 * Returns a specific method by the class and the method index.
353 *
354 * @param klass the class housing the method
355 * @param methodHash the method hash
356 * @return the method
357 */
358 public MethodTuple getMethodTuple(final Class klass, final int methodHash) {
359 return AspectRegistry.getMethodTuple(klass, methodHash);
360 }
361
362 /***
363 * Returns a specific constructor by the class and the constructor index.
364 *
365 * @param klass the class housing the method
366 * @param constructorHash the method hash
367 * @return the constructor
368 */
369 public ConstructorTuple getConstructorTuple(final Class klass, final int constructorHash) {
370 return AspectRegistry.getConstructorTuple(klass, constructorHash);
371 }
372
373 /***
374 * Returns a specific field by the class and the field index.
375 *
376 * @param klass the class housing the method
377 * @param fieldHash the method hash
378 * @return the field
379 */
380 public Field getField(final Class klass, final int fieldHash) {
381 return AspectRegistry.getField(klass, fieldHash);
382 }
383
384 /***
385 * Returns the string representation of the manager.
386 *
387 * @return
388 */
389 public String toString() {
390 StringBuffer sb = new StringBuffer("AspectManager::");
391 sb.append(this.hashCode());
392 sb.append("[").append(m_definition.getUuid());
393 sb.append(" @ ").append(Util.classLoaderToString(m_system.getDefiningClassLoader()));
394 sb.append("]");
395 return sb.toString();
396 }
397 }