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;
9   
10  import gnu.trove.TIntObjectHashMap;
11  import org.codehaus.aspectwerkz.aspect.management.AspectManager;
12  import org.codehaus.aspectwerkz.connectivity.Invoker;
13  import org.codehaus.aspectwerkz.connectivity.RemoteProxy;
14  import org.codehaus.aspectwerkz.connectivity.RemoteProxyServer;
15  import org.codehaus.aspectwerkz.definition.SystemDefinition;
16  import org.codehaus.aspectwerkz.exception.DefinitionException;
17  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
18  import org.codehaus.aspectwerkz.expression.CflowExpressionVisitorRuntime;
19  import org.codehaus.aspectwerkz.expression.ExpressionContext;
20  import org.codehaus.aspectwerkz.expression.PointcutType;
21  import org.codehaus.aspectwerkz.reflect.ClassInfo;
22  import org.codehaus.aspectwerkz.reflect.MethodInfo;
23  
24  import java.io.FileInputStream;
25  import java.lang.reflect.Method;
26  import java.util.List;
27  import java.util.Properties;
28  
29  /***
30   * Represents the aspect runtime system. <br/>Manages the different parts of the runtime system and provides and API to
31   * access and manage the system. <br/><p/>There is an AspectSystem per ClassLoader. An AspectSystem is aware of the
32   * classloader hierarchy and reflects it by gathering the AspectManager, which represents a single &lt;system ..&gt;
33   * entry. <p/>When an instance of an AspectSystem is created (perClassLoader), it checks for existence of previous
34   * AspectManager defined in parent ClassLoader. AspectManager are shared among AspectSystem as shown below: <br/>
35   * </p>
36   * 
37   * <pre>
38   * 
39   *  
40   *   
41   *    
42   *              [0d, 1d, 2d]  (3 SystemDefs, all defined in this classloader)
43   *                      /   \
44   *     [0r, 1r, 2r, 3d]      \  (3 reused, one more defined)
45   *                         [0r, 1r, 2r, 3d]  (one more defined, not the same)
46   *     
47   *    
48   *   
49   *  
50   * </pre>
51   * 
52   * </p>
53   * This composition strategy allow to avoid global static repository, but is tight to following ClassLoader parent
54   * hierarchy.
55   * </p>
56   * If an AspectManager is added at runtime, it should be added in the whole child hierarchy. TODO
57   * </p>
58   * <p/>TODO: caution when addding a new SystemDefinition in between. TODO: move the remote proxy elsewhere unless
59   * defining classloader is needed.
60   * 
61   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
62   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
63   */
64  public final class AspectSystem {
65      /***
66       * The path to the remote proxy server config file.
67       */
68      private static final boolean START_REMOTE_PROXY_SERVER = "true".equals(java.lang.System.getProperty(
69          "aspectwerkz.remote.server.run",
70          "false"));
71  
72      /***
73       * ClassLoader defining this AspectSystem
74       */
75      private final ClassLoader m_classLoader;
76  
77      /***
78       * The aspect managers in the order of the hierarchy
79       */
80      private AspectManager[] m_aspectManagers;
81  
82      /***
83       * Holds a list of the cflow join points passed by the control flow of the current thread.
84       * 
85       * @TODO: I think we need to use a static TL - need test coverage
86       */
87      private final ThreadLocal m_cflowStack = new ThreadLocal();
88  
89      /***
90       * The remote proxy server instance.
91       */
92      private RemoteProxyServer m_remoteProxyServer = null;
93  
94      /***
95       * Should NEVER be invoked by the user. Use <code>SystemLoader.getSystem(uuid)</code> to retrieve the system.
96       * <p/>Creates a new AspectWerkz AOPC system instance. <p/>
97       * 
98       * @param loader the classloader defining the system
99       * @param definitions the ordered SystemDefinitions for the system (whole hierarchy)
100      */
101     AspectSystem(ClassLoader loader, final List definitions) {
102         m_classLoader = loader;
103         m_aspectManagers = new AspectManager[definitions.size()];
104 
105         // assert uuid are unique in the ClassLoader hierarchy
106         assertUuidUniqueWithinHierarchy(definitions);
107 
108         // copy the AspectManagers from the parent ClassLoader AspectSystem
109         if ((loader != null) && (loader.getParent() != null)) {
110             AspectManager[] parentAspectManagers = SystemLoader.getSystem(loader.getParent()).getAspectManagers();
111             System.arraycopy(parentAspectManagers, 0, m_aspectManagers, 0, parentAspectManagers.length);
112         }
113 
114         // note: we should be able to go directly to the correct index instead of this loop and
115         // check
116         for (int i = 0; i < m_aspectManagers.length; i++) {
117             SystemDefinition def = (SystemDefinition) definitions.get(i);
118             String uuid = def.getUuid();
119 
120             // check if the SystemDefinition comes from a parent AspectSystem before adding it
121             AspectManager aspectManager = null;
122             try {
123                 aspectManager = getAspectManager(uuid);
124             } catch (DefinitionException e) {
125                 ;
126             }
127             if (aspectManager == null) {
128                 // new def defined in THIS CL and not a parent one
129                 aspectManager = new AspectManager(this, def);
130 
131                 //System.out.println("created AspectManager = " + uuid + ": " + aspectManager);
132             } else {
133                 //System.out.println("reused AspectManager = " + uuid + ": " + aspectManager);
134                 continue;
135             }
136             m_aspectManagers[i] = aspectManager;
137         }
138         if (START_REMOTE_PROXY_SERVER) {
139             startRemoteProxyServer();
140         }
141     }
142 
143     /***
144      * Returns the classloader which defines this AspectSystem
145      * 
146      * @return the classloader which defines this AspectSystem
147      */
148     public ClassLoader getDefiningClassLoader() {
149         return m_classLoader;
150     }
151 
152     /***
153      * Returns an AspectManager by its index. The index are stable when the ClassLoader hierarchy is crossed from top to
154      * bottom
155      * 
156      * @param aspectManagerIndex
157      * @return AspectManager, or throw an IndexOutOfBoundException
158      */
159     public AspectManager getAspectManager(int aspectManagerIndex) {
160         return m_aspectManagers[aspectManagerIndex];
161     }
162 
163     /***
164      * Returns an AspectManager by its uuid
165      * 
166      * @param uuid
167      * @return AspectManager
168      * @throws DefinitionException (runtime exception) if not found
169      */
170     public AspectManager getAspectManager(final String uuid) {
171         // Note: uuid is assumed to be unique within an AspectSystem
172         for (int i = 0; i < m_aspectManagers.length; i++) {
173             AspectManager aspectManager = m_aspectManagers[i];
174 
175             // the null check makes sense only in the flow of <init>
176             if ((aspectManager != null) && aspectManager.getUuid().equals(uuid)) {
177                 return aspectManager;
178             }
179         }
180         throw new DefinitionException("no AspectManager with system id " + uuid + " in " + m_classLoader);
181     }
182 
183     /***
184      * Initializes the system. The initialization needs to be separated from the construction of the manager, and is
185      * triggered by the runtime system
186      */
187     public void initialize() {
188         for (int i = 0; i < m_aspectManagers.length; i++) {
189             m_aspectManagers[i].initialize();
190         }
191     }
192 
193     /***
194      * Returns the aspect managers for this system.
195      * 
196      * @return the aspect managers
197      */
198     public AspectManager[] getAspectManagers() {
199         return m_aspectManagers;
200     }
201 
202     /***
203      * Registers entering of a control flow join point.
204      * 
205      * @param pointcutType the pointcut type
206      * @param methodInfo the method info
207      * @param withinInfo the within info
208      */
209     public void enteringControlFlow(
210         final PointcutType pointcutType,
211         final MethodInfo methodInfo,
212         final ClassInfo withinInfo) {
213         if (pointcutType == null) {
214             throw new IllegalArgumentException("pointcut type can not be null");
215         }
216         if (methodInfo == null) {
217             throw new IllegalArgumentException("method info can not be null");
218         }
219         TIntObjectHashMap cflows = (TIntObjectHashMap) m_cflowStack.get();
220         if (cflows == null) {
221             cflows = new TIntObjectHashMap();
222         }
223         ExpressionContext expressionContext = new ExpressionContext(pointcutType, methodInfo, withinInfo);
224         cflows.put(expressionContext.hashCode(), expressionContext);
225         m_cflowStack.set(cflows);
226     }
227 
228     /***
229      * Registers exiting from a control flow join point.
230      * 
231      * @param pointcutType the pointcut type
232      * @param methodInfo the method info
233      * @param withinInfo the within info
234      */
235     public void exitingControlFlow(
236         final PointcutType pointcutType,
237         final MethodInfo methodInfo,
238         final ClassInfo withinInfo) {
239         if (pointcutType == null) {
240             throw new IllegalArgumentException("pointcut type can not be null");
241         }
242         if (methodInfo == null) {
243             throw new IllegalArgumentException("method info can not be null");
244         }
245         TIntObjectHashMap cflows = (TIntObjectHashMap) m_cflowStack.get();
246         if (cflows == null) {
247             return;
248         }
249         ExpressionContext ctx = new ExpressionContext(pointcutType, methodInfo, withinInfo);
250         cflows.remove(ctx.hashCode());
251         m_cflowStack.set(cflows);
252     }
253 
254     /***
255      * Checks if we are in the control flow of a join point picked out by a specific pointcut expression.
256      * 
257      * @param expression the cflow expression runtime visitor
258      * @param expressionContext the join point expression context whose pointcut contains cflows sub expression(s)
259      * @return boolean
260      */
261     public boolean isInControlFlowOf(final CflowExpressionVisitorRuntime expression, ExpressionContext expressionContext) {
262         if (expression == null) {
263             throw new IllegalArgumentException("expression can not be null");
264         }
265         TIntObjectHashMap cflows = (TIntObjectHashMap) m_cflowStack.get();
266         if (cflows == null) {
267             // we still need to evaluate the expression to handle "NOT cflow"
268             cflows = new TIntObjectHashMap();
269         }
270         if (expression.matchCflowStack(cflows.getValues(), expressionContext)) {
271             return true;
272         }
273         return false;
274     }
275 
276     /***
277      * Starts up the remote proxy server.
278      * 
279      * @TODO: option to shut down in a nice way?
280      */
281     private void startRemoteProxyServer() {
282         m_remoteProxyServer = new RemoteProxyServer(ContextClassLoader.getLoader(), getInvoker());
283         m_remoteProxyServer.start();
284     }
285 
286     /***
287      * Returns the Invoker instance to use.
288      * 
289      * @return the Invoker
290      */
291     private Invoker getInvoker() {
292         Invoker invoker;
293         try {
294             Properties properties = new Properties();
295             properties.load(new FileInputStream(java.lang.System.getProperty("aspectwerkz.resource.bundle")));
296             String className = properties.getProperty("remote.server.invoker.classname");
297             invoker = (Invoker) ContextClassLoader.getLoader().loadClass(className).newInstance();
298         } catch (Exception e) {
299             invoker = getDefaultInvoker();
300         }
301         return invoker;
302     }
303 
304     /***
305      * Returns the default Invoker.
306      * 
307      * @return the default invoker
308      */
309     private Invoker getDefaultInvoker() {
310         return new Invoker() {
311             public Object invoke(
312                 final String handle,
313                 final String methodName,
314                 final Class[] paramTypes,
315                 final Object[] args,
316                 final Object context) {
317                 Object result;
318                 try {
319                     final Object instance = RemoteProxy.getWrappedInstance(handle);
320                     final Method method = instance.getClass().getMethod(methodName, paramTypes);
321                     result = method.invoke(instance, args);
322                 } catch (Exception e) {
323                     throw new WrappedRuntimeException(e);
324                 }
325                 return result;
326             }
327         };
328     }
329 
330     /***
331      * Checks uuid unicity within the list. Throw a DefinitionException on failure.
332      * 
333      * @param definitions
334      * @TODO AVAOPC algo is crapped, check earlier and avoid exception but do a WARN (in SysDefContainer)
335      */
336     private static void assertUuidUniqueWithinHierarchy(final List definitions) {
337         for (int i = 0; i < definitions.size(); i++) {
338             SystemDefinition systemDefinition = (SystemDefinition) definitions.get(i);
339             for (int j = 0; j < definitions.size(); j++) {
340                 if (j == i) {
341                     continue;
342                 }
343                 SystemDefinition systemDefinition2 = (SystemDefinition) definitions.get(j);
344                 if (systemDefinition2.getUuid().equals(systemDefinition.getUuid())) {
345                     throw new DefinitionException("UUID is not unique within hierarchy: " + systemDefinition.getUuid());
346                 }
347             }
348         }
349     }
350 
351     /***
352      * Propagates the aspect managers.
353      * 
354      * @param block
355      * @param blockSizeBefore
356      */
357     public void propagateAspectManagers(final AspectManager[] block, final int blockSizeBefore) {
358         AspectManager[] newAspectManagers = new AspectManager[m_aspectManagers.length
359             + (block.length - blockSizeBefore)];
360         System.arraycopy(block, 0, newAspectManagers, 0, block.length);
361         if (blockSizeBefore < m_aspectManagers.length) {
362             System.arraycopy(
363                 m_aspectManagers,
364                 blockSizeBefore,
365                 newAspectManagers,
366                 block.length + 1,
367                 m_aspectManagers.length - blockSizeBefore);
368         }
369         m_aspectManagers = newAspectManagers;
370     }
371 }