/** * This class is part of project that implements Aspectual Components * * Author: Predrag Petkovic, predrag@ccs.neu.edu * Northeastern University * * This class represents an implementation of the connector, that connects * application classes and aspectual components. Constructor of this class * takes as an argument instance of edu.neu.ccs.aspects.map.Map and create * one instance of the mapped component that will participate in this connection. */ package edu.neu.ccs.aspects; import edu.neu.ccs.beans.reflect.event.MethodEvent; import edu.neu.ccs.beans.reflect.event.MethodListener; import edu.neu.ccs.beans.visitor.event.BeforeEvent; import edu.neu.ccs.beans.visitor.event.BeforeListener; import edu.neu.ccs.beans.visitor.event.AfterEvent; import edu.neu.ccs.beans.visitor.event.AfterListener; import edu.neu.ccs.beans.visitor.ArroundException; import edu.neu.ccs.aspects.map.Map; import java.lang.reflect.*; import java.util.Hashtable; class AspectConnector implements ConnectorInterface, Connector { public Object getHost() { return host; } public Object getReturnValue() { return returnValue; } /** * Receives before event from the application class * (precisely, from the isntance of the invoker inside of the appl. class) */ public synchronized void receiveBeforeEvent(BeforeEvent event) { if (!(event.getSource() instanceof Invoker)) return; Invoker invoker = (Invoker)event.getSource(); Method methodToMap = invoker.getVisitedMethod(); Object sourceObject = invoker.getSource(); Object[] args = (Object[])invoker.getData(); Class sourceClass = invoker.getSupportedClass(); Object participant = getParticipant(sourceClass); if (participant == null) { // sourceClass is not mapped to any particpant in the component return; } boolean replaced = false; Method mappedMethod = getReplacingMethod(methodToMap, sourceClass); if (mappedMethod != null) replaced = true; else mappedMethod = getBeforeMethod(methodToMap, sourceClass); if (mappedMethod == null) return; Object[] mappedArgs = getMappedArguments(args, mappedMethod); setHost(sourceObject); Object returnValue = invoke(participant, mappedMethod, mappedArgs); if (replaced) { invoker.setReturnValue(returnValue); throw new ArroundException(); } } /** * Receives a method event from the component. */ public void receiveMethodEvent(MethodEvent event) { Object applicationObject = event.getSource(); Method expectedMethod = event.getVisitedMethod(); Object[] expectedArgs = (Object[])event.getData(); Class participantClass = expectedMethod.getDeclaringClass(); Method method = getExpectedMethod(expectedMethod, participantClass); if (method == null) { System.out.println("ALERT! Sholdn't happen."); return; } Object[] args = getMappedArguments(expectedArgs, method); Object returnValue = invoke(applicationObject, method, args); setReturnValue(returnValue); } /** * Receives after event from the application class * (precisely, from the isntance of the invoker inside of the appl. class) */ public synchronized void receiveAfterEvent(AfterEvent event) { if (!(event.getSource() instanceof Invoker)) return; Invoker invoker = (Invoker)event.getSource(); Method methodToMap = invoker.getVisitedMethod(); Object sourceObject = invoker.getSource(); Object[] args = (Object[])invoker.getData(); Class sourceClass = invoker.getSupportedClass(); Object participant = getParticipant(sourceClass); if (participant == null) { // sourceClass is not mapped to any participant in the component return; } Method mappedMethod = getAfterMethod(methodToMap, sourceClass); if (mappedMethod == null) return; setHost(sourceObject); Object[] mappedArgs = getMappedArguments(args, mappedMethod); invoke(participant, mappedMethod, mappedArgs); } public AspectConnector(Map map) throws ConnectingException { try { Class componentClass = map.getComponentClass(); component = (Component) componentClass.newInstance(); component.initComponent(this); this.map = map; makeParticipantsMap(componentClass); connectApplicationClasses(); } catch (Exception e) { System.out.println(e); throw new ConnectingException(e.getMessage()); } } public void disconnect() { try { finalize(); } catch (Throwable e){} } public void finalize() throws Throwable { Class[] classes = map.getApplicationClasses(); Class[] argBefore = new Class[] {BeforeListener.class}; Class[] argAfter = new Class[] {AfterListener.class}; Object[] args = new Object[] {this}; for (int i = 0; i < classes.length; i++) { Method before = classes[i].getMethod("removeBeforeListener", argBefore); Method after = classes[i].getMethod("removeAfterListener", argAfter); before.invoke(null, args); after.invoke(null, args); } } // private instance of connecting component private Component component; private Map map; // Hash(ParticipantClass, ParticipantObject) private Hashtable participants; /** * Participant objects mapping, depend on the fact * that all public fields of the component are participants * and that all of them are of different types. */ private void makeParticipantsMap(Class componentClass) throws IllegalAccessException { participants = new Hashtable(); Field[] fields = componentClass.getFields(); for (int i = 0; i < fields.length; i++) participants.put(fields[i].getType(), fields[i].get(component)); } private void connectApplicationClasses() throws Exception { Class[] classes = map.getApplicationClasses(); Class[] argBefore = new Class[] {BeforeListener.class}; Class[] argAfter = new Class[] {AfterListener.class}; Object[] args = new Object[] {this}; for (int i = 0; i < classes.length; i++) { Method before = classes[i].getMethod("addBeforeListener", argBefore); Method after = classes[i].getMethod("addAfterListener", argAfter); before.invoke(null, args); after.invoke(null, args); } } private Object getParticipant(Class applicationClass) { Class participantClass = map.getParticipantClass(applicationClass); Object o = participants.get(participantClass); return o; } private Method getBeforeMethod(Method methodToMap, Class applicationClass) { return map.getBeforeMethod(methodToMap, applicationClass); } private Method getReplacingMethod(Method methodToMap, Class applicationClass) { return map.getReplacingMethod(methodToMap, applicationClass); } private Method getAfterMethod(Method methodToMap, Class applicationClass) { return map.getAfterMethod(methodToMap, applicationClass); } private Method getExpectedMethod(Method mappedMethod, Class participantClass) { return map.getExpectedMethod(mappedMethod, participantClass); } /** * Returns truncated arguments, so they can fit mapped method */ private Object[] getMappedArguments(Object[] args, Method mappedMethod) { int numberOfArgs = mappedMethod.getParameterTypes().length; Object[] mappedArgs = new Object[numberOfArgs]; for (int i = 0; i < numberOfArgs; i++) mappedArgs[i] = args[i]; return mappedArgs; } private Object invoke(Object object, Method method, Object[] arguments){ try { return method.invoke(object, arguments); } catch (IllegalAccessException exception) { exception.printStackTrace(); } catch (IllegalArgumentException exception) { exception.printStackTrace(); } catch (InvocationTargetException exception) { exception.printStackTrace(); } return null; } private Object host = null; private void setHost(Object o) { host = o; } private Object returnValue; private void setReturnValue(Object v) { returnValue = v; } }