/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz;
import gnu.trove.TIntObjectHashMap;
import org.codehaus.aspectwerkz.aspect.management.AspectManager;
import org.codehaus.aspectwerkz.connectivity.Invoker;
import org.codehaus.aspectwerkz.connectivity.RemoteProxy;
import org.codehaus.aspectwerkz.connectivity.RemoteProxyServer;
import org.codehaus.aspectwerkz.definition.SystemDefinition;
import org.codehaus.aspectwerkz.exception.DefinitionException;
import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
import org.codehaus.aspectwerkz.expression.CflowExpressionVisitorRuntime;
import org.codehaus.aspectwerkz.expression.ExpressionContext;
import org.codehaus.aspectwerkz.expression.PointcutType;
import org.codehaus.aspectwerkz.reflect.ClassInfo;
import org.codehaus.aspectwerkz.reflect.MethodInfo;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Properties;
/**
* Represents the aspect runtime system.<br/> Manages the different parts of the runtime system and provides and API to
* access and manage the system.<br/>
* <p/>
* There is an AspectSystem per ClassLoader. An AspectSystem is aware of the classloader hierarchy and reflects it by
* gathering the AspectManager, which represents a single <system ..> entry.
* <p/>
* When an instance of an AspectSystem is created (perClassLoader), it checks for existence of previous AspectManager
* defined in parent ClassLoader. AspectManager are shared among AspectSystem as shown below:<br/> </p>
* <pre>
* [0d, 1d, 2d] (3 SystemDefs, all defined in this classloader)
* / \
* [0r, 1r, 2r, 3d] \ (3 reused, one more defined)
* [0r, 1r, 2r, 3d] (one more defined, not the same)
* </pre>
* </p> This composition strategy allow to avoid global static repository, but is tight to following ClassLoader parent
* hierarchy. </p> If an AspectManager is added at runtime, it should be added in the whole child hierarchy. TODO </p>
* <p/>
* TODO: caution when addding a new SystemDefinition in between. TODO: move the remote proxy elsewhere unless defining
* classloader is needed.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r</a>
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
*/
public final class AspectSystem {
/**
* The path to the remote proxy server config file.
*/
private static final boolean START_REMOTE_PROXY_SERVER = "true".equals(
java.lang.System.getProperty(
"aspectwerkz.remote.server.run",
"false"
)
);
/**
* ClassLoader defining this AspectSystem
*/
private final ClassLoader m_classLoader;
/**
* The aspect managers in the order of the hierarchy
*/
private AspectManager[] m_aspectManagers;
/**
* Holds a list of the cflow join points passed by the control flow of the current thread.
*
* @TODO: I think we need to use a static TL - need test coverage
*/
private final ThreadLocal m_cflowStack = new ThreadLocal();
/**
* The remote proxy server instance.
*/
private RemoteProxyServer m_remoteProxyServer = null;
/**
* Should NEVER be invoked by the user. Use <code>SystemLoader.getSystem(uuid)</code> to retrieve the system.
* <p/>
* Creates a new AspectWerkz AOPC system instance.
* <p/>
*
* @param loader the classloader defining the system
* @param definitions the ordered SystemDefinitions for the system (whole hierarchy)
*/
AspectSystem(ClassLoader loader, final List definitions) {
m_classLoader = loader;
m_aspectManagers = new AspectManager[definitions.size()];
// assert uuid are unique in the ClassLoader hierarchy
assertUuidUniqueWithinHierarchy(definitions);
// copy the AspectManagers from the parent ClassLoader AspectSystem
if ((loader != null) && (loader.getParent() != null)) {
AspectManager[] parentAspectManagers = SystemLoader.getSystem(loader.getParent()).getAspectManagers();
System.arraycopy(parentAspectManagers, 0, m_aspectManagers, 0, parentAspectManagers.length);
}
// note: we should be able to go directly to the correct index instead of this loop and check
for (int i = 0; i < m_aspectManagers.length; i++) {
SystemDefinition def = (SystemDefinition)definitions.get(i);
String uuid = def.getUuid();
// check if the SystemDefinition comes from a parent AspectSystem before adding it
AspectManager aspectManager = null;
try {
aspectManager = getAspectManager(uuid);
} catch (DefinitionException e) {
;
}
if (aspectManager == null) {
// new def defined in THIS CL and not a parent one
aspectManager = new AspectManager(this, def);
//System.out.println("created AspectManager = " + uuid + ": " + aspectManager);
} else {
//System.out.println("reused AspectManager = " + uuid + ": " + aspectManager);
continue;
}
m_aspectManagers[i] = aspectManager;
}
if (START_REMOTE_PROXY_SERVER) {
startRemoteProxyServer();
}
}
/**
* Returns the classloader which defines this AspectSystem
*
* @return the classloader which defines this AspectSystem
*/
public ClassLoader getDefiningClassLoader() {
return m_classLoader;
}
/**
* Returns an AspectManager by its index. The index are stable when the ClassLoader hierarchy is crossed from top to
* bottom
*
* @param aspectManagerIndex
* @return AspectManager, or throw an IndexOutOfBoundException
*/
public AspectManager getAspectManager(int aspectManagerIndex) {
return m_aspectManagers[aspectManagerIndex];
}
/**
* Returns an AspectManager by its uuid
*
* @param uuid
* @return AspectManager
* @throws DefinitionException (runtime exception) if not found
*/
public AspectManager getAspectManager(final String uuid) {
// Note: uuid is assumed to be unique within an AspectSystem
for (int i = 0; i < m_aspectManagers.length; i++) {
AspectManager aspectManager = m_aspectManagers[i];
// the null check makes sense only in the flow of <init>
if ((aspectManager != null) && aspectManager.getUuid().equals(uuid)) {
return aspectManager;
}
}
throw new DefinitionException("no AspectManager with system id " + uuid + " in " + m_classLoader);
}
/**
* Initializes the system. The initialization needs to be separated from the construction of the manager, and is
* triggered by the runtime system
*/
public void initialize() {
for (int i = 0; i < m_aspectManagers.length; i++) {
m_aspectManagers[i].initialize();
}
}
/**
* Returns the aspect managers for this system.
*
* @return the aspect managers
*/
public AspectManager[] getAspectManagers() {
return m_aspectManagers;
}
/**
* Registers entering of a control flow join point.
*
* @param pointcutType the pointcut type
* @param methodInfo the method info
* @param withinInfo the within info
*/
public void enteringControlFlow(
final PointcutType pointcutType, final MethodInfo methodInfo,
final ClassInfo withinInfo) {
if (pointcutType == null) {
throw new IllegalArgumentException("pointcut type can not be null");
}
if (methodInfo == null) {
throw new IllegalArgumentException("method info can not be null");
}
TIntObjectHashMap cflows = (TIntObjectHashMap)m_cflowStack.get();
if (cflows == null) {
cflows = new TIntObjectHashMap();
}
ExpressionContext expressionContext = new ExpressionContext(pointcutType, methodInfo, withinInfo);
cflows.put(expressionContext.hashCode(), expressionContext);
m_cflowStack.set(cflows);
}
/**
* Registers exiting from a control flow join point.
*
* @param pointcutType the pointcut type
* @param methodInfo the method info
* @param withinInfo the within info
*/
public void exitingControlFlow(
final PointcutType pointcutType, final MethodInfo methodInfo,
final ClassInfo withinInfo) {
if (pointcutType == null) {
throw new IllegalArgumentException("pointcut type can not be null");
}
if (methodInfo == null) {
throw new IllegalArgumentException("method info can not be null");
}
TIntObjectHashMap cflows = (TIntObjectHashMap)m_cflowStack.get();
if (cflows == null) {
return;
}
ExpressionContext ctx = new ExpressionContext(pointcutType, methodInfo, withinInfo);
cflows.remove(ctx.hashCode());
m_cflowStack.set(cflows);
}
/**
* Checks if we are in the control flow of a join point picked out by a specific pointcut expression.
*
* @param expression the cflow expression runtime visitor
* @param expressionContext the join point expression context whose pointcut contains cflows sub expression(s)
* @return boolean
*/
public boolean isInControlFlowOf(
final CflowExpressionVisitorRuntime expression, ExpressionContext expressionContext) {
if (expression == null) {
throw new IllegalArgumentException("expression can not be null");
}
TIntObjectHashMap cflows = (TIntObjectHashMap)m_cflowStack.get();
if (cflows == null) {
// we still need to evaluate the expression to handle "NOT cflow"
cflows = new TIntObjectHashMap();
}
if (expression.matchCflowStack(cflows.getValues(), expressionContext)) {
return true;
}
return false;
}
/**
* Starts up the remote proxy server.
*
* @TODO: option to shut down in a nice way?
*/
private void startRemoteProxyServer() {
m_remoteProxyServer = new RemoteProxyServer(ContextClassLoader.getLoader(), getInvoker());
m_remoteProxyServer.start();
}
/**
* Returns the Invoker instance to use.
*
* @return the Invoker
*/
private Invoker getInvoker() {
Invoker invoker;
try {
Properties properties = new Properties();
properties.load(new FileInputStream(java.lang.System.getProperty("aspectwerkz.resource.bundle")));
String className = properties.getProperty("remote.server.invoker.classname");
invoker = (Invoker)ContextClassLoader.getLoader().loadClass(className).newInstance();
} catch (Exception e) {
invoker = getDefaultInvoker();
}
return invoker;
}
/**
* Returns the default Invoker.
*
* @return the default invoker
*/
private Invoker getDefaultInvoker() {
return new Invoker() {
public Object invoke(
final String handle, final String methodName, final Class[] paramTypes,
final Object[] args, final Object context) {
Object result;
try {
final Object instance = RemoteProxy.getWrappedInstance(handle);
final Method method = instance.getClass().getMethod(methodName, paramTypes);
result = method.invoke(instance, args);
} catch (Exception e) {
throw new WrappedRuntimeException(e);
}
return result;
}
};
}
/**
* Checks uuid unicity within the list. Throw a DefinitionException on failure.
*
* @param definitions
* @TODO AVAOPC algo is crapped, check earlier and avoid exception but do a WARN (in SysDefContainer)
*/
private static void assertUuidUniqueWithinHierarchy(final List definitions) {
for (int i = 0; i < definitions.size(); i++) {
SystemDefinition systemDefinition = (SystemDefinition)definitions.get(i);
for (int j = 0; j < definitions.size(); j++) {
if (j == i) {
continue;
}
SystemDefinition systemDefinition2 = (SystemDefinition)definitions.get(j);
if (systemDefinition2.getUuid().equals(systemDefinition.getUuid())) {
throw new DefinitionException(
"UUID is not unique within hierarchy: " + systemDefinition.getUuid()
);
}
}
}
}
/**
* Propagates the aspect managers.
*
* @param block
* @param blockSizeBefore
*/
public void propagateAspectManagers(final AspectManager[] block, final int blockSizeBefore) {
AspectManager[] newAspectManagers = new AspectManager[m_aspectManagers.length
+ (block.length - blockSizeBefore)];
System.arraycopy(block, 0, newAspectManagers, 0, block.length);
if (blockSizeBefore < m_aspectManagers.length) {
System.arraycopy(
m_aspectManagers, blockSizeBefore, newAspectManagers, block.length + 1,
m_aspectManagers.length - blockSizeBefore
);
}
m_aspectManagers = newAspectManagers;
}
}