How to emulate ServiceMBeanSupport using AOP
genman Jul 26, 2007 2:32 AMI got this to work, it's pretty slick actually... Thanks Ales.
Service.java :
/** * Interface defines the basic operations for a service. */ public interface Service { /** * Possible states for a service. */ enum State { /** * Initial state; no lifecycle methods called. */ INITIAL, /** * Created state; dependencies not ready. */ CREATED, /** * Started state; dependencies in started state. */ STARTED, /** * Stopped state; dependencies also stopped. */ STOPPED, /** * Destroyed state. */ DESTROYED } /** * Starts the service * @throws Exception if this lifecycle method failed */ public void start() throws Exception; /** * Starts the service * @throws Exception if this lifecycle method failed */ public void stop() throws Exception; /** * Creates the service * @throws Exception if this lifecycle method failed */ public void create() throws Exception; /** * Destroys the service * @throws Exception if this lifecycle method failed */ public void destroy() throws Exception; /** * Returns the service state; or null if not known. */ public State getState(); /** * Returns the service name; or null if not known. */ public String getName(); }
LifecycleInterceptor.java:
public class LifecycleInterceptor { private Log log = LogFactory.getLog(getClass()); private Kernel kernel; /** * Maps the service method names to controller states. */ private enum Methods { start(ControllerState.INSTALLED), stop(ControllerState.CREATE), create(ControllerState.CREATE), destroy(ControllerState.INSTANTIATED); private ControllerState cs; Methods(ControllerState cs) { this.cs = cs; } public ControllerState getState() { return cs; } }; private boolean calledByKernel() { // The following detects if the kernel is calling us StackTraceElement[] sta = Thread.currentThread().getStackTrace(); for (StackTraceElement se : sta) { String c = se.getClassName(); if (c.startsWith("org.jboss.kernel")) return true; } return false; } /** * Determines the controller context, by method name chooses * and sets new controller state in the kernel. * * If called by the kernel, returns. * * @param inv method invocation */ public Object handle(Invocation inv) throws Throwable { Service target = (Service) inv.getTargetObject(); if (calledByKernel()) return inv.invokeNext(); KernelController controller = kernel.getController(); ControllerContext context; MethodInvocation minv = (MethodInvocation)inv; String mname = minv.getMethod().getName(); context = kernel.getController().getContext(target.getName(), null); if (context == null) { log.debug("service not installed " + target.getName()); return inv.invokeNext(); } // We assume the method call is a lifecycle method (by configuration) ControllerState state = Methods.valueOf(mname).getState(); log.debug("controller change " + target.getName() + " to " + state.getStateString() + " for " + mname + "()"); controller.change(context, state); return null; } /** * Returns kernel. */ public Kernel getKernel() { return kernel; } /** * Sets kernel. */ public void setKernel(Kernel kernel) { this.kernel = kernel; } }
-beans.xml declaration:
<aop:aspect xmlns:aop="urn:jboss:aop-beans:1.0" name="LifecycleAdvice" class="LifecycleInterceptor" method="handle" pointcut="execution(public void com.autodesk.lbs.service.Service->*())"> <property name="kernel"> <inject bean="jboss.kernel:service=Kernel" /> </property> <depends>LifecycleCallback</depends> </aop:aspect>
One weakness is that the Name has to be stored in the target, there's no way, given a Service to find its name in the Microcontainer. (That I know of.)