/* * 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.security.proxytrust; import com.sun.jini.thread.Executor; import com.sun.jini.thread.GetThreadPoolAction; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.ExportException; import java.security.Permission; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; import net.jini.core.constraint.RemoteMethodControl; import net.jini.export.Exporter; import net.jini.security.Security; import net.jini.security.TrustVerifier; /** * Contains the information necessary to export a remote object that has a * proxy that will not directly be considered trusted by clients, such that * the proxy can be trusted by clients using {@link ProxyTrustVerifier}. The * remote object to be exported (called the main remote object) must be an * instance of {@link ServerProxyTrust}. In addition to exporting the main * remote object, this exporter also exports a second remote object * (called the bootstrap remote object, which is created internally by this * exporter) that implements the {@link ProxyTrust} interface by delegating * the {@link ProxyTrust#getProxyVerifier getProxyVerifier} method to the * corresponding {@link ServerProxyTrust#getProxyVerifier getProxyVerifier} * method of the main remote object. * * @author Sun Microsystems, Inc. * @see net.jini.jeri.ProxyTrustILFactory * @since 2.0 */ public class ProxyTrustExporter implements Exporter { /** Permission required to use class loader of main proxy's class */ private static final Permission loaderPermission = new RuntimePermission("getClassLoader"); /** Executor that executes tasks in pooled system threads. */ private static final Executor systemThreadPool = (Executor) Security.doPrivileged(new GetThreadPoolAction(false)); /** Holds strong refs to WeakRefs until they are cleared */ private static final Set refs = new HashSet(); /** Reference queue for WeakRefs */ private static final ReferenceQueue queue = new ReferenceQueue(); /** WeakRef reaper, if any */ private static Reaper reaper = null; /** The main exporter, for the main remote object.*/ private final Exporter mainExporter; /** The bootstrap exporter, for the ProxyTrust remote object. */ private final Exporter bootExporter; /** The class loader to define the proxy class in, or null */ private final ClassLoader loader; /** WeakRef to impl */ private WeakRef ref = null; /** * Creates an instance with the specified main exporter (which will be * used to export the main remote object) and the specified bootstrap * exporter (which will be used to export the bootstrap remote object). * The main exporter must produce a proxy (called the main proxy) that is * an instance of both {@link RemoteMethodControl} and * {@link TrustEquivalence}, and if the main proxy's class is not * <code>public</code>, the direct superinterfaces of that class and all * of its superclasses must be <code>public</code>. The bootstrap * exporter, when used to export a remote object that is an instance of * {@link ProxyTrust}, must produce a proxy (called the bootstrap proxy) * that is an instance of <code>ProxyTrust</code>, * <code>RemoteMethodControl</code>, and <code>TrustEquivalence</code>, * and should satisfy the bootstrap proxy requirements of * <code>ProxyTrustVerifier</code>. * The dynamic proxy class generated at export will be defined * by the same class loader as the main proxy's class. * * @param mainExporter the main exporter, for the main remote object * @param bootExporter the bootstrap exporter, for the bootstrap remote * object * @throws NullPointerException if any argument is <code>null</code> */ public ProxyTrustExporter(Exporter mainExporter, Exporter bootExporter) { this(mainExporter, bootExporter, null); } /** * Creates an instance with the specified main exporter (which will be * used to export the main remote object), the specified bootstrap * exporter (which will be used to export the bootstrap remote object), * and the specified class loader (in which the generated dynamic proxy * class will be defined). The main exporter must produce a proxy (called * the main proxy) that is an instance of both {@link RemoteMethodControl} * and {@link TrustEquivalence}, and if the main proxy's class is not * <code>public</code>, the direct superinterfaces of that class and all * of its superclasses must be <code>public</code>. The bootstrap * exporter, when used to export a remote object that is an instance of * {@link ProxyTrust}, must produce a proxy (called the bootstrap proxy) * that is an instance of <code>ProxyTrust</code>, * <code>RemoteMethodControl</code>, and <code>TrustEquivalence</code>, * and should satisfy the bootstrap proxy requirements of * <code>ProxyTrustVerifier</code>. * If the specified class loader is <code>null</code>, the * dynamic proxy class generated at export will be defined by the same * class loader as the main proxy's class. * * @param mainExporter the main exporter, for the main remote object * @param bootExporter the bootstrap exporter, for the bootstrap remote * object * @param loader the class loader to define the proxy class in, or * <code>null</code> * @throws NullPointerException if either exporter argument is * <code>null</code> */ public ProxyTrustExporter(Exporter mainExporter, Exporter bootExporter, ClassLoader loader) { if (mainExporter == null || bootExporter == null) { throw new NullPointerException("exporter is null"); } this.mainExporter = mainExporter; this.bootExporter = bootExporter; this.loader = loader; } /** * Exports the specified main remote object and returns a dynamic proxy * for the object. The main remote object must be an instance of * {@link ServerProxyTrust}. The main remote object is exported using the * main exporter (specified at the construction of this exporter), * returning a proxy called the main proxy. The main proxy must be an * instance of both {@link RemoteMethodControl} and * {@link TrustEquivalence}, and if the main proxy's class is not * <code>public</code>, the direct superinterfaces of that class and all * of its superclasses must be <code>public</code>. A bootstrap remote * object that is an instance of {@link ProxyTrust} is created and * exported using the bootstrap exporter (also specified at the * construction of this exporter), returning a proxy called the bootstrap * proxy. The bootstrap proxy must be an instance of * <code>ProxyTrust</code>, <code>RemoteMethodControl</code>, and * <code>TrustEquivalence</code>. The bootstrap remote object will remain * reachable as long as the main remote object is reachable and the * {@link #unexport unexport} method of this exporter has not returned * <code>true</code>. A {@link Proxy} class is generated that implements * the direct superinterfaces of the main proxy's class and all of its * superclasses, in the following order: the direct superinterfaces of a * class immediately follow the direct superinterfaces of its direct * superclass; the direct superinterfaces of a class are in declaration * order (the order in which they are declared in the class's * <code>implements</code> clause); and if an interface appears more than * once, only the first instance is retained. If a non-<code>null</code> * class loader was specified at the construction of this exporter, the * generated class is defined by that class loader, otherwise it is * defined by the class loader of the main proxy's class. The dynamic * proxy returned by this method is an instance of that generated class, * containing a {@link ProxyTrustInvocationHandler} instance created with * the main proxy and the bootstrap proxy. * * @throws ExportException if the export of either remote object throws * <code>ExportException</code>, or if the export the bootstrap remote * object throws <code>IllegalArgumentException</code>, or if the main * proxy is not an instance of both <code>RemoteMethodControl</code> and * <code>TrustEquivalence</code>, or if the main proxy's class is not * <code>public</code> and it or a superclass has a * non-<code>public</code> direct superinterface, or if the bootstrap * proxy is not an instance of <code>ProxyTrust</code>, * <code>RemoteMethodControl</code>, and <code>TrustEquivalence</code>, * or if any of the superinterfaces of the main proxy's class are not * visible through the class loader specified at the construction of this * exporter * @throws IllegalArgumentException if the specified remote object is not * an instance of <code>ServerProxyTrust</code>, or if the export of the * main remote object throws <code>IllegalArgumentException</code> * @throws IllegalStateException if the export of either remote object * throws <code>IllegalStateException</code> * @throws SecurityException if a non-<code>null</code> class loader was * not specified at the construction of this exporter and the calling * context does not have * {@link RuntimePermission}<code>("getClassLoader")</code> permission */ public synchronized Remote export(Remote impl) throws ExportException { if (impl != null && !(impl instanceof ServerProxyTrust)) { throw new IllegalArgumentException( "must implement ServerProxyTrust"); } if (loader == null) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(loaderPermission); } } Object main = mainExporter.export(impl); Class c = main.getClass(); LinkedList ifaces = new LinkedList(); Object boot = null; boolean ok = false; try { if (!(main instanceof RemoteMethodControl)) { throw new ExportException( "main proxy must implement RemoteMethodControl"); } else if (!(main instanceof TrustEquivalence)) { throw new ExportException( "main proxy must implement TrustEquivalence"); } boolean needPub = !Modifier.isPublic(c.getModifiers()); for (Class sup = c; sup != null; sup = sup.getSuperclass()) { Class[] ifs = sup.getInterfaces(); for (int i = ifs.length; --i >= 0; ) { if (needPub && !Modifier.isPublic(ifs[i].getModifiers())) { throw new ExportException( "main proxy implements non-public interface"); } ifaces.remove(ifs[i]); ifaces.addFirst(ifs[i]); } } ref = new WeakRef(impl); synchronized (refs) { if (reaper == null) { reaper = new Reaper(); systemThreadPool.execute(reaper, "ProxyTrustExporter.Reaper"); } refs.add(ref); } boot = bootExporter.export(ref.boot); if (!(boot instanceof ProxyTrust)) { throw new ExportException( "bootstrap proxy must implement ProxyTrust"); } else if (!(boot instanceof RemoteMethodControl)) { throw new ExportException( "bootstrap proxy must implement RemoteMethodControl"); } else if (!(boot instanceof TrustEquivalence)) { throw new ExportException( "bootstrap proxy must implement TrustEquivalence"); } Remote proxy = (Remote) Proxy.newProxyInstance( loader != null ? loader : c.getClassLoader(), (Class[]) ifaces.toArray( new Class[ifaces.size()]), new ProxyTrustInvocationHandler( (RemoteMethodControl) main, (ProxyTrust) boot)); ok = true; return proxy; } catch (IllegalArgumentException e) { throw new ExportException("export failed", e); } finally { if (!ok) { if (ref != null) { ref.enqueue(); } if (boot != null) { bootExporter.unexport(true); } mainExporter.unexport(true); } } } /** * Unexports the remote objects that were previously exported via this * exporter. The <code>unexport</code> method of the main exporter is * called with the specified argument and if that returns * <code>true</code>, the <code>unexport</code> method of the bootstrap * exporter is called with <code>true</code>. The result of the main * <code>unexport</code> call is returned by this method. Any exception * thrown by either <code>unexport</code> call is rethrown by this * method. * * @throws IllegalStateException if the unexport of either remote object * throws <code>IllegalStateException</code> */ public synchronized boolean unexport(boolean force) { if (!mainExporter.unexport(force)) { return false; } bootExporter.unexport(true); if (ref != null) { ref.enqueue(); } return true; } /** * Weak reference to the main remote object with strong reference * to the bootstrap remote object. */ private static class WeakRef extends WeakReference { /** The bootstrap remote object */ ProxyTrust boot = new ProxyTrustImpl(this); /** Create an instance registered with queue */ WeakRef(Remote impl) { super(impl, queue); } /** Clear both references */ public void clear() { super.clear(); boot = null; } } /** WeakRef reaper */ private static class Reaper implements Runnable { Reaper() { } /** * Keep removing refs from the queue, clearing them, and removing * them from the refs list, until the refs list is empty. */ public void run() { try { while (true) { Reference ref = queue.remove(); ref.clear(); synchronized (refs) { refs.remove(ref); if (refs.isEmpty()) { reaper = null; return; } } } } catch (InterruptedException e) { } } } /** ProxyTrust impl class */ private static class ProxyTrustImpl implements ProxyTrust { /** Weak reference to the main remote object */ private final Reference ref; ProxyTrustImpl(Reference ref) { this.ref = ref; } /** Delegate to the main remote object */ public TrustVerifier getProxyVerifier() throws RemoteException { ServerProxyTrust impl = (ServerProxyTrust) ref.get(); if (impl == null) { throw new UnsupportedOperationException("impl is gone"); } return impl.getProxyVerifier(); } } }