1 /*
2 * Copyright 1996-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package sun.rmi.server;
27
28 import java.io.IOException;
29 import java.io.ObjectInput;
30 import java.io.ObjectOutput;
31 import java.io.PrintStream;
32 import java.lang.reflect.InvocationTargetException;
33 import java.lang.reflect.Method;
34 import java.rmi.MarshalException;
35 import java.rmi.Remote;
36 import java.rmi.RemoteException;
37 import java.rmi.ServerError;
38 import java.rmi.ServerException;
39 import java.rmi.UnmarshalException;
40 import java.rmi.server.ExportException;
41 import java.rmi.server.RemoteCall;
42 import java.rmi.server.RemoteRef;
43 import java.rmi.server.RemoteStub;
44 import java.rmi.server.ServerNotActiveException;
45 import java.rmi.server.ServerRef;
46 import java.rmi.server.Skeleton;
47 import java.rmi.server.SkeletonNotFoundException;
48 import java.security.AccessController;
49 import java.security.PrivilegedAction;
50 import java.util.Collections;
51 import java.util.Date;
52 import java.util.HashMap;
53 import java.util.Map;
54 import java.util.WeakHashMap;
55 import sun.rmi.runtime.Log;
56 import sun.rmi.transport.LiveRef;
57 import sun.rmi.transport.Target;
58 import sun.rmi.transport.tcp.TCPTransport;
59 import sun.security.action.GetBooleanAction;
60
61 /**
62 * UnicastServerRef implements the remote reference layer server-side
63 * behavior for remote objects exported with the "UnicastRef" reference
64 * type.
65 *
66 * @author Ann Wollrath
67 * @author Roger Riggs
68 * @author Peter Jones
69 */
70 public class UnicastServerRef extends UnicastRef
71 implements ServerRef, Dispatcher
72 {
73 /** value of server call log property */
74 public static final boolean logCalls = AccessController.doPrivileged(
75 new GetBooleanAction("java.rmi.server.logCalls"));
76
77 /** server call log */
78 public static final Log callLog =
79 Log.getLog("sun.rmi.server.call", "RMI", logCalls);
80
81 // use serialVersionUID from JDK 1.2.2 for interoperability
82 private static final long serialVersionUID = -7384275867073752268L;
83
84 /** flag to enable writing exceptions to System.err */
85 private static final boolean wantExceptionLog =
86 AccessController.doPrivileged(
87 new GetBooleanAction("sun.rmi.server.exceptionTrace"));
88
89 private boolean forceStubUse = false;
90
91 /**
92 * flag to remove server-side stack traces before marshalling
93 * exceptions thrown by remote invocations to this VM
94 */
95 private static final boolean suppressStackTraces =
96 AccessController.doPrivileged(
97 new GetBooleanAction(
98 "sun.rmi.server.suppressStackTraces"));
99
100 /**
101 * skeleton to dispatch remote calls through, for 1.1 stub protocol
102 * (may be null if stub class only uses 1.2 stub protocol)
103 */
104 private transient Skeleton skel;
105
106 /** maps method hash to Method object for each remote method */
107 private transient Map<Long,Method> hashToMethod_Map = null;
108
109 /**
110 * A weak hash map, mapping classes to hash maps that map method
111 * hashes to method objects.
112 **/
113 private static final WeakClassHashMap<Map<Long,Method>> hashToMethod_Maps =
114 new HashToMethod_Maps();
115
116 /** cache of impl classes that have no corresponding skeleton class */
117 private static final Map<Class<?>,?> withoutSkeletons =
118 Collections.synchronizedMap(new WeakHashMap<Class<?>,Void>());
119
120 /**
121 * Create a new (empty) Unicast server remote reference.
122 */
123 public UnicastServerRef() {
124 }
125
126 /**
127 * Construct a Unicast server remote reference for a specified
128 * liveRef.
129 */
130 public UnicastServerRef(LiveRef ref) {
131 super(ref);
132 }
133
134 /**
135 * Construct a Unicast server remote reference to be exported
136 * on the specified port.
137 */
138 public UnicastServerRef(int port) {
139 super(new LiveRef(port));
140 }
141
142 /**
143 * Constructs a UnicastServerRef to be exported on an
144 * anonymous port (i.e., 0) and that uses a pregenerated stub class
145 * (NOT a dynamic proxy instance) if 'forceStubUse' is 'true'.
146 *
147 * This constructor is only called by the method
148 * UnicastRemoteObject.exportObject(Remote) passing 'true' for
149 * 'forceStubUse'. The UnicastRemoteObject.exportObject(Remote) method
150 * returns RemoteStub, so it must ensure that the stub for the
151 * exported object is an instance of a pregenerated stub class that
152 * extends RemoteStub (instead of an instance of a dynamic proxy class
153 * which is not an instance of RemoteStub).
154 **/
155 public UnicastServerRef(boolean forceStubUse) {
156 this(0);
157 this.forceStubUse = forceStubUse;
158 }
159
160 /**
161 * With the addition of support for dynamic proxies as stubs, this
162 * method is obsolete because it returns RemoteStub instead of the more
163 * general Remote. It should not be called. It sets the
164 * 'forceStubUse' flag to true so that the stub for the exported object
165 * is forced to be an instance of the pregenerated stub class, which
166 * extends RemoteStub.
167 *
168 * Export this object, create the skeleton and stubs for this
169 * dispatcher. Create a stub based on the type of the impl,
170 * initialize it with the appropriate remote reference. Create the
171 * target defined by the impl, dispatcher (this) and stub.
172 * Export that target via the Ref.
173 **/
174 public RemoteStub exportObject(Remote impl, Object data)
175 throws RemoteException
176 {
177 forceStubUse = true;
178 return (RemoteStub) exportObject(impl, data, false);
179 }
180
181 /**
182 * Export this object, create the skeleton and stubs for this
183 * dispatcher. Create a stub based on the type of the impl,
184 * initialize it with the appropriate remote reference. Create the
185 * target defined by the impl, dispatcher (this) and stub.
186 * Export that target via the Ref.
187 */
188 public Remote exportObject(Remote impl, Object data,
189 boolean permanent)
190 throws RemoteException
191 {
192 Class implClass = impl.getClass();
193 Remote stub;
194
195 try {
196 stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
197 } catch (IllegalArgumentException e) {
198 throw new ExportException(
199 "remote object implements illegal remote interface", e);
200 }
201 if (stub instanceof RemoteStub) {
202 setSkeleton(impl);
203 }
204
205 Target target =
206 new Target(impl, this, stub, ref.getObjID(), permanent);
207 ref.exportObject(target);
208 hashToMethod_Map = hashToMethod_Maps.get(implClass);
209 return stub;
210 }
211
212 /**
213 * Return the hostname of the current client. When called from a
214 * thread actively handling a remote method invocation the
215 * hostname of the client is returned.
216 * @exception ServerNotActiveException If called outside of servicing
217 * a remote method invocation.
218 */
219 public String getClientHost() throws ServerNotActiveException {
220 return TCPTransport.getClientHost();
221 }
222
223 /**
224 * Discovers and sets the appropriate skeleton for the impl.
225 */
226 public void setSkeleton(Remote impl) throws RemoteException {
227 if (!withoutSkeletons.containsKey(impl.getClass())) {
228 try {
229 skel = Util.createSkeleton(impl);
230 } catch (SkeletonNotFoundException e) {
231 /*
232 * Ignore exception for skeleton class not found, because a
233 * skeleton class is not necessary with the 1.2 stub protocol.
234 * Remember that this impl's class does not have a skeleton
235 * class so we don't waste time searching for it again.
236 */
237 withoutSkeletons.put(impl.getClass(), null);
238 }
239 }
240 }
241
242 /**
243 * Call to dispatch to the remote object (on the server side).
244 * The up-call to the server and the marshalling of return result
245 * (or exception) should be handled before returning from this
246 * method.
247 * @param obj the target remote object for the call
248 * @param call the "remote call" from which operation and
249 * method arguments can be obtained.
250 * @exception IOException If unable to marshal return result or
251 * release input or output streams
252 */
253 public void dispatch(Remote obj, RemoteCall call) throws IOException {
254 // positive operation number in 1.1 stubs;
255 // negative version number in 1.2 stubs and beyond...
256 int num;
257 long op;
258
259 try {
260 // read remote call header
261 ObjectInput in;
262 try {
263 in = call.getInputStream();
264 num = in.readInt();
265 if (num >= 0) {
266 if (skel != null) {
267 oldDispatch(obj, call, num);
268 return;
269 } else {
270 throw new UnmarshalException(
271 "skeleton class not found but required " +
272 "for client version");
273 }
274 }
275 op = in.readLong();
276 } catch (Exception readEx) {
277 throw new UnmarshalException("error unmarshalling call header",
278 readEx);
279 }
280
281 /*
282 * Since only system classes (with null class loaders) will be on
283 * the execution stack during parameter unmarshalling for the 1.2
284 * stub protocol, tell the MarshalInputStream not to bother trying
285 * to resolve classes using its superclasses's default method of
286 * consulting the first non-null class loader on the stack.
287 */
288 MarshalInputStream marshalStream = (MarshalInputStream) in;
289 marshalStream.skipDefaultResolveClass();
290
291 Method method = hashToMethod_Map.get(op);
292 if (method == null) {
293 throw new UnmarshalException("unrecognized method hash: " +
294 "method not supported by remote object");
295 }
296
297 // if calls are being logged, write out object id and operation
298 logCall(obj, method);
299
300 // unmarshal parameters
301 Class[] types = method.getParameterTypes();
302 Object[] params = new Object[types.length];
303
304 try {
305 unmarshalCustomCallData(in);
306 for (int i = 0; i < types.length; i++) {
307 params[i] = unmarshalValue(types[i], in);
308 }
309 } catch (java.io.IOException e) {
310 throw new UnmarshalException(
311 "error unmarshalling arguments", e);
312 } catch (ClassNotFoundException e) {
313 throw new UnmarshalException(
314 "error unmarshalling arguments", e);
315 } finally {
316 call.releaseInputStream();
317 }
318
319 // make upcall on remote object
320 Object result;
321 try {
322 result = method.invoke(obj, params);
323 } catch (InvocationTargetException e) {
324 throw e.getTargetException();
325 }
326
327 // marshal return value
328 try {
329 ObjectOutput out = call.getResultStream(true);
330 Class rtype = method.getReturnType();
331 if (rtype != void.class) {
332 marshalValue(rtype, result, out);
333 }
334 } catch (IOException ex) {
335 throw new MarshalException("error marshalling return", ex);
336 /*
337 * This throw is problematic because when it is caught below,
338 * we attempt to marshal it back to the client, but at this
339 * point, a "normal return" has already been indicated,
340 * so marshalling an exception will corrupt the stream.
341 * This was the case with skeletons as well; there is no
342 * immediately obvious solution without a protocol change.
343 */
344 }
345 } catch (Throwable e) {
346 logCallException(e);
347
348 ObjectOutput out = call.getResultStream(false);
349 if (e instanceof Error) {
350 e = new ServerError(
351 "Error occurred in server thread", (Error) e);
352 } else if (e instanceof RemoteException) {
353 e = new ServerException(
354 "RemoteException occurred in server thread",
355 (Exception) e);
356 }
357 if (suppressStackTraces) {
358 clearStackTraces(e);
359 }
360 out.writeObject(e);
361 } finally {
362 call.releaseInputStream(); // in case skeleton doesn't
363 call.releaseOutputStream();
364 }
365 }
366
367 protected void unmarshalCustomCallData(ObjectInput in)
368 throws IOException, ClassNotFoundException
369 {}
370
371 /**
372 * Handle server-side dispatch using the RMI 1.1 stub/skeleton
373 * protocol, given a non-negative operation number that has
374 * already been read from the call stream.
375 *
376 * @param obj the target remote object for the call
377 * @param call the "remote call" from which operation and
378 * method arguments can be obtained.
379 * @param op the operation number
380 * @exception IOException if unable to marshal return result or
381 * release input or output streams
382 */
383 public void oldDispatch(Remote obj, RemoteCall call, int op)
384 throws IOException
385 {
386 long hash; // hash for matching stub with skeleton
387
388 try {
389 // read remote call header
390 ObjectInput in;
391 try {
392 in = call.getInputStream();
393 hash = in.readLong();
394 } catch (Exception readEx) {
395 throw new UnmarshalException("error unmarshalling call header",
396 readEx);
397 }
398
399 // if calls are being logged, write out object id and operation
400 logCall(obj, skel.getOperations()[op]);
401 unmarshalCustomCallData(in);
402 // dispatch to skeleton for remote object
403 skel.dispatch(obj, call, op, hash);
404
405 } catch (Throwable e) {
406 logCallException(e);
407
408 ObjectOutput out = call.getResultStream(false);
409 if (e instanceof Error) {
410 e = new ServerError(
411 "Error occurred in server thread", (Error) e);
412 } else if (e instanceof RemoteException) {
413 e = new ServerException(
414 "RemoteException occurred in server thread",
415 (Exception) e);
416 }
417 if (suppressStackTraces) {
418 clearStackTraces(e);
419 }
420 out.writeObject(e);
421 } finally {
422 call.releaseInputStream(); // in case skeleton doesn't
423 call.releaseOutputStream();
424 }
425 }
426
427 /**
428 * Clear the stack trace of the given Throwable by replacing it with
429 * an empty StackTraceElement array, and do the same for all of its
430 * chained causative exceptions.
431 */
432 public static void clearStackTraces(Throwable t) {
433 StackTraceElement[] empty = new StackTraceElement[0];
434 while (t != null) {
435 t.setStackTrace(empty);
436 t = t.getCause();
437 }
438 }
439
440 /**
441 * Log the details of an incoming call. The method parameter is either of
442 * type java.lang.reflect.Method or java.rmi.server.Operation.
443 */
444 private void logCall(Remote obj, Object method) {
445 if (callLog.isLoggable(Log.VERBOSE)) {
446 String clientHost;
447 try {
448 clientHost = getClientHost();
449 } catch (ServerNotActiveException snae) {
450 clientHost = "(local)"; // shouldn't happen
451 }
452 callLog.log(Log.VERBOSE, "[" + clientHost + ": " +
453 obj.getClass().getName() +
454 ref.getObjID().toString() + ": " +
455 method + "]");
456 }
457 }
458
459 /**
460 * Log the exception detail of an incoming call.
461 */
462 private void logCallException(Throwable e) {
463 // if calls are being logged, log them
464 if (callLog.isLoggable(Log.BRIEF)) {
465 String clientHost = "";
466 try {
467 clientHost = "[" + getClientHost() + "] ";
468 } catch (ServerNotActiveException snae) {
469 }
470 callLog.log(Log.BRIEF, clientHost + "exception: ", e);
471 }
472
473 // write exceptions (only) to System.err if desired
474 if (wantExceptionLog) {
475 java.io.PrintStream log = System.err;
476 synchronized (log) {
477 log.println();
478 log.println("Exception dispatching call to " +
479 ref.getObjID() + " in thread \"" +
480 Thread.currentThread().getName() +
481 "\" at " + (new Date()) + ":");
482 e.printStackTrace(log);
483 }
484 }
485 }
486
487 /**
488 * Returns the class of the ref type to be serialized.
489 */
490 public String getRefClass(ObjectOutput out) {
491 return "UnicastServerRef";
492 }
493
494 /**
495 * Return the client remote reference for this remoteRef.
496 * In the case of a client RemoteRef "this" is the answer.
497 * For a server remote reference, a client side one will have to
498 * found or created.
499 */
500 protected RemoteRef getClientRef() {
501 return new UnicastRef(ref);
502 }
503
504 /**
505 * Write out external representation for remote ref.
506 */
507 public void writeExternal(ObjectOutput out) throws IOException {
508 }
509
510 /**
511 * Read in external representation for remote ref.
512 * @exception ClassNotFoundException If the class for an object
513 * being restored cannot be found.
514 */
515 public void readExternal(ObjectInput in)
516 throws IOException, ClassNotFoundException
517 {
518 // object is re-exported elsewhere (e.g., by UnicastRemoteObject)
519 ref = null;
520 skel = null;
521 }
522
523
524 /**
525 * A weak hash map, mapping classes to hash maps that map method
526 * hashes to method objects.
527 **/
528 private static class HashToMethod_Maps
529 extends WeakClassHashMap<Map<Long,Method>>
530 {
531 HashToMethod_Maps() {}
532
533 protected Map<Long,Method> computeValue(Class<?> remoteClass) {
534 Map<Long,Method> map = new HashMap<Long,Method>();
535 for (Class<?> cl = remoteClass;
536 cl != null;
537 cl = cl.getSuperclass())
538 {
539 for (Class<?> intf : cl.getInterfaces()) {
540 if (Remote.class.isAssignableFrom(intf)) {
541 for (Method method : intf.getMethods()) {
542 final Method m = method;
543 /*
544 * Set this Method object to override language
545 * access checks so that the dispatcher can invoke
546 * methods from non-public remote interfaces.
547 */
548 AccessController.doPrivileged(
549 new PrivilegedAction<Void>() {
550 public Void run() {
551 m.setAccessible(true);
552 return null;
553 }
554 });
555 map.put(Util.computeMethodHash(m), m);
556 }
557 }
558 }
559 }
560 return map;
561 }
562 }
563 }