|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.rmi.server; |
|
|
|
import java.io.IOException; |
|
import java.io.ObjectInput; |
|
import java.io.ObjectInputStream; |
|
import java.io.ObjectOutput; |
|
import java.io.ObjectStreamClass; |
|
import java.lang.reflect.InvocationTargetException; |
|
import java.lang.reflect.Method; |
|
import java.rmi.AccessException; |
|
import java.rmi.MarshalException; |
|
import java.rmi.Remote; |
|
import java.rmi.RemoteException; |
|
import java.rmi.ServerError; |
|
import java.rmi.ServerException; |
|
import java.rmi.UnmarshalException; |
|
import java.rmi.server.ExportException; |
|
import java.rmi.server.Operation; |
|
import java.rmi.server.RemoteCall; |
|
import java.rmi.server.RemoteRef; |
|
import java.rmi.server.RemoteStub; |
|
import java.rmi.server.ServerNotActiveException; |
|
import java.rmi.server.ServerRef; |
|
import java.rmi.server.Skeleton; |
|
import java.rmi.server.SkeletonNotFoundException; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.util.Collections; |
|
import java.util.Date; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
import java.util.WeakHashMap; |
|
import java.util.concurrent.atomic.AtomicInteger; |
|
import sun.misc.ObjectInputFilter; |
|
import sun.rmi.runtime.Log; |
|
import sun.rmi.transport.LiveRef; |
|
import sun.rmi.transport.StreamRemoteCall; |
|
import sun.rmi.transport.Target; |
|
import sun.rmi.transport.tcp.TCPTransport; |
|
import sun.security.action.GetBooleanAction; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("deprecation") |
|
public class UnicastServerRef extends UnicastRef |
|
implements ServerRef, Dispatcher |
|
{ |
|
|
|
public static final boolean logCalls = AccessController.doPrivileged( |
|
new GetBooleanAction("java.rmi.server.logCalls")); |
|
|
|
|
|
public static final Log callLog = |
|
Log.getLog("sun.rmi.server.call", "RMI", logCalls); |
|
|
|
|
|
private static final long serialVersionUID = -7384275867073752268L; |
|
|
|
|
|
private static final boolean wantExceptionLog = |
|
AccessController.doPrivileged( |
|
new GetBooleanAction("sun.rmi.server.exceptionTrace")); |
|
|
|
private boolean forceStubUse = false; |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final boolean suppressStackTraces = |
|
AccessController.doPrivileged( |
|
new GetBooleanAction( |
|
"sun.rmi.server.suppressStackTraces")); |
|
|
|
|
|
|
|
|
|
*/ |
|
private transient Skeleton skel; |
|
|
|
|
|
private final transient ObjectInputFilter filter; |
|
|
|
|
|
private transient Map<Long,Method> hashToMethod_Map = null; |
|
|
|
|
|
|
|
|
|
**/ |
|
private static final WeakClassHashMap<Map<Long,Method>> hashToMethod_Maps = |
|
new HashToMethod_Maps(); |
|
|
|
|
|
private static final Map<Class<?>,?> withoutSkeletons = |
|
Collections.synchronizedMap(new WeakHashMap<Class<?>,Void>()); |
|
|
|
private final AtomicInteger methodCallIDCount = new AtomicInteger(0); |
|
|
|
|
|
|
|
|
|
*/ |
|
public UnicastServerRef() { |
|
this.filter = null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public UnicastServerRef(LiveRef ref) { |
|
super(ref); |
|
this.filter = null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public UnicastServerRef(LiveRef ref, ObjectInputFilter filter) { |
|
super(ref); |
|
this.filter = filter; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public UnicastServerRef(int port) { |
|
super(new LiveRef(port)); |
|
this.filter = null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**/ |
|
public UnicastServerRef(boolean forceStubUse) { |
|
this(0); |
|
this.forceStubUse = forceStubUse; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**/ |
|
public RemoteStub exportObject(Remote impl, Object data) |
|
throws RemoteException |
|
{ |
|
forceStubUse = true; |
|
return (RemoteStub) exportObject(impl, data, false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Remote exportObject(Remote impl, Object data, |
|
boolean permanent) |
|
throws RemoteException |
|
{ |
|
Class<?> implClass = impl.getClass(); |
|
Remote stub; |
|
|
|
try { |
|
stub = Util.createProxy(implClass, getClientRef(), forceStubUse); |
|
} catch (IllegalArgumentException e) { |
|
throw new ExportException( |
|
"remote object implements illegal remote interface", e); |
|
} |
|
if (stub instanceof RemoteStub) { |
|
setSkeleton(impl); |
|
} |
|
|
|
Target target = |
|
new Target(impl, this, stub, ref.getObjID(), permanent); |
|
ref.exportObject(target); |
|
hashToMethod_Map = hashToMethod_Maps.get(implClass); |
|
return stub; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getClientHost() throws ServerNotActiveException { |
|
return TCPTransport.getClientHost(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setSkeleton(Remote impl) throws RemoteException { |
|
if (!withoutSkeletons.containsKey(impl.getClass())) { |
|
try { |
|
skel = Util.createSkeleton(impl); |
|
} catch (SkeletonNotFoundException e) { |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
withoutSkeletons.put(impl.getClass(), null); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dispatch(Remote obj, RemoteCall call) throws IOException { |
|
// positive operation number in 1.1 stubs; |
|
|
|
int num; |
|
long op; |
|
|
|
try { |
|
|
|
ObjectInput in; |
|
try { |
|
in = call.getInputStream(); |
|
num = in.readInt(); |
|
} catch (Exception readEx) { |
|
throw new UnmarshalException("error unmarshalling call header", |
|
readEx); |
|
} |
|
if (skel != null) { |
|
|
|
oldDispatch(obj, call, num); |
|
return; |
|
|
|
} else if (num >= 0){ |
|
throw new UnmarshalException( |
|
"skeleton class not found but required for client version"); |
|
} |
|
try { |
|
op = in.readLong(); |
|
} catch (Exception readEx) { |
|
throw new UnmarshalException("error unmarshalling call header", |
|
readEx); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
MarshalInputStream marshalStream = (MarshalInputStream) in; |
|
marshalStream.skipDefaultResolveClass(); |
|
|
|
Method method = hashToMethod_Map.get(op); |
|
if (method == null) { |
|
throw new UnmarshalException("unrecognized method hash: " + |
|
"method not supported by remote object"); |
|
} |
|
|
|
|
|
logCall(obj, method); |
|
|
|
|
|
Object[] params = null; |
|
try { |
|
unmarshalCustomCallData(in); |
|
params = unmarshalParameters(obj, method, marshalStream); |
|
|
|
} catch (AccessException aex) { |
|
// For compatibility, AccessException is not wrapped in UnmarshalException |
|
|
|
((StreamRemoteCall) call).discardPendingRefs(); |
|
throw aex; |
|
} catch (java.io.IOException | ClassNotFoundException e) { |
|
|
|
((StreamRemoteCall) call).discardPendingRefs(); |
|
throw new UnmarshalException( |
|
"error unmarshalling arguments", e); |
|
} finally { |
|
call.releaseInputStream(); |
|
} |
|
|
|
|
|
Object result; |
|
try { |
|
result = method.invoke(obj, params); |
|
} catch (InvocationTargetException e) { |
|
throw e.getTargetException(); |
|
} |
|
|
|
|
|
try { |
|
ObjectOutput out = call.getResultStream(true); |
|
Class<?> rtype = method.getReturnType(); |
|
if (rtype != void.class) { |
|
marshalValue(rtype, result, out); |
|
} |
|
} catch (IOException ex) { |
|
throw new MarshalException("error marshalling return", ex); |
|
/* |
|
* This throw is problematic because when it is caught below, |
|
* we attempt to marshal it back to the client, but at this |
|
* point, a "normal return" has already been indicated, |
|
* so marshalling an exception will corrupt the stream. |
|
* This was the case with skeletons as well; there is no |
|
* immediately obvious solution without a protocol change. |
|
*/ |
|
} |
|
} catch (Throwable e) { |
|
Throwable origEx = e; |
|
logCallException(e); |
|
|
|
ObjectOutput out = call.getResultStream(false); |
|
if (e instanceof Error) { |
|
e = new ServerError( |
|
"Error occurred in server thread", (Error) e); |
|
} else if (e instanceof RemoteException) { |
|
e = new ServerException( |
|
"RemoteException occurred in server thread", |
|
(Exception) e); |
|
} |
|
if (suppressStackTraces) { |
|
clearStackTraces(e); |
|
} |
|
out.writeObject(e); |
|
|
|
// AccessExceptions should cause Transport.serviceCall |
|
|
|
if (origEx instanceof AccessException) { |
|
throw new IOException("Connection is not reusable", origEx); |
|
} |
|
} finally { |
|
call.releaseInputStream(); |
|
call.releaseOutputStream(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
protected void unmarshalCustomCallData(ObjectInput in) |
|
throws IOException, ClassNotFoundException { |
|
if (filter != null && |
|
in instanceof ObjectInputStream) { |
|
|
|
ObjectInputStream ois = (ObjectInputStream) in; |
|
|
|
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
|
@Override |
|
public Void run() { |
|
ObjectInputFilter.Config.setObjectInputFilter(ois, filter); |
|
return null; |
|
} |
|
}); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void oldDispatch(Remote obj, RemoteCall call, int op) |
|
throws Exception |
|
{ |
|
long hash; |
|
|
|
|
|
ObjectInput in; |
|
in = call.getInputStream(); |
|
try { |
|
Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel"); |
|
if (clazz.isAssignableFrom(skel.getClass())) { |
|
((MarshalInputStream)in).useCodebaseOnly(); |
|
} |
|
} catch (ClassNotFoundException ignore) { } |
|
|
|
try { |
|
hash = in.readLong(); |
|
} catch (Exception ioe) { |
|
throw new UnmarshalException("error unmarshalling call header", ioe); |
|
} |
|
|
|
|
|
Operation[] operations = skel.getOperations(); |
|
logCall(obj, op >= 0 && op < operations.length ? operations[op] : "op: " + op); |
|
unmarshalCustomCallData(in); |
|
|
|
skel.dispatch(obj, call, op, hash); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static void clearStackTraces(Throwable t) { |
|
StackTraceElement[] empty = new StackTraceElement[0]; |
|
while (t != null) { |
|
t.setStackTrace(empty); |
|
t = t.getCause(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void logCall(Remote obj, Object method) { |
|
if (callLog.isLoggable(Log.VERBOSE)) { |
|
String clientHost; |
|
try { |
|
clientHost = getClientHost(); |
|
} catch (ServerNotActiveException snae) { |
|
clientHost = "(local)"; |
|
} |
|
callLog.log(Log.VERBOSE, "[" + clientHost + ": " + |
|
obj.getClass().getName() + |
|
ref.getObjID().toString() + ": " + |
|
method + "]"); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void logCallException(Throwable e) { |
|
|
|
if (callLog.isLoggable(Log.BRIEF)) { |
|
String clientHost = ""; |
|
try { |
|
clientHost = "[" + getClientHost() + "] "; |
|
} catch (ServerNotActiveException snae) { |
|
} |
|
callLog.log(Log.BRIEF, clientHost + "exception: ", e); |
|
} |
|
|
|
|
|
if (wantExceptionLog) { |
|
java.io.PrintStream log = System.err; |
|
synchronized (log) { |
|
log.println(); |
|
log.println("Exception dispatching call to " + |
|
ref.getObjID() + " in thread \"" + |
|
Thread.currentThread().getName() + |
|
"\" at " + (new Date()) + ":"); |
|
e.printStackTrace(log); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getRefClass(ObjectOutput out) { |
|
return "UnicastServerRef"; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected RemoteRef getClientRef() { |
|
return new UnicastRef(ref); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void writeExternal(ObjectOutput out) throws IOException { |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void readExternal(ObjectInput in) |
|
throws IOException, ClassNotFoundException |
|
{ |
|
|
|
ref = null; |
|
skel = null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
**/ |
|
private static class HashToMethod_Maps |
|
extends WeakClassHashMap<Map<Long,Method>> |
|
{ |
|
HashToMethod_Maps() {} |
|
|
|
protected Map<Long,Method> computeValue(Class<?> remoteClass) { |
|
Map<Long,Method> map = new HashMap<>(); |
|
for (Class<?> cl = remoteClass; |
|
cl != null; |
|
cl = cl.getSuperclass()) |
|
{ |
|
for (Class<?> intf : cl.getInterfaces()) { |
|
if (Remote.class.isAssignableFrom(intf)) { |
|
for (Method method : intf.getMethods()) { |
|
final Method m = method; |
|
|
|
|
|
|
|
|
|
*/ |
|
AccessController.doPrivileged( |
|
new PrivilegedAction<Void>() { |
|
public Void run() { |
|
m.setAccessible(true); |
|
return null; |
|
} |
|
}); |
|
map.put(Util.computeMethodHash(m), m); |
|
} |
|
} |
|
} |
|
} |
|
return map; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private Object[] unmarshalParameters(Object obj, Method method, MarshalInputStream in) |
|
throws IOException, ClassNotFoundException { |
|
return (obj instanceof DeserializationChecker) ? |
|
unmarshalParametersChecked((DeserializationChecker)obj, method, in) : |
|
unmarshalParametersUnchecked(method, in); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private Object[] unmarshalParametersUnchecked(Method method, ObjectInput in) |
|
throws IOException, ClassNotFoundException { |
|
Class<?>[] types = method.getParameterTypes(); |
|
Object[] params = new Object[types.length]; |
|
for (int i = 0; i < types.length; i++) { |
|
params[i] = unmarshalValue(types[i], in); |
|
} |
|
return params; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private Object[] unmarshalParametersChecked( |
|
DeserializationChecker checker, |
|
Method method, MarshalInputStream in) |
|
throws IOException, ClassNotFoundException { |
|
int callID = methodCallIDCount.getAndIncrement(); |
|
MyChecker myChecker = new MyChecker(checker, method, callID); |
|
in.setStreamChecker(myChecker); |
|
try { |
|
Class<?>[] types = method.getParameterTypes(); |
|
Object[] values = new Object[types.length]; |
|
for (int i = 0; i < types.length; i++) { |
|
myChecker.setIndex(i); |
|
values[i] = unmarshalValue(types[i], in); |
|
} |
|
myChecker.end(callID); |
|
return values; |
|
} finally { |
|
in.setStreamChecker(null); |
|
} |
|
} |
|
|
|
private static class MyChecker implements MarshalInputStream.StreamChecker { |
|
private final DeserializationChecker descriptorCheck; |
|
private final Method method; |
|
private final int callID; |
|
private int parameterIndex; |
|
|
|
MyChecker(DeserializationChecker descriptorCheck, Method method, int callID) { |
|
this.descriptorCheck = descriptorCheck; |
|
this.method = method; |
|
this.callID = callID; |
|
} |
|
|
|
@Override |
|
public void validateDescriptor(ObjectStreamClass descriptor) { |
|
descriptorCheck.check(method, descriptor, parameterIndex, callID); |
|
} |
|
|
|
@Override |
|
public void checkProxyInterfaceNames(String[] ifaces) { |
|
descriptorCheck.checkProxyClass(method, ifaces, parameterIndex, callID); |
|
} |
|
|
|
void setIndex(int parameterIndex) { |
|
this.parameterIndex = parameterIndex; |
|
} |
|
|
|
void end(int callId) { |
|
descriptorCheck.end(callId); |
|
} |
|
} |
|
} |