Package ch.ethz.prose.engine

Source Code of ch.ethz.prose.engine.JoinPointManager$ClassLoadConsumer

//
//  This file is part of the prose package.
//
//  The contents of this file are subject to the Mozilla Public License
//  Version 1.1 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.mozilla.org/MPL/
//
//  Software distributed under the License is distributed on an "AS IS" basis,
//  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//  for the specific language governing rights and limitations under the
//  License.
//
//  The Original Code is prose.
//
//  The Initial Developers of the Original Code are Andrei Popovici and
//  Angela Nicoara. Portions created by Andrei Popovici and Angela Nicoara
//  are Copyright (C) 2002 Andrei Popovici and Angela Nicoara.
//  All Rights Reserved.
//
//  Contributor(s):
//  $Id: JoinPointManager.java,v 1.6 2008/11/18 11:37:06 anicoara Exp $
//  =====================================================================
//

package ch.ethz.prose.engine;

// used packages
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import ch.ethz.jvmai.ExceptionJoinPoint;
import ch.ethz.jvmai.ExceptionCatchJoinPoint;
import ch.ethz.jvmai.FieldAccessJoinPoint;
import ch.ethz.jvmai.FieldModificationJoinPoint;
import ch.ethz.jvmai.JVMAspectInterface;
import ch.ethz.jvmai.JoinPoint;
import ch.ethz.jvmai.JoinPointHook;
import ch.ethz.jvmai.MethodEntryJoinPoint;
import ch.ethz.jvmai.MethodExitJoinPoint;
import ch.ethz.jvmai.MethodRedefineJoinPoint;
import ch.ethz.jvmai.ConstructorJoinPoint;
import ch.ethz.prose.Insertable;
import ch.ethz.prose.ProsePermission;
import ch.ethz.prose.crosscut.Crosscut;
import ch.ethz.inf.util.Logger;
import ch.ethz.jvmai.SunBCELRepositoryInterface;

/**
* Class JoinPointManagerImpl defines implements two of the basic methods
* <code>JoinPointManager</code>. Subclasses of <code>AbstractJoinPointManager</code>
* should define only the link between registering event requests and the proper notification.
* They should use the <code>notifyListeners</code> method in order to notify the listeners
* of an event that this event occured, and invoke <code>handleExceptions</code> to handle
* notification exceptions. By providing an implementation of <code>handleExceptions</code>
* with a different <code>handleExceptions</code> method, subclasses may change the policy
* for exception handling.
* <p>
* All implementations of <code>AbstractJoinPointManager</code> should also define the
* abstract methods <code>suspendListenerNotification</code> and <code>resumeListenerNotification</code>
*
* @version  $Revision: 1.6 $
* @author  Andrei Popovici
* @author  Angela Nicoara
*/
public class JoinPointManager extends JoinPointHook implements JoinPointQuery {

  private static ProsePermission permission = new ProsePermission("registerListener");

  // mapping JoinPointRequest -> List(JoinPointListener)
  protected final Map req2listener;

  // mapping JoinPointListener -> List(JoinPointRequest)
  protected final Map listener2req;

  // List(ClassLoadListeners)
  protected final Set classLoadListeners;

  // indicates if this JoinPointManager is connected to the Hook or if it is the test JPM
  protected boolean isConnected;

  // indicates if the table listener2req is maintained for Reverse Mapping.
  protected boolean enableRevMap;


  protected final  HashSet enabledJoinPoints = new HashSet();

  private JVMAspectInterface aspectInterface = null;
  private Vector loadedClasses;
  private ClassLoadConsumer classLoaderNotifier;
  /**
   * Create an <code>JoinPointManagerImpl</code> with no registered listeners
   * or events.
   */
  public JoinPointManager(boolean isConnected, JVMAspectInterface ai, boolean enableRevMap) {
    req2listener = new HashMap();
    listener2req = new HashMap();
    classLoadListeners = new HashSet();
    this.isConnected = isConnected;
    aspectInterface = ai;
    this.enableRevMap = enableRevMap;
    loadedClasses = new Vector();

    if (isConnected)
      connectToJVMAI();
  }

  public static class  ClassLoadConsumer extends Thread {
    public boolean isRunning = true;
    JoinPointManager jpm;
    public ClassLoadConsumer(Vector classVector, JoinPointManager j) {
      jpm=j;
    }

    {
      Class c = NoClassDefFoundError.class;
    }
    public void run() {
      while (isRunning) {
        if (!jpm.loadedClasses.isEmpty()) {
          Class c=(Class)jpm.loadedClasses.get(0);
          try { c.getDeclaredMethods(); }
          catch (NoClassDefFoundError e) {
            c= null;
          }

          synchronized(jpm.loadedClasses) {
            if (!jpm.loadedClasses.isEmpty())
              jpm.loadedClasses.remove(0);


          }
          if (c!=null)jpm.notifyClassLoadListeners(c);
        }
        else {
          synchronized(jpm.loadedClasses) {
            try {jpm.loadedClasses.wait();} catch (java.lang.InterruptedException e){};
          }
        }
      }

    }
  }

  public void connectToJVMAI() {
    if (aspectInterface == null)
      throw new RuntimeException("Cannot have a NULL aspect interface");
    loadedClasses = new Vector();
    classLoaderNotifier = new ClassLoadConsumer(loadedClasses,this);
    classLoaderNotifier.start();
    aspectInterface.setJoinPointHook(this);
  }

  public void disconnectFromJVMAI() {
    if (aspectInterface == null)
      return;
    if (isConnected) {
      aspectInterface.setJoinPointHook(null);
      classLoaderNotifier.isRunning=false;
      synchronized(loadedClasses) {
        loadedClasses.notifyAll();
      }
    }

    // this should be part of a teardown procedure
    listener2req.clear();
    req2listener.clear();
  }

  public JVMAspectInterface getAspectInterface() {
    return aspectInterface;
  }


  public boolean isReverseMappingEnabled() {
    return enableRevMap;
  }

  public void onFieldAccess(FieldAccessJoinPoint joinPoint) {
    List caughtExceptions = null;
    ListenerList listeners = (ListenerList)joinPoint.getAopTag();
    for (int i = 0; i < listeners.nrOfListeners; i++) {
      try {
        listeners.listenerArray[i].joinPointReached(joinPoint);
      }
      catch (Exception e) {
        if (caughtExceptions == null)
          caughtExceptions = new Vector();
        caughtExceptions.add(handleRuntimeException(e));
      }
    }

    handleExceptions(caughtExceptions,joinPoint);
  }

  public void onFieldModification(FieldModificationJoinPoint joinPoint) {
    List caughtExceptions = null;
    ListenerList listeners = (ListenerList)joinPoint.getAopTag();
    for (int i = 0; i < listeners.nrOfListeners; i++) {
      try {
        listeners.listenerArray[i].joinPointReached(joinPoint);
      }
      catch (Exception e) {
        if (caughtExceptions == null)
          caughtExceptions = new Vector();
        caughtExceptions.add(handleRuntimeException(e));
      }
    }

    handleExceptions(caughtExceptions,joinPoint);

  }

  public void onMethodEntry(MethodEntryJoinPoint joinPoint) {
    List caughtExceptions = null;
    ListenerList listeners = (ListenerList)joinPoint.getAopTag();
    for (int i = 0; i < listeners.nrOfListeners; i++) {
      try {
        listeners.listenerArray[i].joinPointReached(joinPoint);
      }
      catch (Exception e) {
        if (caughtExceptions == null)
          caughtExceptions = new Vector();
        caughtExceptions.add(handleRuntimeException(e));
      }
    }

    handleExceptions(caughtExceptions,joinPoint);
  }

  public void onMethodExit(MethodExitJoinPoint joinPoint) {
    List caughtExceptions = null;
    ListenerList listeners = (ListenerList)joinPoint.getAopTag();
    for (int i = 0; i < listeners.nrOfListeners; i++) {
      try {
        listeners.listenerArray[i].joinPointReached(joinPoint);
      }
      catch (Exception e) {
        if (caughtExceptions == null)
          caughtExceptions = new Vector();
        caughtExceptions.add(handleRuntimeException(e));
      }
    }

    handleExceptions(caughtExceptions,joinPoint);
  }

  /**
   * Hook method to be called whenever a registered constructor is invoked.
   */
  public void onConstructor(ConstructorJoinPoint joinPoint) {
    List caughtExceptions = null;
    ListenerList listeners = (ListenerList)joinPoint.getAopTag();
    for (int i = 0; i < listeners.nrOfListeners; i++) {
      try {
        listeners.listenerArray[i].joinPointReached(joinPoint);
      }
      catch (Exception e) {
        if (caughtExceptions == null)
          caughtExceptions = new Vector();
        caughtExceptions.add(handleRuntimeException(e));
      }
    }

    handleExceptions(caughtExceptions,joinPoint);
  }

  /**
   * Hook method to be called whenever an Exception event is reported from
   * the jvmai-interface.
   * For every listener registered on an exception throw event it invoces the
   * corresponding <code>joinPointReached</code>-method.
   */
  public void onExceptionThrow(ExceptionJoinPoint joinPoint) {
    //System.out.println("JoinPointManager -> onExceptionThrow");
    List caughtExceptions = null;
    ListenerList listeners = (ListenerList)joinPoint.getAopTag();
    //System.out.println("JoinPointManager onExceptionThrow -> listeners.nrOfListeners = " + listeners.nrOfListeners);
    //System.out.println("JoinPointManager onExceptionThrow -> listeners.toString = " + listeners.toString());

    for (int i = 0; i < listeners.nrOfListeners; i++) {
      try {
        listeners.listenerArray[i].joinPointReached(joinPoint);
      }
      catch (Exception e) {
        if (caughtExceptions == null)
          caughtExceptions = new Vector();
        caughtExceptions.add(handleRuntimeException(e));
      }
    }

    handleExceptions(caughtExceptions,joinPoint);
  }


  /**
   * Hook method to be called whenever an Exception Catch event is reported from
   * the jvmai-interface.
   * For every listener registered on an exception catch event it invoces the
   * corresponding <code>joinPointReached</code>-method.
   */
  public void onExceptionCatch(ExceptionCatchJoinPoint joinPoint) {
    //System.out.println("JoinPointManager -> onExceptionCatch");
    List caughtExceptions = null;
    ListenerList listeners = (ListenerList)joinPoint.getAopTag();

    //System.out.println("JoinPointManager onExceptionCatch -> listeners.nrOfListeners = " + listeners.nrOfListeners);
    //System.out.println("JoinPointManager onExceptionCatch -> listeners.toString = " + listeners.toString());

    for (int i = 0; i < listeners.nrOfListeners; i++) {
      try {
        listeners.listenerArray[i].joinPointReached(joinPoint);
      }
      catch (Exception e) {
        if (caughtExceptions == null)
          caughtExceptions = new Vector();
        caughtExceptions.add(handleRuntimeException(e));
      }
    }
    handleExceptions(caughtExceptions,joinPoint);
  }


  public void onClassLoad(Class cls) {
    // Supress notification for classes which are already loaded.
    // Bugfix for multiple remote insertion of the same aspect.
    //
    // Only required if aspects are inserted remotely
    // TODO: disable this if property 'prose.port' is not set.
    try {
      // look if cls is already in BCEL repository
      if(aspectInterface instanceof SunBCELRepositoryInterface &&
          ((SunBCELRepositoryInterface) aspectInterface).isClassLoaded(cls.getName())) {
        return;
      }
    } catch(Exception e) { System.err.println("###" + e); e.printStackTrace(); }


    // BIG FIXME: this is because of an error with versions prior to
    // 1.4!. In addition, at this time (for whatever reason)
    // I cannot check whether cls is assignable from ..

    synchronized(loadedClasses) {
      loadedClasses.add(cls);
      loadedClasses.notifyAll();
    }
  }


  /** Register the listener <code>lsnr</code> for events
   * requested by <code>jpr</code>. If the request <code>jpr</code>
   * is the first of its kind, the join corresponding join point
   * will be enabled. More precise, if no other request with an equal
   * signature has been previously registered, the join point
   * will be enabled.
   *
   * This method checks for the permission ProsePermission "registerListener" to be present.
   */
  public void registerListener(JoinPointListener list, JoinPointRequest req) {
    // security check
    //AccessController.checkPermission(permission); // FIXME

    //System.out.println("JoinPointManager - JoinPointListener(list) = " + list);
    //System.out.println("JoinPointManager - JoinPointRequest(req) = " + req);

    ListenerList listeners = null;
    synchronized(req2listener) {
      listeners = (ListenerList)req2listener.get(req);
      if (listeners == null) {
        listeners = new ListenerList();
        req2listener.put(req,listeners);
      }
      if (isConnected)
        req.enableJoinPoint(listeners);
    }


    // note the trick: add adds the element at the end of the list.
    // therefore, during notification, it will be notified last.
    // therefore, the registration-notification ordering constraint
    // holds..yeyeyeye
    listeners.add(list);
    if (enableRevMap) {
      Set joinpoints = null;
      synchronized(listener2req) {
        joinpoints = (Set)listener2req.get(list);
        if (joinpoints == null) {
          joinpoints = new HashSet();
          listener2req.put(list, joinpoints);
        }
      }
      joinpoints.add(req);
    }
  }

  /** Deregister listener <code>lsnr</code> from this JoinPointManager.
   * As a result of this action, the listener <code>lsnr</code> will
   * cease being notified of any kind of events. Upon this action,
   * any 'lonely event' -- one for whom no listener exists will be
   * disabled. More precisely, if <code>lsnr</code> was the last listener
   * listening on events with the signature <em>S</em>,
   * <em>S.disableJoinPoint</em> will be performed.
   */
  public void unregisterListener(JoinPointListener listener) {
    //iterate through our vectors...

    try {
      if (listener instanceof Insertable)
        ((Insertable)listener).withdrawalAction(true);
    }
    catch (Throwable e) {
      // this must be ignored. A malicious crosscut may
      // want to remain forever inserted. In fact,
      // the unregistration should be robust.
      e.printStackTrace();
    }

    Collection toRemoveKey = new Vector();
    synchronized(req2listener) {
      Iterator i = req2listener.keySet().iterator();
      while(i.hasNext()) {
        JoinPointRequest crtRequest = (JoinPointRequest)i.next();
        ListenerList crtListenerList  = (ListenerList)req2listener.get(crtRequest);

        crtListenerList.remove(listener);
        if (crtListenerList.contains(listener)) {
          throw new Error("Vector.removeAll works improperly; please reimplement");
        }
        if (crtListenerList.isEmpty()) {
          if (isConnected)
            crtRequest.disableJoinPoint();
          toRemoveKey.add(crtRequest);
        }
      }
      Iterator oldKeys = toRemoveKey.iterator();
      while(oldKeys.hasNext()) {
        Object key = oldKeys.next();
        req2listener.remove(key);
      }

    }

    if (enableRevMap)
      listener2req.remove(listener);


    try {
      if (listener instanceof Insertable)
        ((Insertable)listener).withdrawalAction(true);
    }
    catch (Throwable e) {
      // this must be ignored. A malicious crosscut may
      // want to remain forever inserted. In fact,
      // the unregistration should be robust.
      e.printStackTrace();
    }
  }

 
  /**
   * Return the state of the join-point manager. The registered listeners and
   * the associated requests will be returned.
   */
  public String toString() {
    String isConnectedString = "(isNotConnected)";
    if (isConnected) isConnectedString = "(isConnected)";
    StringBuffer result  = new StringBuffer();
    result.append(" Join Point Manager (" + System.identityHashCode(this) + "State " + isConnectedString + ": \n");
    result.append(req2listener.toString());
    return result.toString();
  }


  /**
   * This method should be called if a notification fails. If the notification
   * fails with a Runtime exception, the default implementation of this
   * method throws the corresponding run-time exception. Otherwise it
   * returns the exception that caused the failure.
   *
   * @param e the exception to be handled
   * @return a non-runtime exception thrown by the advice
   */
  protected Exception handleRuntimeException(Exception e) {
    // the obvious case

    if (e!= null && e instanceof RuntimeException) {
      throw (RuntimeException) e;
    }

    // invoked via reflection
    if (e instanceof InvocationTargetException) {
      InvocationTargetException invokedViaReflection = (InvocationTargetException)e;
      if (invokedViaReflection.getTargetException() != null &&
          invokedViaReflection.getTargetException() instanceof RuntimeException)
        throw (RuntimeException)(invokedViaReflection.getTargetException());
      else {
        Exception realException = (Exception)invokedViaReflection.getTargetException();
        if (realException != null)
          return realException;
      }
    }

    return e;
  }


  /**
   * This method should be called if the notification fails for some of the
   * listeners. Subclasses are encouraged to override this method in order to
   * implement their own exception handling policy, e.g., removing the <em>bad</em>
   * listeners from this <code>JoinPointManager</code>.
   *
   * @param exceptionList a list of <code>Exception</code> objects, thrown during the notification
   * of listeners of <code>crtEvent</code>.
   * @param crtEvent a <code>JoinPointEvent</code> whose listeners were not properly
   * notified.
   */
  protected void handleExceptions(List exceptionList,JoinPoint crtEvent) {

    if (exceptionList != null) {
      Iterator it = exceptionList.iterator();
      while (it.hasNext()) {
        Logger.error("Notification failed for " + crtEvent, (Exception) it.next());
      }
    }
  }

  /**
   * Notify all registered class load listeners that the class
   * <code>newClass</code> has just been loaded and prepared.
   * This method should be invoked by implementations of this abstract
   * immediately after a class of intereest (containing potential join-points)
   * is loaded and prepared.
   *
   * @param newClass the class that is new to the system
   */
  protected void notifyClassLoadListeners(Class newClass) {
    synchronized(classLoadListeners) {
      Iterator i=classLoadListeners.iterator();
      while (i.hasNext()) {
        ClassLoadListener crtListener  = (ClassLoadListener)i.next();
        crtListener.classLoaded(newClass);
      }
    }
  }

  /**
   * Register a class load listener event. All class load listeners
   * will be notified every time a class of interest (containing potential join-points)
   * has been loaded.
   *
   * @param updateGuy the listener of class load events
   */
  public void registerListener(ClassLoadListener updateGuy) {
    classLoadListeners.add(updateGuy);
  }

  /**
   * Unregister a class load listener.
   *
   * @param updateGuy the listener to be removed from the listeners list.
   */
  public void unregisterListener(ClassLoadListener updateGuy) {
    classLoadListeners.remove(updateGuy);
  }

  /**
   * Suspend notification of all listeners interested in join-points
   * that occur in the specified thread. This method is idempotent.
   * Successive calls to this method with the same argument have
   * the same effect as calling it only once.
   */
  public void suspendListenerNotification(Thread t) {
    //        if (isConnected && aspectInterface != null)
    aspectInterface.suspendNotification(t);
  }

  /**
   * Resume the notification of listeners interested in join-points
   * that occur in the specified thread.
   */
  public void resumeListenerNotification(Thread t) {
    //        if (isConnected && aspectInterface != null)
    aspectInterface.resumeNotification(t);
  }


  /**
   * Returns all currently loaded classes in the virtual machine.
   */
  public List getLoadedClasses() {
    List result = aspectInterface.getLoadedClasses();
    return result;
  }


  public Set allJoinpoints() {
    return req2listener.keySet();
  }


  public Set getCrosscuts(JoinPointRequest jpr) {
    Set result = new HashSet();
    if (jpr == null) return result;

    ListenerList listeners = (ListenerList)req2listener.get(jpr);
    if (listeners == null)
      return result;
    else
      for (int i=0; i < listeners.nrOfListeners; i++)
        result.add(listeners.listenerArray[i]);

    return result;
  }


  public JoinPointRequest createJoinPointRequest(String kind,Object o) {
    if (MethodEntryJoinPoint.KIND.equals(kind))
      return new MethodEntryRequest((Method)o, this);
    if (MethodExitJoinPoint.KIND.equals(kind))
      return new MethodExitRequest((Method)o,this);
    if (MethodRedefineJoinPoint.KIND.equals(kind))
      return new MethodRedefineRequest((Method) o, this);
    if (FieldAccessJoinPoint.KIND.equals(kind))
      return new FieldAccessRequest((Field)o, this);
    if (FieldModificationJoinPoint.KIND.equals(kind))
      return new FieldModificationRequest((Field)o, this);
    if (ExceptionJoinPoint.KIND.equals(kind))
      return new ExceptionThrowRequest((Class)o,this);
    if (ExceptionCatchJoinPoint.KIND.equals(kind))
      return new ExceptionCatchRequest((Class)o,this);
    if (ConstructorJoinPoint.KIND.equals(kind))
      return new ConstructorRequest((Constructor)o, this);
    throw new RuntimeException("unknown kind");
  }

  public Set getJoinpoints(Crosscut cc) {
    if (cc == null) return (Set)null;

    if (enableRevMap) {
      Set result = (Set)listener2req.get(cc);
      if (result == null)
        return  new HashSet();
      else
        return result;
    }
    else {
      return new HashSet(cc.createRequest());
    }
  }

}
TOP

Related Classes of ch.ethz.prose.engine.JoinPointManager$ClassLoadConsumer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.