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 <system ..>
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
106 assertUuidUniqueWithinHierarchy(definitions);
107
108
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
115
116 for (int i = 0; i < m_aspectManagers.length; i++) {
117 SystemDefinition def = (SystemDefinition) definitions.get(i);
118 String uuid = def.getUuid();
119
120
121 AspectManager aspectManager = null;
122 try {
123 aspectManager = getAspectManager(uuid);
124 } catch (DefinitionException e) {
125 ;
126 }
127 if (aspectManager == null) {
128
129 aspectManager = new AspectManager(this, def);
130
131
132 } else {
133
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
172 for (int i = 0; i < m_aspectManagers.length; i++) {
173 AspectManager aspectManager = m_aspectManagers[i];
174
175
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
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 }