blob: b9307f9ea4a862b421c3cae4e32d41856d443965 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2006 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
26package sun.rmi.server;
27
28import java.io.IOException;
29import java.io.ObjectInput;
30import java.io.ObjectOutput;
31import java.lang.reflect.Method;
32import java.rmi.MarshalException;
33import java.rmi.Remote;
34import java.rmi.RemoteException;
35import java.rmi.ServerException;
36import java.rmi.UnmarshalException;
37import java.rmi.server.Operation;
38import java.rmi.server.RemoteCall;
39import java.rmi.server.RemoteObject;
40import java.rmi.server.RemoteRef;
41import java.security.AccessController;
42import sun.rmi.runtime.Log;
43import sun.rmi.transport.Connection;
44import sun.rmi.transport.LiveRef;
45import sun.rmi.transport.StreamRemoteCall;
46import sun.security.action.GetBooleanAction;
47
48/**
49 * NOTE: There is a JDK-internal dependency on the existence of this
50 * class's getLiveRef method (as it is inherited by UnicastRef2) in
51 * the implementation of javax.management.remote.rmi.RMIConnector.
52 **/
53public class UnicastRef implements RemoteRef {
54
55 /**
56 * Client-side transport log.
57 */
58 public static final Log clientRefLog =
59 Log.getLog("sun.rmi.client.ref", "transport", Util.logLevel);
60
61 /**
62 * Client-side call log.
63 */
64 public static final Log clientCallLog =
65 Log.getLog("sun.rmi.client.call", "RMI",
66 AccessController.doPrivileged(
67 new GetBooleanAction("sun.rmi.client.logCalls")));
68
69 protected LiveRef ref;
70
71 /**
72 * Create a new (empty) Unicast remote reference.
73 */
74 public UnicastRef() {
75 }
76
77 /**
78 * Create a new Unicast RemoteRef.
79 */
80 public UnicastRef(LiveRef liveRef) {
81 ref = liveRef;
82 }
83
84 /**
85 * Returns the current value of this UnicastRef's underlying
86 * LiveRef.
87 *
88 * NOTE: There is a JDK-internal dependency on the existence of
89 * this method (as it is inherited by UnicastRef) in the
90 * implementation of javax.management.remote.rmi.RMIConnector.
91 **/
92 public LiveRef getLiveRef() {
93 return ref;
94 }
95
96 /**
97 * Invoke a method. This form of delegating method invocation
98 * to the reference allows the reference to take care of
99 * setting up the connection to the remote host, marshalling
100 * some representation for the method and parameters, then
101 * communicating the method invocation to the remote host.
102 * This method either returns the result of a method invocation
103 * on the remote object which resides on the remote host or
104 * throws a RemoteException if the call failed or an
105 * application-level exception if the remote invocation throws
106 * an exception.
107 *
108 * @param obj the proxy for the remote object
109 * @param method the method to be invoked
110 * @param params the parameter list
111 * @param opnum a hash that may be used to represent the method
112 * @since 1.2
113 */
114 public Object invoke(Remote obj,
115 Method method,
116 Object[] params,
117 long opnum)
118 throws Exception
119 {
120 if (clientRefLog.isLoggable(Log.VERBOSE)) {
121 clientRefLog.log(Log.VERBOSE, "method: " + method);
122 }
123
124 if (clientCallLog.isLoggable(Log.VERBOSE)) {
125 logClientCall(obj, method);
126 }
127
128 Connection conn = ref.getChannel().newConnection();
129 RemoteCall call = null;
130 boolean reuse = true;
131
132 /* If the call connection is "reused" early, remember not to
133 * reuse again.
134 */
135 boolean alreadyFreed = false;
136
137 try {
138 if (clientRefLog.isLoggable(Log.VERBOSE)) {
139 clientRefLog.log(Log.VERBOSE, "opnum = " + opnum);
140 }
141
142 // create call context
143 call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum);
144
145 // marshal parameters
146 try {
147 ObjectOutput out = call.getOutputStream();
148 marshalCustomCallData(out);
149 Class<?>[] types = method.getParameterTypes();
150 for (int i = 0; i < types.length; i++) {
151 marshalValue(types[i], params[i], out);
152 }
153 } catch (IOException e) {
154 clientRefLog.log(Log.BRIEF,
155 "IOException marshalling arguments: ", e);
156 throw new MarshalException("error marshalling arguments", e);
157 }
158
159 // unmarshal return
160 call.executeCall();
161
162 try {
163 Class<?> rtype = method.getReturnType();
164 if (rtype == void.class)
165 return null;
166 ObjectInput in = call.getInputStream();
167
168 /* StreamRemoteCall.done() does not actually make use
169 * of conn, therefore it is safe to reuse this
170 * connection before the dirty call is sent for
171 * registered refs.
172 */
173 Object returnValue = unmarshalValue(rtype, in);
174
175 /* we are freeing the connection now, do not free
176 * again or reuse.
177 */
178 alreadyFreed = true;
179
180 /* if we got to this point, reuse must have been true. */
181 clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
182
183 /* Free the call's connection early. */
184 ref.getChannel().free(conn, true);
185
186 return returnValue;
187
188 } catch (IOException e) {
189 clientRefLog.log(Log.BRIEF,
190 "IOException unmarshalling return: ", e);
191 throw new UnmarshalException("error unmarshalling return", e);
192 } catch (ClassNotFoundException e) {
193 clientRefLog.log(Log.BRIEF,
194 "ClassNotFoundException unmarshalling return: ", e);
195
196 throw new UnmarshalException("error unmarshalling return", e);
197 } finally {
198 try {
199 call.done();
200 } catch (IOException e) {
201 /* WARNING: If the conn has been reused early,
202 * then it is too late to recover from thrown
203 * IOExceptions caught here. This code is relying
204 * on StreamRemoteCall.done() not actually
205 * throwing IOExceptions.
206 */
207 reuse = false;
208 }
209 }
210
211 } catch (RuntimeException e) {
212 /*
213 * Need to distinguish between client (generated by the
214 * invoke method itself) and server RuntimeExceptions.
215 * Client side RuntimeExceptions are likely to have
216 * corrupted the call connection and those from the server
217 * are not likely to have done so. If the exception came
218 * from the server the call connection should be reused.
219 */
220 if ((call == null) ||
221 (((StreamRemoteCall) call).getServerException() != e))
222 {
223 reuse = false;
224 }
225 throw e;
226
227 } catch (RemoteException e) {
228 /*
229 * Some failure during call; assume connection cannot
230 * be reused. Must assume failure even if ServerException
231 * or ServerError occurs since these failures can happen
232 * during parameter deserialization which would leave
233 * the connection in a corrupted state.
234 */
235 reuse = false;
236 throw e;
237
238 } catch (Error e) {
239 /* If errors occurred, the connection is most likely not
240 * reusable.
241 */
242 reuse = false;
243 throw e;
244
245 } finally {
246
247 /* alreadyFreed ensures that we do not log a reuse that
248 * may have already happened.
249 */
250 if (!alreadyFreed) {
251 if (clientRefLog.isLoggable(Log.BRIEF)) {
252 clientRefLog.log(Log.BRIEF, "free connection (reuse = " +
253 reuse + ")");
254 }
255 ref.getChannel().free(conn, reuse);
256 }
257 }
258 }
259
260 protected void marshalCustomCallData(ObjectOutput out) throws IOException
261 {}
262
263 /**
264 * Marshal value to an ObjectOutput sink using RMI's serialization
265 * format for parameters or return values.
266 */
267 protected static void marshalValue(Class<?> type, Object value,
268 ObjectOutput out)
269 throws IOException
270 {
271 if (type.isPrimitive()) {
272 if (type == int.class) {
273 out.writeInt(((Integer) value).intValue());
274 } else if (type == boolean.class) {
275 out.writeBoolean(((Boolean) value).booleanValue());
276 } else if (type == byte.class) {
277 out.writeByte(((Byte) value).byteValue());
278 } else if (type == char.class) {
279 out.writeChar(((Character) value).charValue());
280 } else if (type == short.class) {
281 out.writeShort(((Short) value).shortValue());
282 } else if (type == long.class) {
283 out.writeLong(((Long) value).longValue());
284 } else if (type == float.class) {
285 out.writeFloat(((Float) value).floatValue());
286 } else if (type == double.class) {
287 out.writeDouble(((Double) value).doubleValue());
288 } else {
289 throw new Error("Unrecognized primitive type: " + type);
290 }
291 } else {
292 out.writeObject(value);
293 }
294 }
295
296 /**
297 * Unmarshal value from an ObjectInput source using RMI's serialization
298 * format for parameters or return values.
299 */
300 protected static Object unmarshalValue(Class<?> type, ObjectInput in)
301 throws IOException, ClassNotFoundException
302 {
303 if (type.isPrimitive()) {
304 if (type == int.class) {
305 return Integer.valueOf(in.readInt());
306 } else if (type == boolean.class) {
307 return Boolean.valueOf(in.readBoolean());
308 } else if (type == byte.class) {
309 return Byte.valueOf(in.readByte());
310 } else if (type == char.class) {
311 return Character.valueOf(in.readChar());
312 } else if (type == short.class) {
313 return Short.valueOf(in.readShort());
314 } else if (type == long.class) {
315 return Long.valueOf(in.readLong());
316 } else if (type == float.class) {
317 return Float.valueOf(in.readFloat());
318 } else if (type == double.class) {
319 return Double.valueOf(in.readDouble());
320 } else {
321 throw new Error("Unrecognized primitive type: " + type);
322 }
323 } else {
324 return in.readObject();
325 }
326 }
327
328 /**
329 * Create an appropriate call object for a new call on this object.
330 * Passing operation array and index, allows the stubs generator to
331 * assign the operation indexes and interpret them. The RemoteRef
332 * may need the operation to encode in for the call.
333 */
334 public RemoteCall newCall(RemoteObject obj, Operation[] ops, int opnum,
335 long hash)
336 throws RemoteException
337 {
338 clientRefLog.log(Log.BRIEF, "get connection");
339
340 Connection conn = ref.getChannel().newConnection();
341 try {
342 clientRefLog.log(Log.VERBOSE, "create call context");
343
344 /* log information about the outgoing call */
345 if (clientCallLog.isLoggable(Log.VERBOSE)) {
346 logClientCall(obj, ops[opnum]);
347 }
348
349 RemoteCall call =
350 new StreamRemoteCall(conn, ref.getObjID(), opnum, hash);
351 try {
352 marshalCustomCallData(call.getOutputStream());
353 } catch (IOException e) {
354 throw new MarshalException("error marshaling " +
355 "custom call data");
356 }
357 return call;
358 } catch (RemoteException e) {
359 ref.getChannel().free(conn, false);
360 throw e;
361 }
362 }
363
364 /**
365 * Invoke makes the remote call present in the RemoteCall object.
366 *
367 * Invoke will raise any "user" exceptions which
368 * should pass through and not be caught by the stub. If any
369 * exception is raised during the remote invocation, invoke should
370 * take care of cleaning up the connection before raising the
371 * "user" or remote exception.
372 */
373 public void invoke(RemoteCall call) throws Exception {
374 try {
375 clientRefLog.log(Log.VERBOSE, "execute call");
376
377 call.executeCall();
378
379 } catch (RemoteException e) {
380 /*
381 * Call did not complete; connection can't be reused.
382 */
383 clientRefLog.log(Log.BRIEF, "exception: ", e);
384 free(call, false);
385 throw e;
386
387 } catch (Error e) {
388 /* If errors occurred, the connection is most likely not
389 * reusable.
390 */
391 clientRefLog.log(Log.BRIEF, "error: ", e);
392 free(call, false);
393 throw e;
394
395 } catch (RuntimeException e) {
396 /*
397 * REMIND: Since runtime exceptions are no longer wrapped,
398 * we can't assue that the connection was left in
399 * a reusable state. Is this okay?
400 */
401 clientRefLog.log(Log.BRIEF, "exception: ", e);
402 free(call, false);
403 throw e;
404
405 } catch (Exception e) {
406 /*
407 * Assume that these other exceptions are user exceptions
408 * and leave the connection in a reusable state.
409 */
410 clientRefLog.log(Log.BRIEF, "exception: ", e);
411 free(call, true);
412 /* reraise user (and unknown) exceptions. */
413 throw e;
414 }
415
416 /*
417 * Don't free the connection if an exception did not
418 * occur because the stub needs to unmarshal the
419 * return value. The connection will be freed
420 * by a call to the "done" method.
421 */
422 }
423
424 /**
425 * Private method to free a connection.
426 */
427 private void free(RemoteCall call, boolean reuse) throws RemoteException {
428 Connection conn = ((StreamRemoteCall)call).getConnection();
429 ref.getChannel().free(conn, reuse);
430 }
431
432 /**
433 * Done should only be called if the invoke returns successfully
434 * (non-exceptionally) to the stub. It allows the remote reference to
435 * clean up (or reuse) the connection.
436 */
437 public void done(RemoteCall call) throws RemoteException {
438
439 /* Done only uses the connection inside the call to obtain the
440 * channel the connection uses. Once all information is read
441 * from the connection, the connection may be freed.
442 */
443 clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
444
445 /* Free the call connection early. */
446 free(call, true);
447
448 try {
449 call.done();
450 } catch (IOException e) {
451 /* WARNING: If the conn has been reused early, then it is
452 * too late to recover from thrown IOExceptions caught
453 * here. This code is relying on StreamRemoteCall.done()
454 * not actually throwing IOExceptions.
455 */
456 }
457 }
458
459 /**
460 * Log the details of an outgoing call. The method parameter is either of
461 * type java.lang.reflect.Method or java.rmi.server.Operation.
462 */
463 void logClientCall(Object obj, Object method) {
464 clientCallLog.log(Log.VERBOSE, "outbound call: " +
465 ref + " : " + obj.getClass().getName() +
466 ref.getObjID().toString() + ": " + method);
467 }
468
469 /**
470 * Returns the class of the ref type to be serialized
471 */
472 public String getRefClass(ObjectOutput out) {
473 return "UnicastRef";
474 }
475
476 /**
477 * Write out external representation for remote ref.
478 */
479 public void writeExternal(ObjectOutput out) throws IOException {
480 ref.write(out, false);
481 }
482
483 /**
484 * Read in external representation for remote ref.
485 * @exception ClassNotFoundException If the class for an object
486 * being restored cannot be found.
487 */
488 public void readExternal(ObjectInput in)
489 throws IOException, ClassNotFoundException
490 {
491 ref = LiveRef.read(in, false);
492 }
493
494 //----------------------------------------------------------------------;
495 /**
496 * Method from object, forward from RemoteObject
497 */
498 public String remoteToString() {
499 return Util.getUnqualifiedName(getClass()) + " [liveRef: " + ref + "]";
500 }
501
502 /**
503 * default implementation of hashCode for remote objects
504 */
505 public int remoteHashCode() {
506 return ref.hashCode();
507 }
508
509 /** default implementation of equals for remote objects
510 */
511 public boolean remoteEquals(RemoteRef sub) {
512 if (sub instanceof UnicastRef)
513 return ref.remoteEquals(((UnicastRef)sub).ref);
514 return false;
515 }
516}