259 lines
9.9 KiB
Java
259 lines
9.9 KiB
Java
/*
|
|
* 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
|
|
*/
|
|
@SuppressWarnings("deprecation")
|
|
public abstract class Transport {
|
|
|
|
/** "transport" package log level */
|
|
static final int logLevel = LogStream.parseLevel(getLogLevel());
|
|
|
|
private static String getLogLevel() {
|
|
return java.security.AccessController.doPrivileged(
|
|
new sun.security.action.GetPropertyAction("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 */
|
|
private static final ThreadLocal<Transport> currentTransport = new ThreadLocal<>();
|
|
|
|
/** ObjID for DGCImpl */
|
|
private static final ObjID dgcID = new ObjID(ObjID.DGC_ID);
|
|
|
|
/** 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 <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 <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.
|
|
*/
|
|
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.
|
|
**/
|
|
protected void targetUnexported() { }
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
protected abstract void checkAcceptPermission(AccessControlContext acc);
|
|
|
|
/**
|
|
* 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 <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;
|
|
}
|
|
}
|