/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.geronimo.kernel.basic;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.reflect.FastClass;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.gbean.AbstractName;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.kernel.ClassLoading;
import org.apache.geronimo.kernel.GBeanNotFoundException;
import org.apache.geronimo.kernel.Kernel;
import org.apache.geronimo.kernel.proxy.ProxyCreationException;
import org.apache.geronimo.kernel.proxy.ProxyFactory;
import org.apache.geronimo.kernel.proxy.ProxyManager;
/**
* Creates proxies that communicate directly with a Kernel located in the same
* JVM as the client.
*
* @version $Rev:386515 $ $Date: 2006-11-16 23:35:17 -0500 (Thu, 16 Nov 2006) $
*/
public class BasicProxyManager implements ProxyManager {
private final static String MANAGED_BEAN_NAME = "org.apache.geronimo.kernel.proxy.GeronimoManagedBean";
private final static Log log = LogFactory.getLog(BasicProxyManager.class);
private final Kernel kernel;
private final Map interceptors = Collections.synchronizedMap(new BasicProxyMap());
public BasicProxyManager(Kernel kernel) {
this.kernel = kernel;
}
public ProxyFactory createProxyFactory(Class[] types, ClassLoader classLoader) {
if (types == null) throw new NullPointerException("type is null");
if (types.length == 0) throw new IllegalArgumentException("interface list is empty");
if (classLoader == null) throw new NullPointerException("classLoader is null");
Class managedBean = null;
try {
managedBean = classLoader.loadClass(MANAGED_BEAN_NAME);
} catch (ClassNotFoundException e) {
// Can't load GeronimoManagedBean if the incoming type doesn't have a ClassLoader set
log.debug("Unable to add GeronimoManagedBean to proxy (specified class loader does not have class)");
}
if(managedBean != null) {
Class[] adjusted = new Class[types.length+1];
System.arraycopy(types, 0, adjusted, 0, types.length);
adjusted[types.length] = managedBean;
types = adjusted;
}
return new ManagedProxyFactory(types, classLoader);
}
public Object createProxy(AbstractName target, Class type) {
if (target == null) throw new NullPointerException("target is null");
if (type == null) throw new NullPointerException("type is null");
try {
// if the type is visible from the target's classloader use it
// otherwise use the type's classloader
ClassLoader classLoader;
try {
classLoader = kernel.getClassLoaderFor(target);
if (!type.equals(ClassLoading.loadClass(type.getName(), classLoader))) {
classLoader = type.getClassLoader();
}
} catch (Exception ignored) {
classLoader = type.getClassLoader();
}
// add any interface exposed by the gbean that is visible from the selected class loader
List types = getVisibleInterfaces(target, classLoader, true);
if (types == null) types = new ArrayList();
types.add(type);
return createProxyFactory((Class[]) types.toArray(new Class[types.size()]), classLoader).createProxy(target);
} catch (GBeanNotFoundException e) {
throw new IllegalArgumentException("Could not get GBeanInfo for target object: " + target);
}
}
public Object createProxy(AbstractName target, ClassLoader classLoader) {
if (target == null) throw new NullPointerException("target is null");
if (classLoader == null) throw new NullPointerException("classLoader is null");
try {
List types = getVisibleInterfaces(target, classLoader, true);
if (types == null) return null;
return createProxyFactory((Class[]) types.toArray(new Class[types.size()]), classLoader).createProxy(target);
} catch (GBeanNotFoundException e) {
throw new IllegalArgumentException("Could not get GBeanInfo for target object: " + target);
}
}
private List getVisibleInterfaces(AbstractName target, ClassLoader classLoader, boolean shouldLog) throws GBeanNotFoundException {
GBeanInfo info = kernel.getGBeanInfo(target);
Set interfaces = info.getInterfaces();
if(interfaces.size() == 0) {
if (shouldLog) {
log.warn("No interfaces found for " + target + " ("+target+")");
}
return null;
}
String[] names = (String[]) interfaces.toArray(new String[0]);
List types = new ArrayList();
for (int i = 0; i < names.length; i++) {
try {
Class type = classLoader.loadClass(names[i]);
if (type.isInterface()) {
types.add(type);
}
} catch (ClassNotFoundException e) {
if (shouldLog) {
log.warn("Could not load interface "+names[i]+" in provided ClassLoader for "+target);
}
}
}
return types;
}
public void destroyProxy(Object proxy) {
if (proxy == null) {
return;
}
MethodInterceptor methodInterceptor = (MethodInterceptor) interceptors.remove(proxy);
if (methodInterceptor != null) {
doDestroy(methodInterceptor);
}
}
public boolean isProxy(Object proxy) {
return interceptors.containsKey(proxy);
}
public AbstractName getProxyTarget(Object proxy) {
MethodInterceptor methodInterceptor = (MethodInterceptor) interceptors.get(proxy);
if (methodInterceptor == null) {
return null;
}
return getAbstractName(methodInterceptor);
}
private class ManagedProxyFactory implements ProxyFactory {
private final Class proxyType;
private final FastClass fastClass;
public ManagedProxyFactory(Class type, ClassLoader classLoader) {
this(new Class[]{type}, classLoader);
}
public ManagedProxyFactory(Class[] type, ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
if(type.length > 1) { // shrink first -- may reduce from many to one
type = ClassLoading.reduceInterfaces(type);
}
if(type.length == 0) {
throw new IllegalArgumentException("Cannot generate proxy for 0 interfaces!");
} else if(type.length == 1) { // Unlikely (as a result of GeronimoManagedBean)
enhancer.setSuperclass(type[0]);
} else {
if(type[0].isInterface()) {
enhancer.setSuperclass(Object.class);
enhancer.setInterfaces(type);
} else { // there's a class and reduceInterfaces put the class in the first spot
Class[] intfs = new Class[type.length-1];
System.arraycopy(type, 1, intfs, 0, intfs.length);
enhancer.setSuperclass(type[0]);
enhancer.setInterfaces(intfs);
}
}
enhancer.setClassLoader(classLoader);
enhancer.setCallbackType(MethodInterceptor.class);
enhancer.setUseFactory(false);
proxyType = enhancer.createClass();
fastClass = FastClass.create(proxyType);
}
public Object createProxy(AbstractName target) {
assert target != null: "target is null";
Callback callback = getMethodInterceptor(proxyType, kernel, target);
Enhancer.registerCallbacks(proxyType, new Callback[]{callback});
try {
Object proxy = fastClass.newInstance();
interceptors.put(proxy, callback);
return proxy;
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else if (cause != null) {
throw new ProxyCreationException(cause);
} else {
throw new ProxyCreationException(e);
}
}
}
}
protected Callback getMethodInterceptor(Class proxyType, Kernel kernel, AbstractName target) {
return new ProxyMethodInterceptor(proxyType, kernel, target);
}
protected void doDestroy(MethodInterceptor methodInterceptor) {
((ProxyMethodInterceptor)methodInterceptor).destroy();
}
protected AbstractName getAbstractName(MethodInterceptor methodInterceptor) {
return ((ProxyMethodInterceptor)methodInterceptor).getAbstractName();
}
}