/* * 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.jeri; import com.sun.jini.jeri.internal.runtime.BasicExportTable; import com.sun.jini.logging.Levels; import java.lang.ref.WeakReference; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.ExportException; import java.rmi.server.Unreferenced; import java.security.AccessControlContext; import java.security.PrivilegedAction; import java.util.logging.Level; import java.util.logging.Logger; import net.jini.config.Configuration; import net.jini.export.Exporter; import net.jini.export.ServerContext; import net.jini.id.Uuid; import net.jini.id.UuidFactory; import net.jini.io.MarshalInputStream; import net.jini.io.context.ClientHost; import net.jini.io.context.ClientSubject; import net.jini.security.Security; import net.jini.security.SecurityContext; /** * An <code>Exporter</code> implementation for exporting * a remote object to use Jini extensible remote invocation * (Jini ERI). Typically, instances of this class should be * obtained from a {@link Configuration} rather than being explicitly * constructed. * Each instance of <code>BasicJeriExporter</code> * can export only a single remote object. * * <p>The following properties (defined during construction) govern * invocation behavior and other characteristics of the exported remote * object and its proxy: * <ul> * <li>{@link ServerEndpoint}: the <code>ServerEndpoint</code> over * which calls are accepted. * * <li><p>{@link InvocationLayerFactory}: a factory used to obtain the remote * object's proxy and invocation dispatcher. * * <li><p><i>enableDGC</i> flag: if <code>true</code>, distributed * garbage collection (DGC) is enabled for the exported remote object, * and the {@link BasicObjectEndpoint} produced by this exporter * participates in DGC and thus constitutes a strong remote reference * to the remote object; if <code>false</code>, DGC is not enabled for * the remote object, and the <code>BasicObjectEndpoint</code> does * not participate in DGC and thus is a weak remote reference, so it * does not prevent the remote object from being garbage collected. * * <li><p><i>keepAlive</i> flag: if <code>true</code>, the virtual * machine is kept alive (with a non-daemon thread) while the remote * object remains exported via this exporter. * * <li><p>{@link Uuid Uuid}: the object identifier to use for the * remote object; if <code>null</code>, a unique object identifier is * chosen for the remote object using {@link UuidFactory#generate * UuidFactory.generate}. * </ul> * * <p>If DGC is not enabled for a remote object, then the * implementation always only weakly references the remote object. If * DGC is enabled for a remote object, then the implementation weakly * references the remote object when its referenced set is empty and * strongly references the remote object when its referenced set is * not empty (see below). If the implementation weakly references the * remote object and the weak reference is cleared, the remote object * becomes effectively unexported. * * <p>Enabling DGC is not advisable in some circumstances. DGC should * not be enabled for a remote object exported with a well known * object identifier. Enabling DGC with a secure remote object is * generally discouraged, because DGC communication is always made in * a client-side context in which there are no client constraints and * no client subject, so it can leave the remote object open to denial * of service attacks. Some transport providers may not support making * requests without a client subject, so even if DGC is enabled in the * case where such a transport provider is used, DGC will be effectively * disabled on the client side. * * <p>Multiple remote objects can be exported on the same server endpoint, * and the same remote object can be exported multiple times on different * server endpoints with the only restriction being that a given pair of * object identifier and listen endpoint (derived from the server endpoint) * can only have one active export at any given time. * * <p>Two instances of this class are equal only if they are references to * the same (<code>==</code>) object. * * <p>The server endpoint is not transmitted in the remote reference; only the * derived client endpoint is transmitted. * * <p>Remote objects exported with instances of this class can call {@link * ServerContext#getServerContextElement * ServerContext.getServerContextElement}, passing the class {@link * ClientSubject} to obtain the authenticated identity of the client (if * any) for an incoming remote call, or passing the class {@link * ClientHost} to obtain the address of the client host. * * <p>For remote objects exported with instances of this class, there is no * automatic replacement of the proxy for the remote object during * marshalling; either the proxy must be passed explicitly, or the remote * object implementation class must be serializable and have a * <code>writeReplace</code> method that returns its proxy. * * <h4>Distributed Garbage Collection</h4> * * The <code>BasicJeriExporter</code> class acts as the server-side * DGC implementation for all remote objects exported with DGC enabled * using its instances. * * <p>An entity known as the <i>DGC client</i> tracks the existence * and reachability of live remote references * (<code>BasicObjectEndpoint</code> instances that participate in * DGC) for a <code>BasicObjectEndpoint</code> class in some * (potentially) remote virtual machine. A DGC client is identified * by a universally unique identifier (a <code>Uuid</code>). A DGC * client sends <i>dirty calls</i> and <i>clean calls</i> to the * {@link Endpoint} of a live remote reference to inform the * server-side DGC implementation when the number of live remote * references to a given remote object it is tracking increases from * zero to greater than zero and decreases from greater than zero to * zero, respectively. A DGC client also sends dirty calls to the * <code>Endpoint</code> of live remote references it is tracking to * renew its lease. The client-side behavior of dirty and clean calls * is specified by {@link BasicObjectEndpoint}. * * <p>On the server side, for every remote object exported with DGC * enabled, the implementation maintains a <i>referenced set</i>, * which contains the <code>Uuid</code>s of the DGC clients that are * known to have live remote references to the remote object. The * contents of the referenced set are modified as a result of dirty * calls, clean calls, and expiration of leases (see below). While * the referenced set is not empty, the implementation strongly * references the remote object, so that it will not be garbage * collected. While the referenced set is empty, the implementation * only weakly references the remote object, so that it may be garbage * collected (if it is not otherwise strongly reachable locally). If * a remote object is garbage collected, it becomes effectively * unexported. If a remote object that is an instance of {@link * Unreferenced} is exported with DGC enabled, then whenever the size * of its referenced set transitions from greater than zero to zero, * its {@link Unreferenced#unreferenced unreferenced} method will be * invoked (before the strong reference is dropped). Note that a * referenced set spans multiple exports of the same (identical) * remote object with <code>BasicJeriExporter</code>. * * <p>For every <code>RequestDispatcher</code> passed by * <code>BasicJeriExporter</code> to a <code>ListenEndpoint</code> as * part of exporting, whenever it has at least one remote object * exported with DGC enabled, it also has an implicitly exported * remote object that represents the server-side DGC implementation. * This remote object is effectively exported with an object * identifier of <code>d32cd1bc-273c-11b2-8841-080020c9e4a1</code> and * an <code>InvocationDispatcher</code> that behaves like a {@link * BasicInvocationDispatcher} with no server constraints, with a * {@link BasicInvocationDispatcher#createMarshalInputStream * createMarshalInputStream} implementation that returns a {@link * MarshalInputStream} that ignores codebase annotations, and with * support for at least the following remote methods: * * <pre> * long dirty(Uuid clientID, long sequenceNum, Uuid[] ids) * throws {@link RemoteException}; * * void clean(Uuid clientID, long sequenceNum, Uuid[] ids, boolean strong) * throws RemoteException; * </pre> * * <code>clientID</code> identifies the DGC client that is making the * dirty or clean call, and <code>sequenceNum</code> identifies the * sequence number of the dirty or clean call with respect to other * dirty and clean calls from the same DGC client. The sequence * numbers identify the correct order of semantic interpretation of * dirty and clean calls from the same DGC client, regardless of the * order in which they arrive. The well-known object identifier for * the server-side DGC implementation is reserved; attempting to * export any other remote object with that object identifier always * throws an {@link ExportException}. * * <p>A dirty call is processed as follows: * * <ul> * * <li>It establishes or renews the DGC lease for the identified DGC * client. The duration of the granted lease, which is chosen by the * implementation, is conveyed as the value returned by the dirty * call, in milliseconds starting from the some time during the * processing of the dirty call. While the lease for a DGC client is * valid (not expired), the DGC client is preserved in referenced sets * of exported remote objects. * * <li><p>It adds the DGC client's <code>Uuid</code> to the referenced * sets of the exported remote objects identified by <code>ids</code>, * if any, so that they are prevented from being garbage collected. * For each <code>Uuid</code> in <code>ids</code>: * * <blockquote> * * The identified remote object is the remote object exported with * that <code>Uuid</code> on the <code>ListenEndpoint</code> (and thus * <code>RequestDispatcher</code>) that the dirty call was received * on. If no such exported remote object exists (for example, if it * has been garbage collected), then that <code>Uuid</code> in * <code>ids</code> is ignored. If the sequence number is less than * the last recorded sequence number of a dirty or clean call for the * identified remote object from the same DGC client, then the remote * object's referenced set is not modified. Otherwise, the DGC * client's <code>Uuid</code> is added to the remote object's * referenced set (if not already present). If this addition causes * the referenced set to transition from empty to non-empty, then the * implementation starts strongly referencing the remote object. * * </blockquote> * * </ul> * * <p>A clean call is processed as follows: * * <ul> * * <li>It removes the DGC client's <code>Uuid</code> from the * referenced sets of the exported remote objects identified by * <code>ids</code>, so that they are not prevented from being garbage * collected by the given DGC client. For each <code>Uuid</code> in * <code>ids</code>: * * <blockquote> * * <p>The identified remote object is the remote object exported with * that <code>Uuid</code> on the <code>ListenEndpoint</code> (and thus * <code>RequestDispatcher</code>) that the dirty call was received * on. If no such exported remote object exists (for example, if it * has been garbage collected), then that <code>Uuid</code> in * <code>ids</code> is ignored. If the sequence number is less then * the last recorded sequence number of a dirty or clean call for the * identified remote object from the same DGC client, then the remote * object's referenced set is not modified. Otherwise, the DGC * client's <code>Uuid</code> is removed from the remote object's * referenced set (if it is present). If this removal causes the * referenced set to transition from non-empty to empty, then the * implementation starts only weakly referencing the remote object * (and before doing so, if the remote object is an instance of * <code>Unreferenced</code>, its <code>unreferenced</code> method is * invoked). If <code>strong</code> is <code>true</code>, then a * record is kept of the specified sequence number from the DGC client * for some reasonable period of time, in the event of a dirty call * that might arrive later with a lower sequence number. * * </blockquote> * * </ul> * * <p>If the implementation detects that the most recently granted DGC * lease for a given DGC client has expired, then it assumes that the * DGC client has abnormally terminated, and the DGC client's * <code>Uuid</code> is removed from the referenced sets of all * exported remote objects. If this removal causes a referenced set * to transition from non-empty to empty, then the implementation * starts only weakly referencing the corresponding remote object (and * before doing so, if the remote object is an instance of * <code>Unreferenced</code>, its <code>unreferenced</code> method is * invoked). * * <p>Unexporting a remote object with a * <code>BasicJeriExporter</code> causes the removal of DGC client * <code>Uuid</code>s from the remote object's referenced set that * were only present because of dirty calls that were received as a * result of exporting it with that <code>BasicJeriExporter</code>. * If the remote object remains exported with DGC enabled by another * <code>BasicJeriExporter</code> and this removal causes the * referenced set to transition from non-empty to empty, then the * implementation starts only weakly referencing the remote object * (and before doing so, if the remote object is an instance of * <code>Unreferenced</code>, its <code>unreferenced</code> method is * invoked). * * <p>When the implementation invokes a remote object's * <code>unreferenced</code> method, it does so with the security * context and context class loader in effect when the remote object * was exported. If the remote object is currently exported more than * once, then the security context and context class loader in effect * for any one of those exports are used. * * @author Sun Microsystems, Inc. * @since 2.0 * * @com.sun.jini.impl * * <p>This implementation uses the {@link Logger} named * <code>net.jini.jeri.BasicJeriExporter</code> to log * information at the following levels: * * <table summary="Describes what is logged by BasicJeriExporter at various * logging levels" border=1 cellpadding=5> * * <tr> <th> Level <th> Description * * <tr> <td> {@link Levels#FAILED FAILED} <td> incoming request for * unrecognized object identifier (no such object) * * <tr> <td> {@link Levels#FAILED FAILED} <td> I/O exception * dispatching incoming request * * <tr> <td> {@link Level#FINE FINE} <td> successful export of object * * <tr> <td> {@link Level#FINE FINE} <td> attempted unexport of object * * <tr> <td> {@link Level#FINE FINE} <td> garbage collection of * exported object * * <tr> <td> {@link Level#FINE FINEST} <td> detailed implementation * activity * * </table> **/ public final class BasicJeriExporter implements Exporter { private static final Logger logger = Logger.getLogger("net.jini.jeri.BasicJeriExporter"); private final ServerEndpoint se; private final InvocationLayerFactory ilf; private final boolean enableDGC; private final boolean keepAlive; private final Uuid id; private boolean used = false; private BasicExportTable.Entry entry; private WeakReference weakImplContainer = null; private static final BasicExportTable table = new BasicExportTable(); /** * Creates a new <code>BasicJeriExporter</code> with the given server * endpoint and invocation layer factory. The other properties of the * exporter default as follows: the <code>enableDGC</code> flag is * <code>false</code>, the <code>keepAlive</code> flag is * <code>true</code>, and the object identifier is chosen by invoking * {@link UuidFactory#generate UuidFactory.generate}. * * @param se the server endpoint over which calls may be accepted * @param ilf the factory for creating the remote object's * proxy and invocation dispatcher * @throws NullPointerException if <code>se</code> or <code>ilf</code> * is <code>null</code> **/ public BasicJeriExporter(ServerEndpoint se, InvocationLayerFactory ilf) { this(se, ilf, false, true); } /** * Creates a new <code>BasicJeriExporter</code> with the given server * endpoint, invocation layer factory, <code>enableDGC</code> flag, and * <code>keepAlive</code> flag. The object identifier is chosen by * invoking {@link UuidFactory#generate UuidFactory.generate}. * * @param se the server endpoint over which calls may be accepted * @param ilf the factory for creating the remote object's * proxy and invocation dispatcher * @param enableDGC if <code>true</code>, DGC is enabled to the object * on this server endpoint * @param keepAlive if <code>true</code>, the VM is kept alive * while the object (exported via this exporter) remains * exported * @throws NullPointerException if <code>se</code> or <code>ilf</code> * is <code>null</code> **/ public BasicJeriExporter(ServerEndpoint se, InvocationLayerFactory ilf, boolean enableDGC, boolean keepAlive) { this(se, ilf, enableDGC, keepAlive, null); } /** * Creates a new <code>BasicJeriExporter</code> with the given server * endpoint, invocation layer factory, <code>enableDGC</code> flag, * <code>keepAlive</code> flag, and object identifier. If * <code>id</code> is <code>null</code>, the object identifier is * chosen by invoking {@link UuidFactory#generate * UuidFactory.generate}. * * @param se the server endpoint over which calls may be accepted * @param ilf the factory for creating the remote object's proxy * and invocation dispatcher * @param enableDGC if <code>true</code>, DGC is enabled to the object * on this server endpoint * @param keepAlive if <code>true</code>, the VM is kept alive * while the object (exported via this exporter) remains * exported * @param id an object identifier or <code>null</code> * @throws NullPointerException if <code>se</code> or <code>ilf</code> * is <code>null</code> **/ public BasicJeriExporter(ServerEndpoint se, InvocationLayerFactory ilf, boolean enableDGC, boolean keepAlive, Uuid id) { if (se == null || ilf == null) { throw new NullPointerException(); } this.se = se; this.ilf = ilf; this.id = ((id == null) ? UuidFactory.generate() : id); this.enableDGC = enableDGC; this.keepAlive = keepAlive; } /** * Returns the server endpoint for this exporter. * * @return the server endpoint **/ public ServerEndpoint getServerEndpoint() { return se; } /** * Returns the <code>InvocationLayerFactory</code> for this * exporter. * * @return the factory **/ public InvocationLayerFactory getInvocationLayerFactory() { return ilf; } /** * Returns <code>true</code> if DGC is enabled on the server endpoint to * the object corresponding to this exporter; otherwise * returns <code>false</code>. * * @return <code>true</code> if DGC is enabled; * <code>false</code> otherwise **/ public boolean getEnableDGC() { return enableDGC; } /** * Returns <code>true</code> if the virtual machine is kept alive while * the object corresponding to this exporter is exported; otherwise * returns <code>false</code>. * * @return <code>true</code> if VM is kept alive while object is * exported; <code>false</code> otherwise **/ public boolean getKeepAlive() { return keepAlive; } /** * Returns the object identifier for this exporter. * * @return the object identifier **/ public Uuid getObjectIdentifier() { return id; } /** * Exports the specified remote object and returns a proxy for the * remote object. This method cannot be called more than once to * export a remote object or an <code>IllegalStateException</code> will * be thrown. * * <p>A {@link BasicObjectEndpoint} instance is created with the object * identifier of this exporter, the {@link Endpoint} obtained from * listening on the server endpoint (see below), and the * <code>enableDGC</code> flag of this exporter. * * <p>The client <code>Endpoint</code> for the * <code>BasicObjectEndpoint</code> is obtained by invoking {@link * ServerEndpoint#enumerateListenEndpoints enumerateListenEndpoints} on * the server endpoint with a {@link ServerEndpoint.ListenContext} * whose {@link ServerEndpoint.ListenContext#addListenEndpoint * addListenEndpoint} method is implemented as follows: <ul> * * <li>Invokes {@link ServerEndpoint.ListenEndpoint#checkPermissions * checkPermissions} on the supplied listen endpoint. * * <li>If the supplied listen endpoint has the same class and is equal * to another listen endpoint that has already been listened on, * returns the {@link ServerEndpoint.ListenCookie} corresponding to the * previous <code>listen</code> operation. Otherwise, it creates a * {@link RequestDispatcher} to handle inbound requests dispatched by * the listen endpoint, invokes {@link * ServerEndpoint.ListenEndpoint#listen listen} on the listen endpoint * (passing the request dispatcher) within an action passed to the * {@link Security#doPrivileged Security.doPrivileged} method, and * returns the <code>ServerEndpoint.ListenCookie</code> obtained by * invoking {@link ServerEndpoint.ListenHandle#getCookie getCookie} on * the {@link ServerEndpoint.ListenHandle} returned from the * <code>listen</code> invocation. * </ul> * * <p>A <code>RequestDispatcher</code> for a listen endpoint handles a * dispatched inbound request (when its {@link * RequestDispatcher#dispatch dispatch} method is invoked) as follows. * The request dispatcher reads the object identifer of the target * object being invoked by invoking {@link UuidFactory#read * UuidFactory.read} on the request input stream of the inbound * request. If no exported object corresponds to the object identifier * read, it closes the request input stream, writes <code>0x00</code> * to the response output stream, and closes the response output * stream. Otherwise, it writes <code>0x01</code> to the response * output stream, and invokes the {@link InvocationDispatcher#dispatch * dispatch} method on the invocation dispatcher passing the target * object, the inbound request, and the server context collection (see * below). * * <p>A proxy and an invocation dispatcher are created by * calling the {@link InvocationLayerFactory#createInstances * createInstances} method of this exporter's invocation layer factory, * passing the remote object, the <code>BasicObjectEndpoint</code>, and * the server endpoint (as the {@link ServerCapabilities}). The proxy * is returned by this method. The invocation dispatcher is called for * each incoming remote call to this exporter's object identifier * received from this exporter's server endpoint, passing the remote * object and the {@link InboundRequest} received from the transport * layer. * * <p>Each call to the invocation dispatcher's {@link * InvocationDispatcher#dispatch dispatch} method is invoked with * the following thread context: * <ul> * <li><code>dispatch</code> is invoked in a {@link * PrivilegedAction} wrapped by a {@link SecurityContext} * obtained when this method was invoked, with the {@link * AccessControlContext} of that <code>SecurityContext</code> * in effect. * <li>The context class loader is the context class loader * in effect when this method was invoked. * <li>Each call to the dispatcher is made using {@link * ServerContext#doWithServerContext * ServerContext.doWithServerContext} with a server context * collection that is an unmodifiable view of the context * collection populated by invoking the {@link * InboundRequest#populateContext populateContext} method on the * inbound request passing a modifiable collection. The invocation * dispatcher's {@link InvocationDispatcher#dispatch dispatch} * method is invoked with the <code>impl</code>, the inbound * request, and that modifiable server context collection. * </ul> * * <p>There is no replacement of the proxy for the implementation * object during marshalling; either the proxy must be passed * explicitly in a remote call, or the implementation class must be * serializable and have a <code>writeReplace</code> method that * returns the proxy. * * @throws ExportException if an object is already exported * with the same object identifier and server endpoint, or * the invocation layer factory cannot create a proxy or * invocation dispatcher, or some other problem occurs while * exporting the object * @throws NullPointerException {@inheritDoc} * @throws IllegalStateException {@inheritDoc} * @throws SecurityException if invoking the * <code>checkPermissions</code> method on any of the listen * endpoints throws a <code>SecurityException</code> **/ public synchronized Remote export(Remote impl) throws ExportException { /* * Check if object is already exported; disallow exporting more * than once via this exporter. */ if (used) { throw new IllegalStateException( "object already exported via this exporter"); } assert (entry == null); // ()s to work around javadoc bug /* * Export the remote object. */ entry = table.export(impl, se, enableDGC, keepAlive, id); used = true; /* * Create proxy and invocation dispatcher for the remote object. * * (Use package-private BasicObjectEndpoint constructor to suppress * DGC activity for this local live reference and to keep the impl * strongly referenced through it.) */ Remote proxy; InvocationLayerFactory.Instances inst = null; try { ImplContainer implContainer = new ImplContainer(impl); weakImplContainer = new WeakReference(implContainer); ObjectEndpoint oe = new BasicObjectEndpoint(entry.getEndpoint(), id, enableDGC, implContainer); inst = ilf.createInstances(impl, oe, se); entry.setInvocationDispatcher(inst.getInvocationDispatcher()); proxy = inst.getProxy(); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "export of {0} via {1} returns proxy {2}", new Object[]{ impl, this, proxy }); } } finally { if (inst == null) { unexport(true); } } return proxy; } /** * Unexports the remote object exported via the exporter's * {@link #export export} method such that incoming remote calls * to the object identifier in this exporter are no longer accepted * through the server endpoint in this exporter. * * <p>If <code>force</code> is <code>true</code>, the object * is forcibly unexported even if there are pending or in-progress remote * calls to the object identifier through the server endpoint. If * <code>force</code> is <code>false</code>, the object is only * unexported if there are no pending or in-progress remote calls to the * object identifier through the server endpoint. * * <p>The return value is <code>true</code> if the object is (or was * previously) unexported, and <code>false</code> if the object is still * exported. * * @throws IllegalStateException {@inheritDoc} **/ public synchronized boolean unexport(boolean force) { if (!used) { throw new IllegalStateException( "no object exported via this exporter"); } if (entry != null && entry.unexport(force)) { entry = null; ImplContainer implContainer = (ImplContainer) weakImplContainer.get(); if (implContainer != null) { implContainer.clearImpl(); } } boolean result = entry == null; if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "unexport on {0} returns {1}", new Object[]{ this, Boolean.valueOf(result) }); } return result; } /** * Returns the string representation for this exporter. * * @return the string representation for this exporter **/ public String toString() { return "BasicJeriExporter[" + se + "," + id + "]"; } /** * Container for an impl object. * * BasicJeriExporter, when exporting an impl, passes an impl container * to the package-private BasicObjectEndpoint constructor so that the * BasicObjectEndpoint can reference the impl strongly (through the * container) while the object is exported. The BasicJeriExporter * instance holds onto the impl container weakly so it won't prevent * the impl from being garbage collected; only the local stub that * references the BasicObjectEndpoint will prevent the impl from being * garbage collected. * * If the object is explicitly unexported via BasicJeriExporter, the * BasicJeriExporter instance clears the impl field (if the container * hasn't been garbage collected) so a reachable stub that references * the container (via the BasicObjectEndpoint in the stub) will not * prevent the impl from being garbage collected. **/ private static class ImplContainer { private Object impl; ImplContainer(Object impl) { this.impl = impl; } void clearImpl() { impl = null; } } }