/*
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.transport;
import java.io.IOException;
import java.io.ObjectOutput;
import java.rmi.MarshalException;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.LogStream;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteServer;
import java.rmi.server.ServerNotActiveException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import sun.rmi.runtime.Log;
import sun.rmi.server.Dispatcher;
import sun.rmi.server.UnicastServerRef;
Transport abstraction for enabling communication between different
VMs.
Author: Ann Wollrath
/**
* Transport abstraction for enabling communication between different
* VMs.
*
* @author Ann Wollrath
*/
@SuppressWarnings("deprecation")
public abstract class Transport {
"transport" package log level /** "transport" package log level */
static final int logLevel = LogStream.parseLevel(getLogLevel());
private static String getLogLevel() {
return java.security.AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.rmi.transport.logLevel"));
}
/* transport package log */
static final Log transportLog =
Log.getLog("sun.rmi.transport.misc", "transport", Transport.logLevel);
References the current transport when a call is being serviced /** References the current transport when a call is being serviced */
private static final ThreadLocal<Transport> currentTransport = new ThreadLocal<>();
ObjID for DGCImpl /** ObjID for DGCImpl */
private static final ObjID dgcID = new ObjID(ObjID.DGC_ID);
AccessControlContext for setting context ClassLoader /** AccessControlContext for setting context ClassLoader */
private static final AccessControlContext SETCCL_ACC;
static {
Permissions perms = new Permissions();
perms.add(new RuntimePermission("setContextClassLoader"));
ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };
SETCCL_ACC = new AccessControlContext(pd);
}
Returns a Channel that generates connections to the
endpoint ep. A Channel is an object that creates and
manages connections of a particular type to some particular
address space.
Params: - ep – the endpoint to which connections will be generated.
Returns: the channel or null if the transport cannot
generate connections to this endpoint
/**
* Returns a <I>Channel</I> that generates connections to the
* endpoint <I>ep</I>. A Channel is an object that creates and
* manages connections of a particular type to some particular
* address space.
* @param ep the endpoint to which connections will be generated.
* @return the channel or null if the transport cannot
* generate connections to this endpoint
*/
public abstract Channel getChannel(Endpoint ep);
Removes the Channel that generates connections to the
endpoint ep.
/**
* Removes the <I>Channel</I> that generates connections to the
* endpoint <I>ep</I>.
*/
public abstract void free(Endpoint ep);
Export the object so that it can accept incoming calls.
/**
* Export the object so that it can accept incoming calls.
*/
public void exportObject(Target target) throws RemoteException {
target.setExportedTransport(this);
ObjectTable.putTarget(target);
}
Invoked when an object that was exported on this transport has
become unexported, either by being garbage collected or by
being explicitly unexported.
/**
* Invoked when an object that was exported on this transport has
* become unexported, either by being garbage collected or by
* being explicitly unexported.
**/
protected void targetUnexported() { }
Returns the current transport if a call is being serviced, otherwise
returns null.
/**
* Returns the current transport if a call is being serviced, otherwise
* returns null.
**/
static Transport currentTransport() {
return currentTransport.get();
}
Verify that the current access control context has permission to accept
the connection being dispatched by the current thread. The current
access control context is passed as a parameter to avoid the overhead of
an additional call to AccessController.getContext.
/**
* Verify that the current access control context has permission to accept
* the connection being dispatched by the current thread. The current
* access control context is passed as a parameter to avoid the overhead of
* an additional call to AccessController.getContext.
*/
protected abstract void checkAcceptPermission(AccessControlContext acc);
Sets the context class loader for the current thread.
/**
* Sets the context class loader for the current thread.
*/
private static void setContextClassLoader(ClassLoader ccl) {
AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
Thread.currentThread().setContextClassLoader(ccl);
return null;
}, SETCCL_ACC);
}
Service an incoming remote call. When a message arrives on the
connection indicating the beginning of a remote call, the
threads are required to call the serviceCall method of
their transport. The default implementation of this method
locates and calls the dispatcher object. Ordinarily a
transport implementation will not need to override this method.
At the entry to tr.serviceCall(conn), the connection's
input stream is positioned at the start of the incoming
message. The serviceCall method processes the incoming
remote invocation and sends the result on the connection's
output stream. If it returns "true", then the remote
invocation was processed without error and the transport can
cache the connection. If it returns "false", a protocol error
occurred during the call, and the transport should destroy the
connection.
/**
* Service an incoming remote call. When a message arrives on the
* connection indicating the beginning of a remote call, the
* threads are required to call the <I>serviceCall</I> method of
* their transport. The default implementation of this method
* locates and calls the dispatcher object. Ordinarily a
* transport implementation will not need to override this method.
* At the entry to <I>tr.serviceCall(conn)</I>, the connection's
* input stream is positioned at the start of the incoming
* message. The <I>serviceCall</I> method processes the incoming
* remote invocation and sends the result on the connection's
* output stream. If it returns "true", then the remote
* invocation was processed without error and the transport can
* cache the connection. If it returns "false", a protocol error
* occurred during the call, and the transport should destroy the
* connection.
*/
public boolean serviceCall(final RemoteCall call) {
try {
/* read object id */
final Remote impl;
ObjID id;
try {
id = ObjID.read(call.getInputStream());
} catch (java.io.IOException e) {
throw new MarshalException("unable to read objID", e);
}
/* get the remote object */
Transport transport = id.equals(dgcID) ? null : this;
Target target =
ObjectTable.getTarget(new ObjectEndpoint(id, transport));
if (target == null || (impl = target.getImpl()) == null) {
throw new NoSuchObjectException("no such object in table");
}
final Dispatcher disp = target.getDispatcher();
target.incrementCallCount();
try {
/* call the dispatcher */
transportLog.log(Log.VERBOSE, "call dispatcher");
final AccessControlContext acc =
target.getAccessControlContext();
ClassLoader ccl = target.getContextClassLoader();
ClassLoader savedCcl = Thread.currentThread().getContextClassLoader();
try {
setContextClassLoader(ccl);
currentTransport.set(this);
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
public Void run() throws IOException {
checkAcceptPermission(acc);
disp.dispatch(impl, call);
return null;
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (IOException) pae.getException();
}
} finally {
setContextClassLoader(savedCcl);
currentTransport.set(null);
}
} catch (IOException ex) {
transportLog.log(Log.BRIEF,
"exception thrown by dispatcher: ", ex);
return false;
} finally {
target.decrementCallCount();
}
} catch (RemoteException e) {
// if calls are being logged, write out exception
if (UnicastServerRef.callLog.isLoggable(Log.BRIEF)) {
// include client host name if possible
String clientHost = "";
try {
clientHost = "[" +
RemoteServer.getClientHost() + "] ";
} catch (ServerNotActiveException ex) {
}
String message = clientHost + "exception: ";
UnicastServerRef.callLog.log(Log.BRIEF, message, e);
}
/* We will get a RemoteException if either a) the objID is
* not readable, b) the target is not in the object table, or
* c) the object is in the midst of being unexported (note:
* NoSuchObjectException is thrown by the incrementCallCount
* method if the object is being unexported). Here it is
* relatively safe to marshal an exception to the client
* since the client will not have seen a return value yet.
*/
try {
ObjectOutput out = call.getResultStream(false);
UnicastServerRef.clearStackTraces(e);
out.writeObject(e);
call.releaseOutputStream();
} catch (IOException ie) {
transportLog.log(Log.BRIEF,
"exception thrown marshalling exception: ", ie);
return false;
}
}
return true;
}
}