/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.vm;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* <p>Title: BaseWrappedClass</p>
* <p>Description: Base class for reflected access class wrappers</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* @version $LastChangedRevision$
* <p><code>org.helios.vm.BaseWrappedClass</code></p>
*/
public abstract class BaseWrappedClass {
/** The attach API delegate VirtualMachine */
protected final Object delegate;
/** Method mapping synchronization lock */
protected final Object synchLock = new Object();
/** The method mapping for this class */
protected Map<String, Method> methods = null;
/** The reflected class methods keyed by the standard method encoding name */
protected static final Map<Class<?>, Map<String, Method>> methodMap = new ConcurrentHashMap<Class<?>, Map<String, Method>>();
/** Thread local to save (and restore) a calling thread's context classloader */
protected static final ThreadLocal<ClassLoader> savedState = new ThreadLocal<ClassLoader>();
/**
* Saves the calling thread's context class loader and replaces it with the VM class loader if it's thread local saved state is null
*/
public static void pushCl() {
if(savedState.get()==null) {
savedState.set(Thread.currentThread().getContextClassLoader());
ClassLoader cl = VirtualMachineBootstrap.attachClassLoader.get();
Thread.currentThread().setContextClassLoader(cl==null ? ClassLoader.getSystemClassLoader() : cl);
//log("Pushed ClassLoader [" + Thread.currentThread().getContextClassLoader() + "]");
}
}
/**
* Restored the calling thread's context class loader if it's thread local saved state is not null.
*/
public static void popCl() {
if(savedState.get()!=null) {
Thread.currentThread().setContextClassLoader(savedState.get());
savedState.remove();
}
}
/**
* Reflective invocation
* @param delegate The target object to invoke against. Ignored if method is static.
* @param type The class of the delegate . Ignored if the actual delegate is passed.
* @param methodEncode The method encode key
* @param args The arguments to pass to the method invocation
* @return The return value of the method invocation
*/
protected static Object invoke(Object delegate, String delegateType, String methodEncode, Object...args) {
if(delegate==null && delegateType==null) throw new IllegalArgumentException("The passed delegate and delegate type was null. One must be provided", new Throwable());
if(methodEncode==null) throw new IllegalArgumentException("The passed methodEncode was null", new Throwable());
Class<?> delegateClass = null;
if(delegate!=null) {
delegateClass = delegate.getClass();
} else {
delegateClass = VirtualMachineBootstrap.getInstance().classCache.get(delegateType);
}
if(delegateClass==null) throw new IllegalArgumentException("Could not determine delegate class", new Throwable());
Map<String, Method> mMap = getMethodMap(delegateClass);
Method m = mMap.get(methodEncode);
if(m==null) throw new IllegalArgumentException("The passed methodEncode [" + methodEncode + "] does not map to a delegate method", new Throwable());
try {
return m.invoke(java.lang.reflect.Modifier.isStatic(m.getModifiers()) ? null : delegate, args);
} catch (Exception e) {
throw new RuntimeException("Failed to invoke [" + (m==null ? methodEncode : m.toGenericString()) + "]", e);
}
}
/**
* Retrieves the method map for the passed class, climbing the type hierarchy if necessary
* @param clazz The class to get the method map for
* @return The method map for the passed class
*/
protected static Map<String, Method> getMethodMap(Class<?> clazz) {
Map<String, Method> mMap = null;
Class<?> target = clazz;
while(!target.equals(Object.class)) {
mMap = methodMap.get(target);
if(mMap!=null) return mMap;
target = target.getSuperclass();
}
throw new IllegalArgumentException("No method map for delegate class [" + clazz.getName() + "]", new Throwable());
}
/**
* Creates a new BaseWrappedClass
* @param delegate The attach API delegate object
*/
public BaseWrappedClass(Object delegate) {
this.delegate = delegate;
if(methods==null) {
synchronized(synchLock) {
if(methods==null) {
methods = getMethodMapping(this.getClass());
}
}
}
}
protected static Map<String, Method> getMethodMapping(Class<?> type) {
if(type==null) throw new IllegalArgumentException("The passed type was null", new Throwable());
Map<String, Method> mMap = methodMap.get(type);
if(mMap==null) {
synchronized(type) {
mMap = methodMap.get(type);
if(mMap==null) {
Method[] methods = type.getDeclaredMethods();
mMap = new HashMap<String, Method>(methods.length);
methodMap.put(type, mMap);
Map<String, Integer> overloads = mapOverloads(methods);
for(Method m: methods) {
m.setAccessible(true);
String name = m.getName();
if(overloads.get(name)==1 || m.getParameterTypes().length==0) {
mMap.put(name, m);
} else {
StringBuilder b = new StringBuilder(name);
for(Class<?> clazz: m.getParameterTypes()) {
b.append(clazz.isPrimitive() ? clazz.getName().charAt(0) : clazz.getSimpleName().charAt(0));
}
mMap.put(b.toString(), m);
}
}
}
}
}
return mMap;
}
public static void log(Object msg) {
System.out.println(msg);
}
/**
* Returns a map of the method names and a count of the number of instances by each name
* @param methods An array of methods
* @return A map of overload counts keyed by method name
*/
protected static Map<String, Integer> mapOverloads(Method[] methods) {
Map<String, Integer> map = new HashMap<String, Integer>(methods.length);
for(Method m: methods) {
Integer i = map.get(m.getName());
if(i==null) {
map.put(m.getName(), 1);
} else {
map.put(m.getName(), i+1);
}
}
return map;
}
}