/* * 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; } }