blob: 48b91f2e57413db0c66c4151e2a5b7e1bc47fbff [file] [log] [blame]
/*
* 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.aries.proxy.impl;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.Callable;
import org.apache.aries.proxy.InvocationListener;
import org.apache.aries.proxy.ProxyManager;
import org.apache.aries.proxy.UnableToProxyException;
import org.apache.aries.proxy.impl.gen.ProxySubclassGenerator;
import org.apache.aries.proxy.impl.interfaces.InterfaceProxyGenerator;
import org.apache.aries.proxy.weaving.WovenProxy;
import org.osgi.framework.Bundle;
public final class AsmProxyManager extends AbstractProxyManager implements ProxyManager
{
static final ClassLoader bootClassLoader = new ClassLoader(Object.class.getClassLoader()) { /* boot class loader */};
public Object createNewProxy(Bundle clientBundle, Collection<Class<?>> classes,
Callable<Object> dispatcher, InvocationListener listener) throws UnableToProxyException
{
Object proxyObject = null;
// if we just have interfaces and no classes we default to using
// the interface proxy because we can't dynamically
// subclass more than one interface
// unless we have a class
// that implements all of them
// loop through the classes checking if they are java interfaces
// if we find any class that isn't an interface we need to use
// the subclass proxy
Set<Class<?>> notInterfaces = new HashSet<Class<?>>();
Set<Class<?>> interfaces = new HashSet<Class<?>>();
for (Class<?> clazz : classes) {
if (!!!clazz.isInterface()) {
notInterfaces.add(clazz);
} else {
interfaces.add(clazz);
}
}
// if we just have no classes we default to using
// the interface proxy because we can't dynamically
// subclass more than one interface
// unless we have a class
// that implements all of them
if (notInterfaces.isEmpty()) {
proxyObject = InterfaceProxyGenerator.getProxyInstance(clientBundle, null, interfaces, dispatcher, listener);
} else {
// if we need to use the subclass proxy then we need to find
// the most specific class
Class<?> classToProxy = getLowestSubclass(notInterfaces);
if(WovenProxy.class.isAssignableFrom(classToProxy)) {
if(isConcrete(classToProxy) && implementsAll(classToProxy, interfaces)) {
try {
Constructor<?> c = classToProxy.getDeclaredConstructor(Callable.class,
InvocationListener.class);
c.setAccessible(true);
proxyObject = c.newInstance(dispatcher, listener);
} catch (Exception e) {
//We will have to subclass this one, but we should always have a constructor
//to use
//TODO log that performance would be improved by using a non-null template
}
} else {
//We need to generate a class that implements the interfaces (if any) and
//has the classToProxy as a superclass
if((classToProxy.getModifiers() & Modifier.FINAL) != 0) {
throw new UnableToProxyException(classToProxy, "The class " + classToProxy
+ " does not implement all of the interfaces " + interfaces +
" and is final. This means that we cannot create a proxy for both the class and all of the requested interfaces.");
}
proxyObject = InterfaceProxyGenerator.getProxyInstance(clientBundle,
classToProxy, interfaces, dispatcher, listener);
}
}
if(proxyObject == null){
// ARIES-1216 : in some cases, some class can not be visible from the root class classloader.
// If that's the case, we need to build a custom classloader that has visibility on all those classes
// If we could generate a proper constructor this would not be necessary, but since we have to rely
// on the generated serialization constructor to bypass the JVM verifier, we don't have much choice
ClassLoader classLoader = classToProxy.getClassLoader();
if (classLoader == null) {
classLoader = bootClassLoader;
}
boolean allVisible = true;
for (Class<?> clazz : classes) {
try {
if (classLoader.loadClass(clazz.getName()) != clazz) {
throw new UnableToProxyException(classToProxy, "The requested class " + clazz + " is different from the one seen by by " + classToProxy);
}
} catch (ClassNotFoundException e) {
allVisible = false;
break;
}
}
if (!allVisible) {
List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
for (Class<?> clazz : classes) {
ClassLoader cl = clazz.getClassLoader();
if (cl != null && !classLoaders.contains(cl)) {
classLoaders.add(cl);
}
}
classLoader = new MultiClassLoader(classLoaders);
}
proxyObject = ProxySubclassGenerator.newProxySubclassInstance(classToProxy, classLoader, new ProxyHandler(this, dispatcher, listener));
}
}
return proxyObject;
}
private static class MultiClassLoader extends ClassLoader {
private final List<ClassLoader> parents;
private MultiClassLoader(List<ClassLoader> parents) {
this.parents = parents;
}
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
for (ClassLoader cl : parents) {
try {
return cl.loadClass(name);
} catch (ClassNotFoundException e) {
// Ignore
}
}
try {
// use bootClassLoader as last fallback
return bootClassLoader.loadClass(name);
} catch (ClassNotFoundException e) {
// Ignore
}
throw new ClassNotFoundException(name);
}
@Override
public URL getResource(String name) {
for (ClassLoader cl : parents) {
URL url = cl.getResource(name);
if (url != null) {
return url;
}
}
return null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
final List<Enumeration<URL>> tmp = new ArrayList<Enumeration<URL>>();
for (ClassLoader cl : parents) {
tmp.add(cl.getResources(name));
}
return new Enumeration<URL>() {
int index = 0;
@Override
public boolean hasMoreElements() {
return next();
}
@Override
public URL nextElement() {
if (!next()) {
throw new NoSuchElementException();
}
return tmp.get(index).nextElement();
}
private boolean next() {
while (index < tmp.size()) {
if (tmp.get(index) != null && tmp.get(index).hasMoreElements()) {
return true;
}
index++;
}
return false;
}
};
}
}
private Class<?> getLowestSubclass(Set<Class<?>> notInterfaces) throws
UnableToProxyException {
Iterator<Class<?>> it = notInterfaces.iterator();
Class<?> classToProxy = it.next();
while(it.hasNext()) {
Class<?> potential = it.next();
if(classToProxy.isAssignableFrom(potential)) {
//potential can be widened to classToProxy, and is therefore
//a lower subclass
classToProxy = potential;
} else if (!!!potential.isAssignableFrom(classToProxy)){
//classToProxy is not a subclass of potential - This is
//an error, we can't be part of two hierarchies at once!
throw new UnableToProxyException(classToProxy, "The requested classes "
+ classToProxy + " and " + potential + " are not in the same type hierarchy");
}
}
return classToProxy;
}
private boolean isConcrete(Class<?> classToProxy) {
return (classToProxy.getModifiers() & Modifier.ABSTRACT) == 0;
}
private boolean implementsAll(Class<?> classToProxy, Set<Class<?>> interfaces) {
//If we can't widen to one of the interfaces then we need to do some more work
for(Class<?> iface : interfaces) {
if(!!!iface.isAssignableFrom(classToProxy))
return false;
}
return true;
}
@Override
protected boolean isProxyClass(Class<?> clazz)
{
return WovenProxy.class.isAssignableFrom(clazz) || ProxySubclassGenerator.isProxySubclass(clazz) || Proxy.isProxyClass(clazz);
}
@Override
protected InvocationHandler getInvocationHandler(Object proxy)
{
Class<?> type = proxy.getClass();
InvocationHandler ih = null;
if (ProxySubclassGenerator.isProxySubclass(type)) {
ih = ProxySubclassGenerator.getInvocationHandler(proxy);
} else if (Proxy.isProxyClass(type)) {
ih = Proxy.getInvocationHandler(proxy);
}
return ih;
}
}