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