/*
* Copyright(C) 2001 Mika Riekkinen, Joni Suominen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or(at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package alt.jiapi.event;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Category;
import alt.jiapi.InstrumentationContext;
import alt.jiapi.InstrumentationDescriptor;
import alt.jiapi.Runtime;
import alt.jiapi.instrumentor.ChainInstrumentor;
import alt.jiapi.instrumentor.InstrumentorChain;
import alt.jiapi.instrumentor.HeadInstrumentor;
import alt.jiapi.instrumentor.Hook;
import alt.jiapi.instrumentor.MethodCallInstrumentor;
import alt.jiapi.instrumentor.MethodDispatcherInstrumentor;
import alt.jiapi.instrumentor.MethodEntryStrategy;
import alt.jiapi.instrumentor.MethodReturnStrategy;
import alt.jiapi.instrumentor.GrepInstrumentor;
/**
* This class registers itself to Jiapi runtime and tracks method
* entries and exits. When either event happens it will notify each
* listeners that has been registered.<p>
*
* If an exception is thrown from a method, the exit event is not produced.
* That is, exit events are produced only for the normal exits from
* methods.
*
* @author Mika Riekkinen
* @author Joni Suominen
* @version $Revision: 1.23 $ $Date: 2004/03/21 12:50:07 $
*/
public class MethodEventProducer extends EventProducer {
// private static Category log =
// Runtime.getLogCategory(MethodEventProducer.class);
private List listeners = new ArrayList();
/**
* Constructor. Resolution is set to '*', which indicates all the
* MethodEvents are produced.
*
* @param id Instrumentation decsriptor, that this MethodEventProducer
* registers itself to.
*/
public MethodEventProducer(InstrumentationDescriptor id) {
this(id, "*");
}
/**
* Creates new MethodEventProducer.
* Only MethodEvents, that match with given resolution,
* are produced. Resolution is compared against the name of the
* method during instrumentation.
*
* @param id Instrumentation decsriptor, that this MethodEventProducer
* registers itself to.
* @param resolution Resolution, that is used further to select which
* methods will trigger events to be produced.
*/
public MethodEventProducer(InstrumentationDescriptor id, String resolution) {
super(resolution);
if (true) {
id.addInstrumentor(new MethodEventInstrumentor(this));
return;
}
try {
// InstrumentationContext ctx = new InstrumentationContext();
// For method entry traps :
ChainInstrumentor entryDispatcher = new MethodDispatcherInstrumentor();
GrepInstrumentor grepEntry =
new GrepInstrumentor(new MethodEntryStrategy());
// Instrumentor head0 = new HeadInstrumentor(); // ---
grepEntry.setResolutions(getResolutions());
ChainInstrumentor entryCall
= new MethodCallInstrumentor(new MethodEntryHook(this));
InstrumentorChain entryChain = new InstrumentorChain();
entryChain.add(entryDispatcher);
entryChain.add(grepEntry);
// entryChain.add(head0); // ---
entryChain.add(entryCall);
// For method exit traps :
ChainInstrumentor exitDispatcher = new MethodDispatcherInstrumentor();
GrepInstrumentor grepExit
= new GrepInstrumentor(new MethodReturnStrategy());
grepExit.setResolutions(getResolutions());
ChainInstrumentor head = new HeadInstrumentor();
ChainInstrumentor exitCall =
new MethodCallInstrumentor(new MethodExitHook(this));
InstrumentorChain exitChain = new InstrumentorChain();
exitChain.add(exitDispatcher);
exitChain.add(grepExit);
exitChain.add(head);
exitChain.add(exitCall);
// Add chains created
id.addInstrumentor(entryChain);
id.addInstrumentor(exitChain);
}
catch (Exception e) {
// NOTE! Fix exception handling.
e.printStackTrace();
}
}
/**
* Adds a MethodListener.
* @param mel a MethodListener
*/
public synchronized void addMethodListener(MethodListener mel) {
listeners.add(mel);
}
/**
* Removes a MethodListener.
* @param mel a MethodListener
*/
public synchronized void removeMethodListener(MethodListener mel) {
listeners.remove(mel);
}
/**
* This method is called by the Jiapi runtime. It should not be called
* by others.
*/
public void methodEntered(Object sourceObject, String methodName) {
if (!isProtected(sourceObject)) {
fireMethodEnterEvent(sourceObject, methodName);
}
else {
// log.debug("sourceObject " + sourceObject +
// " was protected. Not firing JiapiEvent.");
}
}
/**
* This method is called by the Jiapi runtime. It should not be called
* by others.
*/
public void methodExited(Object sourceObject, String methodName) {
if (!isProtected(sourceObject)) {
fireMethodExitEvent(sourceObject, methodName);
}
else {
// log.debug("sourceObject " + sourceObject +
// " was protected. Not firing JiapiEvent.");
}
}
/**
* Fires an event on method entry.
* @param sourceObject Source Object
* @param methodName Name of the method
*/
protected synchronized void fireMethodEnterEvent(Object sourceObject,
String methodName) {
Iterator i = listeners.iterator();
MethodEvent event = new MethodEvent(this, sourceObject, methodName,
MethodEvent.METHOD_ENTERED);
while (i.hasNext()) {
MethodListener ml = (MethodListener)i.next();
ml.methodEntered(event);
}
}
/**
* Fires an event on method exit.
* @param sourceObject Source Object
* @param methodName Name of the method
*/
protected synchronized void fireMethodExitEvent(Object sourceObject,
String methodName) {
Iterator i = listeners.iterator();
MethodEvent event = new MethodEvent(this, sourceObject, methodName,
MethodEvent.METHOD_EXITED);
while (i.hasNext()) {
MethodListener ml = (MethodListener)i.next();
ml.methodExited(event);
}
}
}