//
// 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 Developer of the Original Code is Andrei Popovici. Portions
// created by Andrei Popovici are Copyright (C) 2002 Andrei Popovici.
// All Rights Reserved.
//
// Contributor(s):
// $Id: LocalAspectManager.java,v 1.4 2008/11/18 11:44:47 anicoara Exp $
// =====================================================================
//
package ch.ethz.prose;
// used packages
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.HashMap;
import ch.ethz.jvmai.JVMAspectInterface;
import ch.ethz.prose.crosscut.Crosscut;
import ch.ethz.prose.crosscut.CrosscutRequest;
import ch.ethz.prose.crosscut.CrosscutGroup;
import ch.ethz.prose.engine.ClassLoadListener;
import ch.ethz.prose.engine.JoinPointManager;
import ch.ethz.prose.engine.JoinPointRequest;
import ch.ethz.inf.util.Logger;
/**
* Class LocalAspectManager extends AspectManager and implements a local
* extension manager. Upon creation, a <code>LocalAspectManager</code> registers
* itself as a listener of class load events into the current JoinPointManager.
*
* @version $Revision: 1.4 $
* @author Andrei Popovici
*/
public class LocalAspectManager implements AspectManager, ClassLoadListener {
static int ABORT = 0x1;
static int COMMIT = 0x2;
private HashMap txMap = new HashMap();
private Set theExtensions;
private boolean isConnectedToVM;
private boolean isStarted;
protected JoinPointManager jpm;
/**
* Create a new <code>LocalAspectManager</code>
* with no aspects inserted and create a <code>JoinPointManager</code>.
* register this extension manager as a class load listener
* of the current JoinPointManager.
*/
protected LocalAspectManager(boolean isConnected, JVMAspectInterface ai) {
isConnectedToVM = isConnected;
isStarted = false;
theExtensions = Collections.synchronizedSet(new HashSet());
createJoinPointManager(isConnectedToVM, ai);
jpm.registerListener(this);
}
/**
* This Method is going to be overwritten from a subclass of <code>LocalAspectManager</code>
* that wants to register its own <code>JoinPointManager</code>.
*/
protected void createJoinPointManager(boolean isConnected, JVMAspectInterface ai) {
jpm = new JoinPointManager(isConnected, ai, true);
}
/**
* This method has to be called before an <code>AspectManager</code> is used.
*/
public synchronized void startup() {
isStarted = true;
// do nothing
}
/**
* This method has to be called before an <code>AspectManager</code> is destroyed.
*/
public synchronized void teardown() {
if (!isStarted)
return;
withdrawAll();
if (jpm != null) {
jpm.disconnectFromJVMAI();
jpm = null;
}
isStarted=false;
}
{ Class c=Throwable.class;}
/** This method is called every time a new class has been successfully
* loaded and prepared into the system. It implements the functionality
* needed to update the extensions currently inserted into this extension
* to the current state of the system. For example, if a new class is loaded
* that contains join-points cross-cut by an existing crosscut E.C, the
* crosscut in question will be added as a listener of the JoinPointManager.
*/
public void classLoaded(Class newClass) {
// FIXME: exception classes are loaded when an exception is
// thrown. It is better not to stop the throwing of an exception
// with cross-cutting activity. Unfortunatelly, the test 'isAssignableFrom'
// was false for both exception and non-exeception classes. This
// seemed to be the only way around. Unclean, error prone. FIX!!
if (newClass.getName().endsWith("Exception"))
return;
synchronized(theExtensions) {
Iterator i = theExtensions.iterator();
while (i.hasNext()) {
Aspect crtExtension = (Aspect)i.next();
registerCrosscuts(crtExtension,newClass);
}
}
}
static class TransactionGroup {
CrosscutGroup insertGroup;
CrosscutGroup withdrawGroup;
List toBeInserted;
List toBeWithdrawn;
TransactionGroup() {
insertGroup = new CrosscutGroup();
insertGroup.setExecuteAdvice(false);
withdrawGroup = new CrosscutGroup();
withdrawGroup.setExecuteAdvice(true);
toBeInserted = new Vector();
toBeWithdrawn = new Vector();
}
};
private TransactionGroup getTransactionGroup(Object transactionId) {
TransactionGroup crtGroup = (TransactionGroup)txMap.get(transactionId);
if (crtGroup == null) {
crtGroup = new TransactionGroup();
txMap.put(transactionId,crtGroup);
}
return crtGroup;
}
private void prepareInsertExtension(Aspect ext, Object transactionId) {
// get the transaction group
TransactionGroup crtGroup = getTransactionGroup(transactionId);
//add ext to the list which should be inserted
crtGroup.toBeInserted.add(ext);
// associate all ext crosscuts to this group
Iterator i = ext.getCrosscuts().iterator();
while (i.hasNext()) {
Crosscut crtCrosscut = (Crosscut)i.next();
crtCrosscut.associateToGroup(crtGroup.insertGroup);
}
}
private void prepareWithdrawExtension(Aspect ext, Object transactionId) {
// get the transaction group
TransactionGroup crtGroup = getTransactionGroup(transactionId);
//add ext to the list which should be withdrawn
crtGroup.toBeWithdrawn.add(ext);
// associate all crosscuts to this group
Iterator i = ext.getCrosscuts().iterator();
while (i.hasNext())
{
Crosscut crtCrosscut = (Crosscut)i.next();
crtCrosscut.associateToGroup(crtGroup.withdrawGroup);
}
}
private void finishTransaction(Object transactionId, int commitOrAbort) {
// do withdraw (toBeInserted)
TransactionGroup crtGroup = getTransactionGroup(transactionId);
List extensionsToWithdraw = null;
if (commitOrAbort == COMMIT) {
crtGroup.insertGroup.setExecuteAdvice(true);
crtGroup.withdrawGroup.setExecuteAdvice(false);
extensionsToWithdraw = crtGroup.toBeWithdrawn;
}
if (commitOrAbort == ABORT) {
extensionsToWithdraw = crtGroup.toBeInserted;
}
Iterator i = extensionsToWithdraw.iterator();
while (i.hasNext())
doWithdrawExtension((Aspect)(i.next()));
txMap.remove(transactionId);
}
public void insert(Aspect x,Object txId) {
if (txId == null)
throw new IllegalArgumentException("txId must be non-null");
prepareInsertExtension(x,txId);
doInsertExtension(x);
}
public void withdraw(Aspect x, Object txId) {
if (txId == null || x == null)
throw new IllegalArgumentException("txId must be non-null");
prepareWithdrawExtension(x,txId);
}
public void insert(Aspect x) {
Object txId = new Object();
if (txId == null || x == null)
throw new IllegalArgumentException("txId and x must be non-null");
try {
insert(x,txId);
finishTransaction(txId,COMMIT);
}
catch (RuntimeException e) {
finishTransaction(txId,ABORT);
throw e;
}
}
public void withdraw(Aspect x) {
Object txId = new Object();
if (txId == null || x == null)
throw new IllegalArgumentException("txId must be non-null");
try {
withdraw(x,txId);
finishTransaction(txId,COMMIT);
}
catch (RuntimeException e) {
finishTransaction(txId,ABORT);
throw e;
}
}
public void commit(Object txId) {
finishTransaction(txId,COMMIT);
}
public void abort(Object txId) {
finishTransaction(txId,ABORT);
}
/**
* Insert the extension <code>ext</code> into the extension
* manager. This involves registering the <code>ext</code>'s
* crosscut into the current <code>JoinPointManager</code>.
*/
public synchronized void doInsertExtension(Aspect ext) throws AspectManagerException {
// supress notification
jpm.suspendListenerNotification(Thread.currentThread());
Logger.message("LocalAspectManager(" + isConnectedToVM + ").insertExtension: attempting insert, extension=" + ext);
try {
ext.insertionAction(true);
}
catch (AspectInsertionException extDoesntLikeThisVM) {
Logger.warning("LocalExtgensionManager(" + isConnectedToVM +
").insertExtnesion:failed 'insertAction'",extDoesntLikeThisVM);
throw new AspectManagerException("Aspect ext does not wish to be inserted(" +
extDoesntLikeThisVM.toString()+")");
}
// has extension already been inserted?
if (theExtensions.contains(ext)) {
Logger.message("LocalAspectManager(" + isConnectedToVM +
").insertExtension: failed, ext. already existent, extension=" + ext);
throw new AspectManagerException("Aspect already available");
}
// insert into the JPM all crosscut requests generated by the extension's crosscuts.
// insert Crosscuts
registerCrosscuts(ext,null);
// bookkeeping the inserted extensions
theExtensions.add(ext);
// tell the extension we are finished
try {
ext.insertionAction(false);
}
catch (Exception e) {
Logger.warning("LocalAspectManager(" + isConnectedToVM
+ ").insertExtension: insertAction failed, extension = " + ext);
}
// re-enable notification
jpm.resumeListenerNotification(Thread.currentThread());
Logger.message("LocalAspectManager(" + isConnectedToVM + ").insertExtension: done");
}
private void registerCrosscuts(Aspect ext, Class cls) {
//System.out.println("LocalAspectManager - Aspect = " + ext.toString());
//System.out.println("LocalAspectManager - Class = " + cls);
Iterator i = ext.getCrosscuts().iterator();
while (i.hasNext()) {
Crosscut crtCrosscut = (Crosscut)i.next();
//System.out.println("LocalAspectManager - crtCrosscut = " + crtCrosscut.toString());
if ( (crtCrosscut instanceof Insertable) && cls == null)
((Insertable)crtCrosscut).insertionAction(true);
CrosscutRequest crtRequest = null;
if (cls == null)
// This method is used by first use of this crosscut
crtRequest = crtCrosscut.createRequest();
else
// This method is used upon loading of new classes in the VM
crtRequest = crtCrosscut.createRequest(cls);
Iterator j = crtRequest.iterator();
while (j.hasNext()) {
//System.out.println("LocalAspectManager - crtCrosscut.iterator = " + crtRequest.toString());
JoinPointRequest crtJPR = (JoinPointRequest)(j.next());
jpm.registerListener(crtCrosscut,crtJPR);
}
if ( (crtCrosscut instanceof Insertable) && cls == null)
((Insertable)crtCrosscut).insertionAction(false);
}
}
/**
* Unregister the crosscuts belonging to <code>ext</code>
* from the corresponding <code>JoinPointManager</code>
*/
public synchronized void doWithdrawExtension(Aspect ext) {
Logger.message("LocalAspectManager(" + isConnectedToVM + ").withdrawExtension: withdrawing " + ext);
jpm.suspendListenerNotification(Thread.currentThread());
try {
ext.withdrawalAction(true);
}
catch (Exception e) {
Logger.warning("Aspect does not wish withdrawal",e);
}
Iterator i = ext.getCrosscuts().iterator();
while (i.hasNext()) {
Crosscut crtCrosscut = (Crosscut)i.next();
jpm.unregisterListener(crtCrosscut);
}
theExtensions.remove(ext);
try {
ext.withdrawalAction(false);
}
catch (Exception e) {
Logger.message("extension " + ext + " does not wish withdrawal (end)");
}
jpm.resumeListenerNotification(Thread.currentThread());
}
/**
* Return the list of extensions currently
* inserted into the system.
*/
public List getAllAspects() {
return new Vector(theExtensions);
}
/**
* Return the <code>JoinPointManager</code> of this extension manager.
*/
public JoinPointManager getJoinPointManager() {
return jpm;
}
/**
* Return the boolean status that indicates if this extension manager is connected to the
* VM through its joinpoint manager or if it is the test manager and has no connection to the VM.
*/
public boolean isConnectedToVM() {
return isConnectedToVM;
}
private void withdrawAll() {
if (jpm == null)
return;
jpm.suspendListenerNotification(Thread.currentThread());
if (getAllAspects() == null)
return;
Iterator i = new HashSet(getAllAspects()).iterator();
while (i.hasNext()) {
Aspect e = (Aspect)i.next();
withdraw(e);
}
jpm.resumeListenerNotification(Thread.currentThread());
}
/**
* Remove all inserted extensions from this extension manager, disconnect the joinpoint manager
* from the JVMAI System and deconstruct it.
*/
protected void finalize() {
//withdrawAll();
if (jpm != null) {
jpm.disconnectFromJVMAI();
jpm = null;
}
}
}