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