Modified: river/jtsk/skunk/qa_refactor/trunk/src/net/jini/loader/ClassLoading.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/net/jini/loader/ClassLoading.java?rev=1593493&r1=1593492&r2=1593493&view=diff ============================================================================== --- river/jtsk/skunk/qa_refactor/trunk/src/net/jini/loader/ClassLoading.java (original) +++ river/jtsk/skunk/qa_refactor/trunk/src/net/jini/loader/ClassLoading.java Fri May 9 07:03:18 2014 @@ -1,637 +1,637 @@ -/* - * 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 net.jini.loader; - -import au.net.zeus.collection.RC; -import au.net.zeus.collection.Ref; -import au.net.zeus.collection.Referrer; -import java.lang.ref.SoftReference; -import java.net.MalformedURLException; -import java.rmi.server.RMIClassLoader; -import java.rmi.server.RMIClassLoaderSpi; -import java.security.AccessController; -import java.security.Guard; -import java.security.PrivilegedAction; -import java.util.Iterator; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.WeakHashMap; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.FutureTask; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; -import net.jini.security.Security; -import org.apache.river.impl.thread.NamedThreadFactory; - -/** - * Provides static methods for loading classes using {@link - * RMIClassLoaderSpi} with optional verification that the codebase URIs - * used to load classes provide content integrity (see {@link - * Security#verifyCodebaseIntegrity - * Security.verifyCodebaseIntegrity}). - * <p> - * Traditionally a class extending {@link RMIClassLoaderSpi} is determined by setting - * the system property "java.rmi.server.RMIClassLoaderSpi", or alternatively, - * {@link RMIClassLoaderSpi} may also be defined by {@link RMIClassLoader} - * using a provider visible to the {@link ClassLoader} returned by - * {@link ClassLoader#getSystemClassLoader} with {@link ServiceLoader}. - * </p><p> - * As explained in River-336 this isn't always practical for IDE's or other - * frameworks. To solve River-336, ClassLoading now uses {@link ServiceLoader} - * to determine a {@link RMIClassLoaderSpi} provider, however unlike - * {@link RMIClassLoader}, by default it uses ClassLoading's {@link ClassLoader#getResources} - * instance to find providers. - * </p><p> - * To define a new RMIClassLoaderSpi for River to utilize, create a file in - * your providers jar file called: - * </p><p> - * META-INF/services/java.rmi.server.RMIClassLoaderSpi - * </p><p> - * This file should contain a single line with the fully qualified name of - * your RMIClassLoaderSpi implementation. - * </p><p> - * ClassLoading will iterate through all RMIClassLoaderSpi implementations found - * until it finds one defined by the system property: - * </p><p> - * System.getProperty("net.jini.loader.ClassLoading.provider"); - * </p><p> - * If this System property is not defined, ClassLoading will load - * <code>net.jini.loader.pref.PreferredClassProvider</code>, alternatively - * <code>java.rmi.server.RMIClassLoader</code> delegates all calls to {@link - * RMIClassLoader}. - * </p><p> - * If a provider is not found, it will not be updated. - * </p><p> - * <h1>History</h1> - * <p>Gregg Wonderly originally reported River-336 and provided a patch - * containing a new CodebaseAccessClassLoader to replace {@link RMIClassLoader}, - * later Sim Isjkes created RiverClassLoader that utilized ServiceLoader. - * Both implementations contained methods identical to {@link RMIClassLoaderSpi}, - * however new implementations were required to extend new provider - * implementations, creating a compatibility issue with existing implementations - * extending {@link RMIClassLoaderSpi}. For backward compatibility with existing - * implementations, {@link RMIClassLoaderSpi} has been retained as the provider, - * avoiding the need to recompile client code. The abilities of both - * implementations, to use ServiceLoader, or to define a provider using a method - * call have been retained, with the restriction that implementations are to be - * obtained via ServiceLoader. - * </p><p> - * Instead, all that is required for utilization of existing service provider - * {@link RMIClassLoaderSpi} implementations is to set the system property - * "net.jini.loader.ClassLoading.provider". - * </p> - * @author Sun Microsystems, Inc. - * @since 2.0 - **/ -public final class ClassLoading { - private final static Logger logger = Logger.getLogger(ClassLoading.class.getName()); - private static volatile RMIClassLoaderSpi provider; - private static final Object lock = new Object(); - private static final Guard permission = new RuntimePermission("setFactory"); - - static { - provider(null, ClassLoading.class.getClassLoader()); - } - - /** - * The current RMIClassLoaderSpi provider in use by ClassLoading. - * - * @return currently installed Provider, may be null. - * @throws SecurityException if caller doesn't have RuntimePermission "getFactory" - */ - public static RMIClassLoaderSpi getProvider(){ - permission.checkGuard(null); - return provider; - } - - private static boolean provider( - final String providerName, - final ClassLoader providerLoader) - { - RMIClassLoaderSpi newProvider = AccessController.doPrivileged( - new PrivilegedAction<RMIClassLoaderSpi>(){ - @Override - public RMIClassLoaderSpi run() { - String name = providerName; - if (name == null){ - name = System.getProperty( - "net.jini.loader.ClassLoading.provider"); - if (name == null) { - name = "net.jini.loader.pref.PreferredClassProvider"; - } else if ("java.rmi.server.RMIClassLoader".equals(name)){ - return null; - } - } - ServiceLoader<RMIClassLoaderSpi> loader - = ServiceLoader.load(RMIClassLoaderSpi.class, providerLoader); - Iterator<RMIClassLoaderSpi> iter = loader.iterator(); - RMIClassLoaderSpi spi; - while ( iter.hasNext() ) { - try { - spi = iter.next(); - if (spi != null) { - if (!name.equals(spi.getClass().getName())) - continue; - logger.log(Level.CONFIG, "loaded: {0}", name); - return spi; - } - } catch (Exception e) { - logger.log( - Level.CONFIG, - "error loading RMIClassLoaderSpi: {0}", - new Object[]{e} - ); - } - } - logger.log(Level.CONFIG, "uable to find {0}" , name); - return null; - } - }); - if (newProvider != null) { - provider = newProvider; - return true; - } else if (providerName == null) { - provider = null; - logger.log(Level.CONFIG, "loaded: java.rmi.server.RMIClassLoader"); - } - return false; - } - - /** - * Installs a new RMIClassLoaderSpi provider with the ClassLoader - * provided. - * - * @param providerName fully defined class name of the provider, if null, - * a new provider instance will be determined by system properties. - * @param providerLoader The class loader to be used to load - * provider-configuration files and provider classes, or null if the - * system class loader (or, failing that, the bootstrap class loader) - * is to be used. - * @return true if successful. - * @throws SecurityException if caller doesn't have RuntimePermission "getFactory" - */ - public static boolean installNewProvider( - String providerName, - ClassLoader providerLoader) - { - permission.checkGuard(null); - synchronized (lock){ - return provider(providerName, providerLoader); - } - } - - /** - * loaderMap contains a list of single threaded ExecutorService's for - * each ClassLoader, used for loading classes and proxies to avoid - * ClassLoader lock contention. An Entry is removed if the ClassLoader - * becomes weakly reachable, or the ExecutorService hasn't been used - * recently. - */ - private static final ConcurrentMap<ClassLoader,ExecutorService> loaderMap - = RC.concurrentMap( - new ConcurrentHashMap<Referrer<ClassLoader>,Referrer<ExecutorService>>(), - Ref.WEAK_IDENTITY, - Ref.TIME, - 10000L, - 10000L - ); - - /** - * per-thread cache (weakly) mapping verifierLoader values to - * (soft) sets of codebase values that have been verified (to - * provide content integrity) with the verifierLoader value - **/ - private static final ThreadLocal perThreadCache = new ThreadLocal() { - protected Object initialValue() { return new WeakHashMap(); } - }; - - /** - * Returns a class loader that loads classes from the given codebase - * RFC3986 compliant URI path. - * - * <p>This method delegates to the - * {@link RMIClassLoaderSpi#getClassLoader(String)} method - * of the provider instance, passing <code>codebase</code> as the argument. - * - * <p>If there is a security manger, its <code>checkPermission</code> - * method will be invoked with a - * <code>RuntimePermission("getClassLoader")</code> permission; - * this could result in a <code>SecurityException</code>. - * The provider implementation of this method may also perform further - * security checks to verify that the calling context has permission to - * connect to all of the URIs in the codebase URI path. - * - * @param codebase the list of URIs (space-separated) from which - * the returned class loader will load classes from, or <code>null</code> - * - * @return a class loader that loads classes from the given codebase URI - * path - * - * @throws MalformedURLException if <code>codebase</code> is - * non-<code>null</code> and contains an non RFC3986 compliant URI, or - * if <code>codebase</code> is <code>null</code> and a provider-specific - * URL used to identify the class loader is invalid - * - * @throws SecurityException if there is a security manager and the - * invocation of its <code>checkPermission</code> method fails, or - * if the caller does not have permission to connect to all of the - * URIs in the codebase URI path - * @since 3.0 - */ - public static ClassLoader getClassLoader(String codebase) - throws MalformedURLException, SecurityException - { - if (provider != null) return provider.getClassLoader(codebase); - return RMIClassLoader.getClassLoader(codebase); - } - - /** - * Returns the annotation string (representing a location for - * the class definition as a single or space delimited list of - * RFC3986 compliant URI) that JERI will use to annotate the class - * descriptor when marshalling objects of the given class. - * - * <p>This method delegates to the - * {@link RMIClassLoaderSpi#getClassAnnotation(Class)} method - * of the provider instance, passing <code>cl</code> as the argument. - * - * @param cl the class to obtain the annotation for - * - * @return a string to be used to annotate the given class when - * it gets marshalled, or <code>null</code> - * - * @throws NullPointerException if <code>cl</code> is <code>null</code> - * @since 3.0 - */ - public static String getClassAnnotation(Class<?> cl) { - if (provider != null) return provider.getClassAnnotation(cl); - return RMIClassLoader.getClassAnnotation(cl); - } - - /** - * Loads a class using {@link - * RMIClassLoaderSpi#loadClass(String,String,ClassLoader)}, - * optionally verifying that the RFC3986 compliant - * codebase URIs provide content integrity. - * - * <p>If <code>verifyCodebaseIntegrity</code> is <code>true</code> - * and <code>codebase</code> is not <code>null</code>, then this - * method invokes {@link Security#verifyCodebaseIntegrity - * Security.verifyCodebaseIntegrity} with <code>codebase</code> as - * the first argument and <code>verifierLoader</code> as the - * second argument (this invocation may be skipped if a previous - * invocation of this method or {@link #loadProxyClass - * loadProxyClass} has already invoked - * <code>Security.verifyCodebaseIntegrity</code> with the same - * value of <code>codebase</code> and the same effective value of - * <code>verifierLoader</code> as arguments without it throwing an - * exception). If <code>Security.verifyCodebaseIntegrity</code> - * throws a <code>SecurityException</code>, then this method - * proceeds as if <code>codebase</code> were <code>null</code>. - * If <code>Security.verifyCodebaseIntegrity</code> throws any - * other exception, then this method throws that exception. - * - * <p>This method then invokes {@link - * RMIClassLoaderSpi#loadClass(String,String,ClassLoader) - * RMIClassLoaderSpi.loadClass} with <code>codebase</code> as the - * first argument (or <code>null</code> if in the previous step - * <code>Security.verifyCodebaseIntegrity</code> was invoked and - * it threw a <code>SecurityException</code>), <code>name</code> - * as the second argument, and <code>defaultLoader</code> as the - * third argument. If <code>RMIClassLoaderSpi.loadClass</code> - * throws a <code>ClassNotFoundException</code>, then this method - * throws a <code>ClassNotFoundException</code>; if - * <code>RMIClassLoaderSpi.loadClass</code> throws any other - * exception, then this method throws that exception; otherwise, - * this method returns the <code>Class</code> returned by - * <code>RMIClassLoaderSpi.loadClass</code>. - * - * @param codebase the list of URLs (separated by spaces) to load - * the class from, or <code>null</code> - * - * @param name the name of the class to load - * - * @param defaultLoader the class loader value (possibly - * <code>null</code>) to pass as the <code>defaultLoader</code> - * argument to <code>RMIClassLoaderSpi.loadClass</code> - * - * @param verifyCodebaseIntegrity if <code>true</code>, verify - * that the RFC3986 compliant codebase URIs provide content integrity - * - * @param verifierLoader the class loader value (possibly - * <code>null</code>) to pass to - * <code>Security.verifyCodebaseIntegrity</code>, if - * <code>verifyCodebaseIntegrity</code> is <code>true</code> - * - * @return the <code>Class</code> object representing the loaded - * class - * - * @throws MalformedURLException if - * <code>Security.verifyCodebaseIntegrity</code> or - * <code>RMIClassLoaderSpi.loadClass</code> throws a - * <code>MalformedURLException</code> - * - * @throws ClassNotFoundException if - * <code>RMIClassLoaderSpi.loadClass</code> throws a - * <code>ClassNotFoundException</code> - * - * @throws NullPointerException if <code>name</code> is - * <code>null</code> - **/ - public static Class<?> loadClass(String codebase, - String name, - ClassLoader defaultLoader, - boolean verifyCodebaseIntegrity, - ClassLoader verifierLoader) - throws MalformedURLException, ClassNotFoundException - { - SecurityException verifyException = null; - if (verifyCodebaseIntegrity && codebase != null) { - try { - verifyIntegrity(codebase, verifierLoader); - } catch (SecurityException e) { - verifyException = e; - codebase = null; - } - } - try { - if (provider != null) - return provider.loadClass(codebase, name, defaultLoader); - return RMIClassLoader.loadClass(codebase, name, defaultLoader); - } catch (ClassNotFoundException e) { - if (verifyException != null) { - // assume that the verify exception is more important - throw new ClassNotFoundException(e.getMessage(), - verifyException); - } else { - e.fillInStackTrace(); - throw e; - } - } - } - - /** - * Loads a dynamic proxy class using {@link - * RMIClassLoaderSpi#loadProxyClass(String,String[],ClassLoader)}, - * optionally verifying that the RFC3986 compliant - * codebase URIs provide content integrity. - * - * <p>If <code>verifyCodebaseIntegrity</code> is <code>true</code> - * and <code>codebase</code> is not <code>null</code>, then this - * method invokes {@link Security#verifyCodebaseIntegrity - * Security.verifyCodebaseIntegrity} with <code>codebase</code> as - * the first argument and <code>verifierLoader</code> as the - * second argument (this invocation may be skipped if a previous - * invocation of this method or {@link #loadClass loadClass} has - * already invoked <code>Security.verifyCodebaseIntegrity</code> - * with the same value of <code>codebase</code> and the same - * effective value of <code>verifierLoader</code> as arguments - * without it throwing an exception). If - * <code>Security.verifyCodebaseIntegrity</code> throws a - * <code>SecurityException</code>, then this method proceeds as if - * <code>codebase</code> were <code>null</code>. If - * <code>Security.verifyCodebaseIntegrity</code> throws any other - * exception, then this method throws that exception. - * - * <p>This method invokes {@link - * RMIClassLoaderSpi#loadProxyClass(String,String[],ClassLoader)} - * with <code>codebase</code> as - * the first argument (or <code>null</code> if in the previous - * step <code>Security.verifyCodebaseIntegrity</code> was invoked - * and it threw a <code>SecurityException</code>), - * <code>interfaceNames</code> as the second argument, and - * <code>defaultLoader</code> as the third argument. If - * <code>RMIClassLoaderSpi.loadProxyClass</code> throws a - * <code>ClassNotFoundException</code>, then this method throws a - * <code>ClassNotFoundException</code>; if - * <code>RMIClassLoaderSpi.loadProxyClass</code> throws any other - * exception, then this method throws that exception; otherwise, - * this method returns the <code>Class</code> returned by - * <code>RMIClassLoaderSpi.loadProxyClass</code>. - * - * @param codebase the list of URLs (separated by spaces) to load - * classes from, or <code>null</code> - * - * @param interfaceNames the names of the interfaces for the proxy - * class to implement - * - * @param defaultLoader the class loader value (possibly - * <code>null</code>) to pass as the <code>defaultLoader</code> - * argument to <code>RMIClassLoader.loadProxyClass</code> - * - * @param verifyCodebaseIntegrity if <code>true</code>, verify - * that the codebase URLs provide content integrity - * - * @param verifierLoader the class loader value (possibly - * <code>null</code>) to pass to - * <code>Security.verifyCodebaseIntegrity</code>, if - * <code>verifyCodebaseIntegrity</code> is <code>true</code> - * - * @return the <code>Class</code> object representing the loaded - * dynamic proxy class - * - * @throws MalformedURLException if - * <code>Security.verifyCodebaseIntegrity</code> or - * <code>RMIClassLoaderSpi.loadProxyClass</code> throws a - * <code>MalformedURLException</code> - * - * @throws ClassNotFoundException if - * <code>RMIClassLoaderSpi.loadProxyClass</code> throws a - * <code>ClassNotFoundException</code> - * - * @throws NullPointerException if <code>interfaceNames</code> is - * <code>null</code> or if any element of - * <code>interfaceNames</code> is <code>null</code> - **/ - public static Class<?> loadProxyClass(String codebase, - String[] interfaceNames, - ClassLoader defaultLoader, - boolean verifyCodebaseIntegrity, - ClassLoader verifierLoader) - throws MalformedURLException, ClassNotFoundException - { - SecurityException verifyException = null; - if (verifyCodebaseIntegrity && codebase != null) { - try { - verifyIntegrity(codebase, verifierLoader); - } catch (SecurityException e) { - verifyException = e; - codebase = null; - } - } - try { - if (provider != null) return - provider.loadProxyClass(codebase, interfaceNames, defaultLoader); - return RMIClassLoader.loadProxyClass(codebase, interfaceNames, - defaultLoader); - } catch (ClassNotFoundException e) { - if (verifyException != null) { - // assume that the verify exception is more important - throw new ClassNotFoundException(e.getMessage(), - verifyException); - } else { - e.fillInStackTrace(); - throw e; - } - } - } - - /** - * Wraps Security.verifyCodebaseIntegrity with caching for - * performance. (Perhaps such caching should be done by - * Security.verifyCodebaseIntegrity instead.) - **/ - private static void verifyIntegrity(String codebase, - ClassLoader verifierLoader) - throws MalformedURLException - { - /* - * Check if we've already verified the same codebase in this - * thread using the same verifierLoader value. - */ - Map verifierLoaderCache = (Map) perThreadCache.get(); - // defend against varying context class loader value of thread - ClassLoader verifierLoaderKey = - (verifierLoader != null ? verifierLoader : - (ClassLoader) AccessController.doPrivileged( - new PrivilegedAction() { - public Object run() { - return Thread.currentThread().getContextClassLoader(); - } - })); - Map verifiedCodebases = - (Map) verifierLoaderCache.get(verifierLoaderKey); - if (verifiedCodebases != null && - verifiedCodebases.containsKey(codebase)) - { - return; - } - - Security.verifyCodebaseIntegrity(codebase, verifierLoader); - - /* - * Remember that we've verified this codebase in this thread - * with the given verifierLoader value. - */ - if (verifiedCodebases == null) { - verifiedCodebases = new WeakHashMap(); - verifierLoaderCache.put(verifierLoaderKey, verifiedCodebases); - } - verifiedCodebases.put(codebase, new SoftReference(codebase)); - return; - } - - /** - * Returns the {@code Class} object associated with the class or - * interface with the given string name, using the given class loader. - * - * This method calls {@link Class#forName(String,boolean,ClassLoader)}, - * from a Thread dedicated for each - * ClassLoader, avoiding contention for ClassLoader locks by thread - * confinement. This provides a significant scalability benefit for - * JERI, without needing to resort to parallel ClassLoader locks, which - * isn't part of the Java specification. - * - * If loader is null, thread confinement is not used. - * - * @param name fully qualified name of the desired class - * @param initialize whether the class must be initialized - * @param loader class loader from which the class must be loaded - * @return class object representing the desired class - * - * @exception LinkageError if the linkage fails - * @exception ExceptionInInitializerError if the initialization provoked - * by this method fails - * @exception ClassNotFoundException if the class cannot be located by - * the specified class loader - * @see Class - * @since 3.0 - */ - public static Class<?> forName(String name, boolean initialize, - ClassLoader loader) - throws ClassNotFoundException - { - if (loader == null) return Class.forName(name, initialize, loader); - // Don't thread confine profiler ClassLoaders. - if (loader.toString().startsWith("javax.management.remote.rmi.RMIConnectionImpl") ) - return Class.forName(name, initialize, loader); - - ExecutorService exec = loaderMap.get(loader); - if (exec == null){ - exec = new ThreadPoolExecutor( - 1, - 1, - 0, - TimeUnit.SECONDS, - new LinkedBlockingQueue(), - new NamedThreadFactory(loader.toString(),true), - new ThreadPoolExecutor.CallerRunsPolicy() - ); - ExecutorService existed = loaderMap.putIfAbsent(loader, exec); - if (existed != null){ - exec = existed; - } - } - FutureTask<Class> future = new FutureTask(new GetClassTask(name, initialize, loader)); - exec.submit(future); - try { - return future.get(); - } catch (InterruptedException e){ - e.fillInStackTrace(); - throw new ClassNotFoundException("Interrupted, Unable to find Class: " + name, e); - } catch (ExecutionException e){ - Throwable t = e.getCause(); - if (t instanceof LinkageError) throw (LinkageError) t; - if (t instanceof ExceptionInInitializerError) - throw (ExceptionInInitializerError) t; - if (t instanceof SecurityException) throw (SecurityException) t; - if (t instanceof ClassNotFoundException ) - throw (ClassNotFoundException) t; - if (t instanceof NullPointerException) throw (NullPointerException) t; - throw new ClassNotFoundException("Unable to find Class:" + name, t); - } - } - - private static class GetClassTask implements Callable<Class> { - private final String name; - private final boolean initialize; - private final ClassLoader loader; - - private GetClassTask(String name, boolean initialize, ClassLoader loader){ - this.name = name; - this.initialize = initialize; - this.loader = loader; - } - - @Override - public Class call() throws ClassNotFoundException { - return Class.forName(name, initialize, loader); - } - - } - - private ClassLoading() { throw new AssertionError(); } -} +/* + * 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 net.jini.loader; + +import au.net.zeus.collection.RC; +import au.net.zeus.collection.Ref; +import au.net.zeus.collection.Referrer; +import java.lang.ref.SoftReference; +import java.net.MalformedURLException; +import java.rmi.server.RMIClassLoader; +import java.rmi.server.RMIClassLoaderSpi; +import java.security.AccessController; +import java.security.Guard; +import java.security.PrivilegedAction; +import java.util.Iterator; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.WeakHashMap; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.FutureTask; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.jini.security.Security; +import org.apache.river.impl.thread.NamedThreadFactory; + +/** + * Provides static methods for loading classes using {@link + * RMIClassLoaderSpi} with optional verification that the codebase URIs + * used to load classes provide content integrity (see {@link + * Security#verifyCodebaseIntegrity + * Security.verifyCodebaseIntegrity}). + * <p> + * Traditionally a class extending {@link RMIClassLoaderSpi} is determined by setting + * the system property "java.rmi.server.RMIClassLoaderSpi", or alternatively, + * {@link RMIClassLoaderSpi} may also be defined by {@link RMIClassLoader} + * using a provider visible to the {@link ClassLoader} returned by + * {@link ClassLoader#getSystemClassLoader} with {@link ServiceLoader}. + * </p><p> + * As explained in River-336 this isn't always practical for IDE's or other + * frameworks. To solve River-336, ClassLoading now uses {@link ServiceLoader} + * to determine a {@link RMIClassLoaderSpi} provider, however unlike + * {@link RMIClassLoader}, by default it uses ClassLoading's {@link ClassLoader#getResources} + * instance to find providers. + * </p><p> + * To define a new RMIClassLoaderSpi for River to utilize, create a file in + * your providers jar file called: + * </p><p> + * META-INF/services/java.rmi.server.RMIClassLoaderSpi + * </p><p> + * This file should contain a single line with the fully qualified name of + * your RMIClassLoaderSpi implementation. + * </p><p> + * ClassLoading will iterate through all RMIClassLoaderSpi implementations found + * until it finds one defined by the system property: + * </p><p> + * System.getProperty("net.jini.loader.ClassLoading.provider"); + * </p><p> + * If this System property is not defined, ClassLoading will load + * <code>net.jini.loader.pref.PreferredClassProvider</code>, alternatively + * <code>java.rmi.server.RMIClassLoader</code> delegates all calls to {@link + * RMIClassLoader}. + * </p><p> + * If a provider is not found, it will not be updated. + * </p><p> + * <h1>History</h1> + * <p>Gregg Wonderly originally reported River-336 and provided a patch + * containing a new CodebaseAccessClassLoader to replace {@link RMIClassLoader}, + * later Sim Isjkes created RiverClassLoader that utilized ServiceLoader. + * Both implementations contained methods identical to {@link RMIClassLoaderSpi}, + * however new implementations were required to extend new provider + * implementations, creating a compatibility issue with existing implementations + * extending {@link RMIClassLoaderSpi}. For backward compatibility with existing + * implementations, {@link RMIClassLoaderSpi} has been retained as the provider, + * avoiding the need to recompile client code. The abilities of both + * implementations, to use ServiceLoader, or to define a provider using a method + * call have been retained, with the restriction that implementations are to be + * obtained via ServiceLoader. + * </p><p> + * Instead, all that is required for utilization of existing service provider + * {@link RMIClassLoaderSpi} implementations is to set the system property + * "net.jini.loader.ClassLoading.provider". + * </p> + * @author Sun Microsystems, Inc. + * @since 2.0 + **/ +public final class ClassLoading { + private final static Logger logger = Logger.getLogger(ClassLoading.class.getName()); + private static volatile RMIClassLoaderSpi provider; + private static final Object lock = new Object(); + private static final Guard permission = new RuntimePermission("setFactory"); + + static { + provider(null, ClassLoading.class.getClassLoader()); + } + + /** + * The current RMIClassLoaderSpi provider in use by ClassLoading. + * + * @return currently installed Provider, may be null. + * @throws SecurityException if caller doesn't have RuntimePermission "getFactory" + */ + public static RMIClassLoaderSpi getProvider(){ + permission.checkGuard(null); + return provider; + } + + private static boolean provider( + final String providerName, + final ClassLoader providerLoader) + { + RMIClassLoaderSpi newProvider = AccessController.doPrivileged( + new PrivilegedAction<RMIClassLoaderSpi>(){ + @Override + public RMIClassLoaderSpi run() { + String name = providerName; + if (name == null){ + name = System.getProperty( + "net.jini.loader.ClassLoading.provider"); + if (name == null) { + name = "net.jini.loader.pref.PreferredClassProvider"; + } else if ("java.rmi.server.RMIClassLoader".equals(name)){ + return null; + } + } + ServiceLoader<RMIClassLoaderSpi> loader + = ServiceLoader.load(RMIClassLoaderSpi.class, providerLoader); + Iterator<RMIClassLoaderSpi> iter = loader.iterator(); + RMIClassLoaderSpi spi; + while ( iter.hasNext() ) { + try { + spi = iter.next(); + if (spi != null) { + if (!name.equals(spi.getClass().getName())) + continue; + logger.log(Level.CONFIG, "loaded: {0}", name); + return spi; + } + } catch (Exception e) { + logger.log( + Level.CONFIG, + "error loading RMIClassLoaderSpi: {0}", + new Object[]{e} + ); + } + } + logger.log(Level.CONFIG, "uable to find {0}" , name); + return null; + } + }); + if (newProvider != null) { + provider = newProvider; + return true; + } else if (providerName == null) { + provider = null; + logger.log(Level.CONFIG, "loaded: java.rmi.server.RMIClassLoader"); + } + return false; + } + + /** + * Installs a new RMIClassLoaderSpi provider with the ClassLoader + * provided. + * + * @param providerName fully defined class name of the provider, if null, + * a new provider instance will be determined by system properties. + * @param providerLoader The class loader to be used to load + * provider-configuration files and provider classes, or null if the + * system class loader (or, failing that, the bootstrap class loader) + * is to be used. + * @return true if successful. + * @throws SecurityException if caller doesn't have RuntimePermission "getFactory" + */ + public static boolean installNewProvider( + String providerName, + ClassLoader providerLoader) + { + permission.checkGuard(null); + synchronized (lock){ + return provider(providerName, providerLoader); + } + } + + /** + * loaderMap contains a list of single threaded ExecutorService's for + * each ClassLoader, used for loading classes and proxies to avoid + * ClassLoader lock contention. An Entry is removed if the ClassLoader + * becomes weakly reachable, or the ExecutorService hasn't been used + * recently. + */ + private static final ConcurrentMap<ClassLoader,ExecutorService> loaderMap + = RC.concurrentMap( + new ConcurrentHashMap<Referrer<ClassLoader>,Referrer<ExecutorService>>(), + Ref.WEAK_IDENTITY, + Ref.TIME, + 10000L, + 10000L + ); + + /** + * per-thread cache (weakly) mapping verifierLoader values to + * (soft) sets of codebase values that have been verified (to + * provide content integrity) with the verifierLoader value + **/ + private static final ThreadLocal perThreadCache = new ThreadLocal() { + protected Object initialValue() { return new WeakHashMap(); } + }; + + /** + * Returns a class loader that loads classes from the given codebase + * RFC3986 compliant URI path. + * + * <p>This method delegates to the + * {@link RMIClassLoaderSpi#getClassLoader(String)} method + * of the provider instance, passing <code>codebase</code> as the argument. + * + * <p>If there is a security manger, its <code>checkPermission</code> + * method will be invoked with a + * <code>RuntimePermission("getClassLoader")</code> permission; + * this could result in a <code>SecurityException</code>. + * The provider implementation of this method may also perform further + * security checks to verify that the calling context has permission to + * connect to all of the URIs in the codebase URI path. + * + * @param codebase the list of URIs (space-separated) from which + * the returned class loader will load classes from, or <code>null</code> + * + * @return a class loader that loads classes from the given codebase URI + * path + * + * @throws MalformedURLException if <code>codebase</code> is + * non-<code>null</code> and contains an non RFC3986 compliant URI, or + * if <code>codebase</code> is <code>null</code> and a provider-specific + * URL used to identify the class loader is invalid + * + * @throws SecurityException if there is a security manager and the + * invocation of its <code>checkPermission</code> method fails, or + * if the caller does not have permission to connect to all of the + * URIs in the codebase URI path + * @since 3.0 + */ + public static ClassLoader getClassLoader(String codebase) + throws MalformedURLException, SecurityException + { + if (provider != null) return provider.getClassLoader(codebase); + return RMIClassLoader.getClassLoader(codebase); + } + + /** + * Returns the annotation string (representing a location for + * the class definition as a single or space delimited list of + * RFC3986 compliant URI) that JERI will use to annotate the class + * descriptor when marshalling objects of the given class. + * + * <p>This method delegates to the + * {@link RMIClassLoaderSpi#getClassAnnotation(Class)} method + * of the provider instance, passing <code>cl</code> as the argument. + * + * @param cl the class to obtain the annotation for + * + * @return a string to be used to annotate the given class when + * it gets marshalled, or <code>null</code> + * + * @throws NullPointerException if <code>cl</code> is <code>null</code> + * @since 3.0 + */ + public static String getClassAnnotation(Class<?> cl) { + if (provider != null) return provider.getClassAnnotation(cl); + return RMIClassLoader.getClassAnnotation(cl); + } + + /** + * Loads a class using {@link + * RMIClassLoaderSpi#loadClass(String,String,ClassLoader)}, + * optionally verifying that the RFC3986 compliant + * codebase URIs provide content integrity. + * + * <p>If <code>verifyCodebaseIntegrity</code> is <code>true</code> + * and <code>codebase</code> is not <code>null</code>, then this + * method invokes {@link Security#verifyCodebaseIntegrity + * Security.verifyCodebaseIntegrity} with <code>codebase</code> as + * the first argument and <code>verifierLoader</code> as the + * second argument (this invocation may be skipped if a previous + * invocation of this method or {@link #loadProxyClass + * loadProxyClass} has already invoked + * <code>Security.verifyCodebaseIntegrity</code> with the same + * value of <code>codebase</code> and the same effective value of + * <code>verifierLoader</code> as arguments without it throwing an + * exception). If <code>Security.verifyCodebaseIntegrity</code> + * throws a <code>SecurityException</code>, then this method + * proceeds as if <code>codebase</code> were <code>null</code>. + * If <code>Security.verifyCodebaseIntegrity</code> throws any + * other exception, then this method throws that exception. + * + * <p>This method then invokes {@link + * RMIClassLoaderSpi#loadClass(String,String,ClassLoader) + * RMIClassLoaderSpi.loadClass} with <code>codebase</code> as the + * first argument (or <code>null</code> if in the previous step + * <code>Security.verifyCodebaseIntegrity</code> was invoked and + * it threw a <code>SecurityException</code>), <code>name</code> + * as the second argument, and <code>defaultLoader</code> as the + * third argument. If <code>RMIClassLoaderSpi.loadClass</code> + * throws a <code>ClassNotFoundException</code>, then this method + * throws a <code>ClassNotFoundException</code>; if + * <code>RMIClassLoaderSpi.loadClass</code> throws any other + * exception, then this method throws that exception; otherwise, + * this method returns the <code>Class</code> returned by + * <code>RMIClassLoaderSpi.loadClass</code>. + * + * @param codebase the list of URLs (separated by spaces) to load + * the class from, or <code>null</code> + * + * @param name the name of the class to load + * + * @param defaultLoader the class loader value (possibly + * <code>null</code>) to pass as the <code>defaultLoader</code> + * argument to <code>RMIClassLoaderSpi.loadClass</code> + * + * @param verifyCodebaseIntegrity if <code>true</code>, verify + * that the RFC3986 compliant codebase URIs provide content integrity + * + * @param verifierLoader the class loader value (possibly + * <code>null</code>) to pass to + * <code>Security.verifyCodebaseIntegrity</code>, if + * <code>verifyCodebaseIntegrity</code> is <code>true</code> + * + * @return the <code>Class</code> object representing the loaded + * class + * + * @throws MalformedURLException if + * <code>Security.verifyCodebaseIntegrity</code> or + * <code>RMIClassLoaderSpi.loadClass</code> throws a + * <code>MalformedURLException</code> + * + * @throws ClassNotFoundException if + * <code>RMIClassLoaderSpi.loadClass</code> throws a + * <code>ClassNotFoundException</code> + * + * @throws NullPointerException if <code>name</code> is + * <code>null</code> + **/ + public static Class<?> loadClass(String codebase, + String name, + ClassLoader defaultLoader, + boolean verifyCodebaseIntegrity, + ClassLoader verifierLoader) + throws MalformedURLException, ClassNotFoundException + { + SecurityException verifyException = null; + if (verifyCodebaseIntegrity && codebase != null) { + try { + verifyIntegrity(codebase, verifierLoader); + } catch (SecurityException e) { + verifyException = e; + codebase = null; + } + } + try { + if (provider != null) + return provider.loadClass(codebase, name, defaultLoader); + return RMIClassLoader.loadClass(codebase, name, defaultLoader); + } catch (ClassNotFoundException e) { + if (verifyException != null) { + // assume that the verify exception is more important + throw new ClassNotFoundException(e.getMessage(), + verifyException); + } else { + e.fillInStackTrace(); + throw e; + } + } + } + + /** + * Loads a dynamic proxy class using {@link + * RMIClassLoaderSpi#loadProxyClass(String,String[],ClassLoader)}, + * optionally verifying that the RFC3986 compliant + * codebase URIs provide content integrity. + * + * <p>If <code>verifyCodebaseIntegrity</code> is <code>true</code> + * and <code>codebase</code> is not <code>null</code>, then this + * method invokes {@link Security#verifyCodebaseIntegrity + * Security.verifyCodebaseIntegrity} with <code>codebase</code> as + * the first argument and <code>verifierLoader</code> as the + * second argument (this invocation may be skipped if a previous + * invocation of this method or {@link #loadClass loadClass} has + * already invoked <code>Security.verifyCodebaseIntegrity</code> + * with the same value of <code>codebase</code> and the same + * effective value of <code>verifierLoader</code> as arguments + * without it throwing an exception). If + * <code>Security.verifyCodebaseIntegrity</code> throws a + * <code>SecurityException</code>, then this method proceeds as if + * <code>codebase</code> were <code>null</code>. If + * <code>Security.verifyCodebaseIntegrity</code> throws any other + * exception, then this method throws that exception. + * + * <p>This method invokes {@link + * RMIClassLoaderSpi#loadProxyClass(String,String[],ClassLoader)} + * with <code>codebase</code> as + * the first argument (or <code>null</code> if in the previous + * step <code>Security.verifyCodebaseIntegrity</code> was invoked + * and it threw a <code>SecurityException</code>), + * <code>interfaceNames</code> as the second argument, and + * <code>defaultLoader</code> as the third argument. If + * <code>RMIClassLoaderSpi.loadProxyClass</code> throws a + * <code>ClassNotFoundException</code>, then this method throws a + * <code>ClassNotFoundException</code>; if + * <code>RMIClassLoaderSpi.loadProxyClass</code> throws any other + * exception, then this method throws that exception; otherwise, + * this method returns the <code>Class</code> returned by + * <code>RMIClassLoaderSpi.loadProxyClass</code>. + * + * @param codebase the list of URLs (separated by spaces) to load + * classes from, or <code>null</code> + * + * @param interfaceNames the names of the interfaces for the proxy + * class to implement + * + * @param defaultLoader the class loader value (possibly + * <code>null</code>) to pass as the <code>defaultLoader</code> + * argument to <code>RMIClassLoader.loadProxyClass</code> + * + * @param verifyCodebaseIntegrity if <code>true</code>, verify + * that the codebase URLs provide content integrity + * + * @param verifierLoader the class loader value (possibly + * <code>null</code>) to pass to + * <code>Security.verifyCodebaseIntegrity</code>, if + * <code>verifyCodebaseIntegrity</code> is <code>true</code> + * + * @return the <code>Class</code> object representing the loaded + * dynamic proxy class + * + * @throws MalformedURLException if + * <code>Security.verifyCodebaseIntegrity</code> or + * <code>RMIClassLoaderSpi.loadProxyClass</code> throws a + * <code>MalformedURLException</code> + * + * @throws ClassNotFoundException if + * <code>RMIClassLoaderSpi.loadProxyClass</code> throws a + * <code>ClassNotFoundException</code> + * + * @throws NullPointerException if <code>interfaceNames</code> is + * <code>null</code> or if any element of + * <code>interfaceNames</code> is <code>null</code> + **/ + public static Class<?> loadProxyClass(String codebase, + String[] interfaceNames, + ClassLoader defaultLoader, + boolean verifyCodebaseIntegrity, + ClassLoader verifierLoader) + throws MalformedURLException, ClassNotFoundException + { + SecurityException verifyException = null; + if (verifyCodebaseIntegrity && codebase != null) { + try { + verifyIntegrity(codebase, verifierLoader); + } catch (SecurityException e) { + verifyException = e; + codebase = null; + } + } + try { + if (provider != null) return + provider.loadProxyClass(codebase, interfaceNames, defaultLoader); + return RMIClassLoader.loadProxyClass(codebase, interfaceNames, + defaultLoader); + } catch (ClassNotFoundException e) { + if (verifyException != null) { + // assume that the verify exception is more important + throw new ClassNotFoundException(e.getMessage(), + verifyException); + } else { + e.fillInStackTrace(); + throw e; + } + } + } + + /** + * Wraps Security.verifyCodebaseIntegrity with caching for + * performance. (Perhaps such caching should be done by + * Security.verifyCodebaseIntegrity instead.) + **/ + private static void verifyIntegrity(String codebase, + ClassLoader verifierLoader) + throws MalformedURLException + { + /* + * Check if we've already verified the same codebase in this + * thread using the same verifierLoader value. + */ + Map verifierLoaderCache = (Map) perThreadCache.get(); + // defend against varying context class loader value of thread + ClassLoader verifierLoaderKey = + (verifierLoader != null ? verifierLoader : + (ClassLoader) AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return Thread.currentThread().getContextClassLoader(); + } + })); + Map verifiedCodebases = + (Map) verifierLoaderCache.get(verifierLoaderKey); + if (verifiedCodebases != null && + verifiedCodebases.containsKey(codebase)) + { + return; + } + + Security.verifyCodebaseIntegrity(codebase, verifierLoader); + + /* + * Remember that we've verified this codebase in this thread + * with the given verifierLoader value. + */ + if (verifiedCodebases == null) { + verifiedCodebases = new WeakHashMap(); + verifierLoaderCache.put(verifierLoaderKey, verifiedCodebases); + } + verifiedCodebases.put(codebase, new SoftReference(codebase)); + return; + } + + /** + * Returns the {@code Class} object associated with the class or + * interface with the given string name, using the given class loader. + * + * This method calls {@link Class#forName(String,boolean,ClassLoader)}, + * from a Thread dedicated for each + * ClassLoader, avoiding contention for ClassLoader locks by thread + * confinement. This provides a significant scalability benefit for + * JERI, without needing to resort to parallel ClassLoader locks, which + * isn't part of the Java specification. + * + * If loader is null, thread confinement is not used. + * + * @param name fully qualified name of the desired class + * @param initialize whether the class must be initialized + * @param loader class loader from which the class must be loaded + * @return class object representing the desired class + * + * @exception LinkageError if the linkage fails + * @exception ExceptionInInitializerError if the initialization provoked + * by this method fails + * @exception ClassNotFoundException if the class cannot be located by + * the specified class loader + * @see Class + * @since 3.0 + */ + public static Class<?> forName(String name, boolean initialize, + ClassLoader loader) + throws ClassNotFoundException + { + if (loader == null) return Class.forName(name, initialize, loader); + // Don't thread confine profiler ClassLoaders. + if (loader.toString().startsWith("javax.management.remote.rmi.RMIConnectionImpl") ) + return Class.forName(name, initialize, loader); + + ExecutorService exec = loaderMap.get(loader); + if (exec == null){ + exec = new ThreadPoolExecutor( + 1, + 1, + 0, + TimeUnit.SECONDS, + new LinkedBlockingQueue(), + new NamedThreadFactory(loader.toString(),false), + new ThreadPoolExecutor.CallerRunsPolicy() + ); + ExecutorService existed = loaderMap.putIfAbsent(loader, exec); + if (existed != null){ + exec = existed; + } + } + FutureTask<Class> future = new FutureTask(new GetClassTask(name, initialize, loader)); + exec.submit(future); + try { + return future.get(); + } catch (InterruptedException e){ + e.fillInStackTrace(); + throw new ClassNotFoundException("Interrupted, Unable to find Class: " + name, e); + } catch (ExecutionException e){ + Throwable t = e.getCause(); + if (t instanceof LinkageError) throw (LinkageError) t; + if (t instanceof ExceptionInInitializerError) + throw (ExceptionInInitializerError) t; + if (t instanceof SecurityException) throw (SecurityException) t; + if (t instanceof ClassNotFoundException ) + throw (ClassNotFoundException) t; + if (t instanceof NullPointerException) throw (NullPointerException) t; + throw new ClassNotFoundException("Unable to find Class:" + name, t); + } + } + + private static class GetClassTask implements Callable<Class> { + private final String name; + private final boolean initialize; + private final ClassLoader loader; + + private GetClassTask(String name, boolean initialize, ClassLoader loader){ + this.name = name; + this.initialize = initialize; + this.loader = loader; + } + + @Override + public Class call() throws ClassNotFoundException { + return Class.forName(name, initialize, loader); + } + + } + + private ClassLoading() { throw new AssertionError(); } +}
