/* * 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.jrmp; import java.lang.ref.WeakReference; import java.rmi.NoSuchObjectException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.activation.Activatable; import java.rmi.activation.ActivationID; import java.rmi.server.ExportException; import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import java.rmi.server.UnicastRemoteObject; import java.util.logging.Level; import java.util.logging.Logger; import net.jini.export.Exporter; /** * A <code>JrmpExporter</code> contains the information necessary to export a * single remote object to the * <a href="http://java.sun.com/j2se/1.4/docs/guide/rmi/">JRMP</a> runtime. It * acts as an adapter between existing methods for (un)exporting remote objects * over JRMP (e.g, {@link UnicastRemoteObject#exportObject(Remote)}, * {@link Activatable#exportObject(Remote, ActivationID, int)}) and the * {@link Exporter} interface. * * <p>An object exported via a <code>JrmpExporter</code> can customize the * following properties that govern invocation behavior and other * characteristics of the exported remote object and its stub: * <ul> * * <li><code>port</code>: the port number for the {@link java.net.ServerSocket} * on which the JRMP runtime will listen for incoming calls to the exported * object. * * <li>{@link RMIClientSocketFactory}: the factory object used by client stubs * to create {@link java.net.Socket} objects over which to issue calls to the * exported object. If <code>null</code>, then the global JRMP socket factory * will be used (i.e., the value returned by * {@link java.rmi.server.RMISocketFactory#getSocketFactory}), or if there * isn't one set, then the default global JRMP socket factory will be used * (i.e., the value returned by * {@link java.rmi.server.RMISocketFactory#getDefaultSocketFactory}). * * <li>{@link RMIServerSocketFactory}: the factory object used by the JRMP * runtime to create <code>ServerSocket</code> objects over which to receive * calls to the exported object. If <code>null</code>, then the global JRMP * socket factory will be used, or if there isn't one set, then the default * global JRMP socket factory will be used. The default global JRMP socket * factory returns a <code>ServerSocket</code> for an anonymous port if passed * a port number of zero. * * <li>{@link ActivationID}: the <code>ActivationID</code> identifying this * remote object to the activation system, or <code>null</code> if the remote * object is not to be exported as activatable. * </ul> * * <p>This exporter is a front-end adapter on top of * <code>UnicastRemoteObject</code> and <code>Activatable</code>; exporting * remote objects through this exporter is equivalent to doing so directly via * the various <code>exportObject</code> methods defined by the aforementioned * classes. * * @author Sun Microsystems, Inc. * @since 2.0 * * @com.sun.jini.impl * * <p>This implementation uses the {@link Logger} named * <code>net.jini.jrmp.JrmpExporter</code> to log * information at the following levels: * * <table summary="Describes what is logged by JrmpExporter at various * logging levels" border=1 cellpadding=5> * * <tr> <th scope="col"> Level <th scope="col"> Description * * <tr> <td> {@link Level#FINE FINE} <td> successful export of object * * <tr> <td> {@link Level#FINE FINE} <td> attempted unexport of object * * </table> */ public final class JrmpExporter implements Exporter { private static final Logger logger = Logger.getLogger("net.jini.jrmp.JrmpExporter"); private final int port; private final RMIClientSocketFactory csf; private final RMIServerSocketFactory ssf; private final ActivationID id; private WeakReference ref; /** * Creates an exporter for a non-activatable JRMP "unicast" remote object * that exports on an anonymous port and does not use custom socket * factories. */ public JrmpExporter() { this(0); } /** * Creates an exporter for a non-activatable JRMP "unicast" remote object * that exports on the specified TCP port and does not use custom socket * factories. * * @param port number of the port on which an exported object will * receive calls (if zero, an anonymous port will be chosen) */ public JrmpExporter(int port) { this(port, null, null); } /** * Creates an exporter for a non-activatable JRMP "unicast" remote object * that exports on the specified TCP port and uses sockets created by the * given custom socket factories. * * @param port number of the port on which an exported object will * receive calls (if zero, an anonymous port will be chosen) * @param csf client-side socket factory (if null, the global JRMP socket * factory or, if necessary, the default global JRMP socket * factory will be used to create client-side sockets) * @param ssf server-side socket factory (if null, the global JRMP socket * factory or, if necessary, the default global JRMP socket * factory will be used to create server-side sockets) */ public JrmpExporter(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) { this.port = port; this.csf = csf; this.ssf = ssf; id = null; } /** * Creates an exporter for an activatable JRMP remote object with the given * activation ID that exports on the specified TCP port and does not use * custom socket factories. * * @param id activation ID associated with the object to export * @param port number of the port on which an exported object will * receive calls (if zero, an anonymous port will be chosen) * @throws NullPointerException if <code>id</code> is <code>null</code> */ public JrmpExporter(ActivationID id, int port) { this(id, port, null, null); } /** * Creates an exporter for an activatable JRMP remote object with the given * activation ID that exports on the specified TCP port and uses sockets * created by the given custom socket factories. * * @param id activation ID associated with the object to export * @param port number of the port on which an exported object will * receive calls (if zero, an anonymous port will be chosen) * @param csf client-side socket factory (if null, the global JRMP socket * factory or, if necessary, the default global JRMP socket * factory will be used to create client-side sockets) * @param ssf server-side socket factory (if null, the global JRMP socket * factory or, if necessary, the default global JRMP socket * factory will be used to create server-side sockets) * @throws NullPointerException if <code>id</code> is <code>null</code> */ public JrmpExporter(ActivationID id, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) { if (id == null) { throw new NullPointerException(); } this.id = id; this.port = port; this.csf = csf; this.ssf = ssf; } /** * Returns the port used by this exporter, or zero if an anonymous port is * used. * * @return port number, or zero if anonymous */ public int getPort() { return port; } /** * Returns the client socket factory for this exporter, or * <code>null</code> if none (in which case {@link java.net.Socket} objects * are created directly). * * @return client socket factory, or <code>null</code> if none */ public RMIClientSocketFactory getClientSocketFactory() { return csf; } /** * Returns the server socket factory for this exporter, or * <code>null</code> if none (in which case * <code>java.net.ServerSocket</code> objects are created directly). * * @return server socket factory, or <code>null</code> if none */ public RMIServerSocketFactory getServerSocketFactory() { return ssf; } /** * Returns the activation ID associated with the object exported by this * exporter, or <code>null</code> if activation is not being used with this * exporter. * * @return activation ID, or <code>null</code> if none */ public ActivationID getActivationID() { return id; } /** * Exports a remote object, <code>impl</code>, to the JRMP runtime and * returns a proxy (stub) 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>If this exporter was created with a constructor that accepted a * <code>java.rmi.activation.ActivationID</code>, then calling this method * is equivalent to invoking * <code>java.rmi.activation.Activatable.exportObject</code> with the * appropriate impl, <code>ActivationID</code>, port, * <code>RMIClientSocketFactory</code>, and * <code>RMIServerSocketFactory</code> values. Otherwise, calling this * method is equivalent to invoking * <code>java.rmi.server.UnicastRemoteObject.exportObject</code> with the * appropriate impl, port, <code>RMIClientSocketFactory</code>, and * <code>RMIServerSocketFactory</code> values. * * @throws NullPointerException {@inheritDoc} * @throws IllegalStateException {@inheritDoc} */ public synchronized Remote export(Remote impl) throws ExportException { if (impl == null) { throw new NullPointerException(); } else if (ref != null) { throw new IllegalStateException( "object already exported via this exporter"); } ref = new WeakReference(impl); try { Remote proxy = (id != null) ? Activatable.exportObject(impl, id, port, csf, ssf) : UnicastRemoteObject.exportObject(impl, port, csf, ssf); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "export of {0} via {1} returns proxy {2}", new Object[]{ impl, this, proxy }); } return proxy; } catch (ExportException ex) { throw ex; } catch (RemoteException ex) { throw new ExportException("export failed", ex); } } /** * Unexports the remote object exported via this exporter's * <code>export</code> method such that the object can no longer * accept incoming remote calls that were possible as a result of * exporting via this exporter. * * <p>If <code>force</code> is <code>true</code>, the object is forcibly * unexported even if there are pending or in progress calls to the remote * object via this exporter. If <code>force</code> is <code>false</code>, * the object is only unexported if there are no pending or in progress * calls to the remote object via this exporter. This method is equivalent * to calling <code>java.rmi.activation.Activatable.unexportObject</code> * or <code>java.rmi.server.UnicastRemoteObject.unexportObject</code> and * passing the "impl" that was previously passed to this exporter's * <code>export</code> method, depending on whether or not this exporter * was created with a constructor that accepted a * <code>java.rmi.activation.ActivationID</code> value. * * <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 (ref == null) { throw new IllegalStateException( "an object has not been exported via this exporter"); } Remote impl = (Remote) ref.get(); if (impl == null) { return true; } try { boolean result = (id != null) ? Activatable.unexportObject(impl, force) : UnicastRemoteObject.unexportObject(impl, force); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "unexport on {0} returns {1}", new Object[]{ this, Boolean.valueOf(result) }); } return result; } catch (NoSuchObjectException ex) { return true; } } /** * Returns the string representation for this exporter. * * @return the string representation for this exporter */ public String toString() { return (id != null) ? "JrmpExporter[" + id + "," + port + "," + csf + "," + ssf + "]" : "JrmpExporter[" + port + "," + csf + "," + ssf + "]"; } }