/*
* 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.iiop;
import java.lang.ref.WeakReference;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.ExportException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.rmi.PortableRemoteObject;
import javax.rmi.CORBA.Stub;
import javax.rmi.CORBA.Util;
import net.jini.export.Exporter;
import org.omg.CORBA.ORB;
/**
* An <code>IiopExporter</code> can be used to export a single remote object to
* the <a href="http://java.sun.com/j2se/1.4/docs/guide/rmi-iiop/">RMI-IIOP</a>
* runtime. It acts as an adapter between the {@link Exporter} interface and
* existing RMI-IIOP (un)export/utility APIs provided by the {@link javax.rmi}
* and {@link javax.rmi.CORBA} packages.
*
* <p>Note: although this exporter internally makes use of {@link
* javax.rmi.PortableRemoteObject}, it cannot be used to export remote objects
* over JRMP (as <code>PortableRemoteObject</code> can). For JRMP exports,
* {@link net.jini.jrmp.JrmpExporter} should be used instead.
*
* @author Sun Microsystems, Inc.
* @since 2.0
*
* @com.sun.jini.impl
*
* <p>This implementation uses the {@link Logger} named
* <code>net.jini.iiop.IiopExporter</code> to log
* information at the following levels:
*
* <table summary="Describes what is logged by IiopExporter 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 IiopExporter implements Exporter {
private static final Logger logger =
Logger.getLogger("net.jini.iiop.IiopExporter");
private ORB orb;
private WeakReference ref;
/**
* Creates a new exporter which can be used to export a remote object over
* IIOP. The stub resulting from an export of a remote object with this
* exporter will not be connected to any {@link ORB}.
*/
public IiopExporter() {
}
/**
* Creates a new exporter which can be used to export a remote object over
* IIOP. If the given {@link ORB} is non-<code>null</code>, then the stub
* resulting from an export of a remote object with this exporter will be
* connected to it; otherwise, the stub will be left unconnected.
*
* @param orb if non-<code>null</code>, ORB to which to connect stub of
* exported object
*/
public IiopExporter(ORB orb) {
this.orb = orb;
}
/**
* Exports a remote object, <code>impl</code>, to the RMI-IIOP runtime and
* returns a proxy (stub) for the remote object. If an {@link ORB} was
* specified during construction of this exporter, then the returned
* RMI-IIOP stub will be connected to it. This method cannot be called
* more than once to export a remote object or an
* {@link IllegalStateException} will be thrown.
*
* @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");
} else if (getTieClass(impl.getClass()) == null) {
throw new ExportException("tie class unavailable");
}
ref = new WeakReference(impl);
try {
PortableRemoteObject.exportObject(impl);
Remote proxy = PortableRemoteObject.toStub(impl);
if (orb != null) {
((Stub) proxy).connect(orb);
}
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
* {@link #export} method such that the object can no longer
* accept incoming remote calls that were possible as a result of
* exporting via this exporter.
*
* <p>This method unexports the remote object via a call to
* {@link PortableRemoteObject#unexportObject}, which only supports the
* equivalent of a "forced" unexport (i.e., one in which the object is
* unexported regardless of the presence of pending or in-progress calls).
* Hence, this method will not consult the value of <code>force</code>,
* and will always attempt a "forced" unexport of the remote object,
* returning <code>true</code> upon normal completion.
*
* @param force ignored value (normally indicates whether or not to
* unexport the object in the presence of pending or in-progress
* calls, but this exporter does not support "unforced" unexports)
* @return <code>true</code>
* @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) {
try {
PortableRemoteObject.unexportObject(impl);
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "unexport on {0} returns {1}",
new Object[]{ this, Boolean.TRUE });
}
} catch (NoSuchObjectException ex) {
}
}
return true;
}
/**
* Returns the string representation for this exporter.
*
* @return the string representation for this exporter
*/
public String toString() {
return (orb != null) ? "IiopExporter[" + orb + "]" : "IiopExporter[]";
}
/**
* Returns tie class for the given remote object class, or null if none
* available.
*/
private static Class getTieClass(Class implClass) {
// based on com.sun.corba.se.internal.util.Utility.loadTie()
// REMIND: cache results?
String implClassName = implClass.getName();
int i = implClassName.indexOf('$');
if (i < 0) {
i = implClassName.lastIndexOf('.');
}
String tieClassName = (i > 0) ?
implClassName.substring(0, i + 1) + "_" +
implClassName.substring(i + 1) + "_Tie" :
"_" + implClassName + "_Tie";
// workaround for 4632973
ArrayList names = new ArrayList(2);
names.add(tieClassName);
if (tieClassName.startsWith("java.") ||
tieClassName.startsWith("com.sun.") ||
tieClassName.startsWith("net.jini.") ||
tieClassName.startsWith("jini.") ||
tieClassName.startsWith("javax."))
{
names.add("org.omg.stub." + tieClassName);
}
ClassLoader loader = implClass.getClassLoader();
String codebase = Util.getCodebase(implClass);
for (Iterator iter = names.iterator(); iter.hasNext();) {
tieClassName = (String) iter.next();
try {
return Util.loadClass(tieClassName, codebase, loader);
} catch (ClassNotFoundException ex) {
}
// second attempt futile, but try anyway to mimic Utility.loadTie()
if (loader != null) {
try {
return loader.loadClass(tieClassName);
} catch (ClassNotFoundException ex) {
}
}
}
Class implSuper = implClass.getSuperclass();
return (implSuper != null &&
implSuper != PortableRemoteObject.class &&
implSuper != Object.class) ? getTieClass(implSuper) : null;
}
}