/* * 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 WARRANTIESOR 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.interfaces; import java.security.AllPermission; import java.security.PermissionCollection; import java.security.Permissions; import java.security.ProtectionDomain; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.aries.proxy.InvocationListener; import org.apache.aries.proxy.UnableToProxyException; import org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter; import org.apache.aries.proxy.weaving.WovenProxy; import org.osgi.framework.Bundle; import org.osgi.framework.wiring.BundleWiring; /** An implementation of ClassLoader that will be used to define our proxy class */ final class ProxyClassLoader extends ClassLoader { private static final ProtectionDomain PROXY_PROTECTION_DOMAIN; static { PermissionCollection pc = new Permissions(); pc.add(new AllPermission()); PROXY_PROTECTION_DOMAIN = new ProtectionDomain(null, pc); } /** A {@link Map} of classes we already know */ private final ConcurrentMap<LinkedHashSet<Class<?>>, String> classes = new ConcurrentHashMap<LinkedHashSet<Class<?>>, String>(); private final ConcurrentMap<String, Class<?>> locatedClasses = new ConcurrentHashMap<String, Class<?>>(); private final Set<Class<?>> ifaces = new HashSet<Class<?>>(); private final ReadWriteLock ifacesLock = new ReentrantReadWriteLock(); public ProxyClassLoader(Bundle bundle) { // super(AriesFrameworkUtil.getClassLoader(bundle)); super(getClassloader(bundle)); } private static ClassLoader getClassloader(Bundle bundle) { if (bundle == null) return ProxyClassLoader.class.getClassLoader(); BundleWiring wiring = bundle != null ? bundle.adapt(BundleWiring.class) : null; return wiring != null ? wiring.getClassLoader() : null; } @Override protected Class<?> findClass(String className) { if(WovenProxy.class.getName().equals(className)) return WovenProxy.class; else if (InvocationListener.class.getName().equals(className)) return InvocationListener.class; else { Class<?> c = locatedClasses.get(className); if(c != null) return c; Lock rLock = ifacesLock.readLock(); rLock.lock(); try { Set<ClassLoader> cls = new HashSet<ClassLoader>(); for(Class<?> iface : ifaces) { if(cls.add(iface.getClassLoader())) { try { c = Class.forName(className, false, iface.getClassLoader()); locatedClasses.put(className, c); return c; } catch (ClassNotFoundException e) { // This is a no-op } } } } finally { rLock.unlock(); } } return null; } /** * Test whether the classloader is invalidated by the set of classes * @return */ public boolean isInvalid(Set<Class<?>> createSet) { for (Class<?> iface : createSet) { try { Class<?> newIFace = Class.forName(iface.getName(), false, this); if (!!!newIFace.equals(iface)) return true; } catch (ClassNotFoundException cnfe) { return true; } } return false; } public Class<?> createProxyClass(Class<?> superclass, SortedSet<Class<?>> interfaces) throws UnableToProxyException { LinkedHashSet<Class<?>> createSet = new LinkedHashSet<Class<?>>(interfaces); //Even a null superclass helps with key uniqueness createSet.add(superclass); String className = classes.get(createSet); if(className != null) { try { return Class.forName(className, false, this); } catch (ClassNotFoundException cnfe) { //This is odd, but we should be able to recreate the class, continue classes.remove(createSet); } } Lock wLock = ifacesLock.writeLock(); wLock.lock(); try { //We want the superclass, but only if it isn't null ifaces.addAll(interfaces); if(superclass != null) ifaces.add(superclass); } finally { wLock.unlock(); } className = "Proxy" + AbstractWovenProxyAdapter.getSanitizedUUIDString(); InterfaceCombiningClassAdapter icca = new InterfaceCombiningClassAdapter( className, this, superclass, interfaces); //Use a special protection domain that grants AllPermission to our Proxy //object. This is important so that we never get in the way of any security //checks. This isn't unsafe because we only add simple dispatch/listener code try { byte[] bytes = icca.generateBytes(); Class<?> c = defineClass(className, bytes, 0, bytes.length, PROXY_PROTECTION_DOMAIN); String old = classes.putIfAbsent(createSet, className); if(old != null) { c = Class.forName(className, false, this); } return c; } catch (ClassFormatError cfe) { throw new UnableToProxyException(createSet.iterator().next(), cfe); } catch (ClassNotFoundException e) { throw new UnableToProxyException(createSet.iterator().next(), e); } } }