/*
* 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.interceptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Category;
import alt.jiapi.InstrumentationContext;
import alt.jiapi.InstrumentationDescriptor;
import alt.jiapi.reflect.Signature;
import alt.jiapi.event.EventProducer;
/**
* InvocationInterceptor intercepts method invocations and
* delegates invocations to InvokeHandler.<p>
* Following code snippet is an example, how to initialize
* InvocationInterceptor:
*
* <pre>
* InstrumentationContext ctx = new InstrumentationContext();
* InstrumentationDescriptor id = new InstrumentationDescriptor();
* id.addInclusionRule("samples.*");
* ctx.addInstrumentationDescriptor(id);
*
* InvocationInterceptor ii = new InvocationInterceptor(id, "java*",this);
* </pre>
*
* @author Mika Riekkinen
* @version $Revision: 1.9 $ $Date: 2004/04/11 14:22:58 $
*/
public class InvocationInterceptor extends EventProducer {
private InvocationHandler handler;
private HashMap rmCache = new HashMap();
/**
* Constructor. Resolution is set to '*'.
*
* @param id Instrumentation decsriptor, that this InvocationInterceptor
* registers itself to.
* @param handler InvocationHandler
*/
public InvocationInterceptor(InstrumentationDescriptor id, InvocationHandler handler) {
this(id, "*", handler);
}
/**
* Creates new InvocationInterceptor. Resolution tells, which
* methods are to be intercepted.
*
* @param id Instrumentation decsriptor, that this InvocationInterceptor
* registers itself to.
* @param resolution Resolution, that is used to select which
* methods will be intercepted.
* @param handler InvocationHandler
*/
public InvocationInterceptor(InstrumentationDescriptor id,
String resolution, InvocationHandler handler){
this(id, new String[] { resolution }, handler);
}
/**
* Creates new InvocationInterceptor.
*
* @param id Instrumentation decsriptor, that this InvocationInterceptor
* registers itself to.
* @param resolutions Resolutions, that is used further to select which
* methods will trigger events to be produced.
* @param handler InvocationHandler
*/
public InvocationInterceptor(InstrumentationDescriptor id,
String[] resolutions,
InvocationHandler handler) {
super(resolutions);
this.handler = handler;
id.addInstrumentor(new InvocationInstrumentor(this, handler));
}
/**
* Called by Jiapi runtime.
* @param o if invocation is being made to an static method,
* this parameter holds a Class of the called method,
* otherwise this is the instance, that invocation should be
* acted on.
* @param name of the method to call
* @param args Arguments of the method
*/
public Object invokeMethod(Object o, String name, Object[] args,
String signature) throws Throwable {
try {
Method m = null;
Class c = null;
if (o instanceof Class) { // invokestatic
c = (Class)o;
}
else { // invokevirtual
c = o.getClass();
}
//System.out.println("Signature: " + signature);
//long l1 = System.currentTimeMillis();
m = getReflectionMethod(c, name, args, signature);
//long l2 = System.currentTimeMillis();
//System.out.println((l2-l1));
Object r = handler.invoke(o, m, args);
return r;
}
catch(java.lang.reflect.InvocationTargetException ite) {
throw ite.getTargetException();
}
catch(Throwable t) {
t.printStackTrace();
throw t;
}
}
private Method getReflectionMethod(Class c, String name, Object[] args,
String signature) {
Method m = null;
// NOTE: cache should be configurable. It can potentially
// consume a lot of memory
m = (Method)rmCache.get(signature);
if (m != null) {
return m;
}
String sign = signature.substring(signature.lastIndexOf('('));
Signature s = new Signature(sign);
String[] paramTypes = s.getParameters();
try {
Class[] params = params = new Class[args.length];
for (int i = 0; i < args.length; i++) {
params[i] = args[i].getClass();
// BUG: Following code segment makes a false
// assumption, that java.lang.Integer etc. allways
// represents a primitive type
if ("int".equals(paramTypes[i])) {
params[i] = Integer.TYPE;
}
else if ("long".equals(paramTypes[i])) {
params[i] = Long.TYPE;
}
else if ("char".equals(paramTypes[i])) {
params[i] = Character.TYPE;
}
else if ("boolean".equals(paramTypes[i])) {
params[i] = Boolean.TYPE;
}
else if ("byte".equals(paramTypes[i])) {
params[i] = Byte.TYPE;
}
else if ("float".equals(paramTypes[i])) {
params[i] = Float.TYPE;
}
else if ("double".equals(paramTypes[i])) {
params[i] = Double.TYPE;
}
}
m = c.getMethod(name, params);
}
catch(Exception e) {
e.printStackTrace();
}
rmCache.put(signature, m);
return m;
}
}