1/* 2 * Copyright (c) 1996, 2017, 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 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.UnmarshalException; 36import java.rmi.server.Operation; 37import java.rmi.server.RemoteCall; 38import java.rmi.server.RemoteObject; 39import java.rmi.server.RemoteRef; 40import java.security.AccessController; 41import java.security.PrivilegedAction; 42import sun.rmi.runtime.Log; 43import sun.rmi.transport.Connection; 44import sun.rmi.transport.LiveRef; 45import sun.rmi.transport.StreamRemoteCall; 46 47/** 48 * NOTE: There is a JDK-internal dependency on the existence of this 49 * class's getLiveRef method (as it is inherited by UnicastRef2) in 50 * the implementation of javax.management.remote.rmi.RMIConnector. 51 */ 52@SuppressWarnings("deprecation") 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((PrivilegedAction<Boolean>) () -> 67 Boolean.getBoolean("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 | ClassNotFoundException e) { 190 // disable saving any refs in the inputStream for GC 191 ((StreamRemoteCall)call).discardPendingRefs(); 192 clientRefLog.log(Log.BRIEF, 193 e.getClass().getName() + " unmarshalling return: ", e); 194 throw new UnmarshalException("error unmarshalling return", e); 195 } finally { 196 try { 197 call.done(); 198 } catch (IOException e) { 199 /* WARNING: If the conn has been reused early, 200 * then it is too late to recover from thrown 201 * IOExceptions caught here. This code is relying 202 * on StreamRemoteCall.done() not actually 203 * throwing IOExceptions. 204 */ 205 reuse = false; 206 } 207 } 208 209 } catch (RuntimeException e) { 210 /* 211 * Need to distinguish between client (generated by the 212 * invoke method itself) and server RuntimeExceptions. 213 * Client side RuntimeExceptions are likely to have 214 * corrupted the call connection and those from the server 215 * are not likely to have done so. If the exception came 216 * from the server the call connection should be reused. 217 */ 218 if ((call == null) || 219 (((StreamRemoteCall) call).getServerException() != e)) 220 { 221 reuse = false; 222 } 223 throw e; 224 225 } catch (RemoteException e) { 226 /* 227 * Some failure during call; assume connection cannot 228 * be reused. Must assume failure even if ServerException 229 * or ServerError occurs since these failures can happen 230 * during parameter deserialization which would leave 231 * the connection in a corrupted state. 232 */ 233 reuse = false; 234 throw e; 235 236 } catch (Error e) { 237 /* If errors occurred, the connection is most likely not 238 * reusable. 239 */ 240 reuse = false; 241 throw e; 242 243 } finally { 244 245 /* alreadyFreed ensures that we do not log a reuse that 246 * may have already happened. 247 */ 248 if (!alreadyFreed) { 249 if (clientRefLog.isLoggable(Log.BRIEF)) { 250 clientRefLog.log(Log.BRIEF, "free connection (reuse = " + 251 reuse + ")"); 252 } 253 ref.getChannel().free(conn, reuse); 254 } 255 } 256 } 257 258 protected void marshalCustomCallData(ObjectOutput out) throws IOException 259 {} 260 261 /** 262 * Marshal value to an ObjectOutput sink using RMI's serialization 263 * format for parameters or return values. 264 */ 265 protected static void marshalValue(Class<?> type, Object value, 266 ObjectOutput out) 267 throws IOException 268 { 269 if (type.isPrimitive()) { 270 if (type == int.class) { 271 out.writeInt(((Integer) value).intValue()); 272 } else if (type == boolean.class) { 273 out.writeBoolean(((Boolean) value).booleanValue()); 274 } else if (type == byte.class) { 275 out.writeByte(((Byte) value).byteValue()); 276 } else if (type == char.class) { 277 out.writeChar(((Character) value).charValue()); 278 } else if (type == short.class) { 279 out.writeShort(((Short) value).shortValue()); 280 } else if (type == long.class) { 281 out.writeLong(((Long) value).longValue()); 282 } else if (type == float.class) { 283 out.writeFloat(((Float) value).floatValue()); 284 } else if (type == double.class) { 285 out.writeDouble(((Double) value).doubleValue()); 286 } else { 287 throw new Error("Unrecognized primitive type: " + type); 288 } 289 } else { 290 out.writeObject(value); 291 } 292 } 293 294 /** 295 * Unmarshal value from an ObjectInput source using RMI's serialization 296 * format for parameters or return values. 297 */ 298 protected static Object unmarshalValue(Class<?> type, ObjectInput in) 299 throws IOException, ClassNotFoundException 300 { 301 if (type.isPrimitive()) { 302 if (type == int.class) { 303 return Integer.valueOf(in.readInt()); 304 } else if (type == boolean.class) { 305 return Boolean.valueOf(in.readBoolean()); 306 } else if (type == byte.class) { 307 return Byte.valueOf(in.readByte()); 308 } else if (type == char.class) { 309 return Character.valueOf(in.readChar()); 310 } else if (type == short.class) { 311 return Short.valueOf(in.readShort()); 312 } else if (type == long.class) { 313 return Long.valueOf(in.readLong()); 314 } else if (type == float.class) { 315 return Float.valueOf(in.readFloat()); 316 } else if (type == double.class) { 317 return Double.valueOf(in.readDouble()); 318 } else { 319 throw new Error("Unrecognized primitive type: " + type); 320 } 321 } else { 322 return in.readObject(); 323 } 324 } 325 326 /** 327 * Create an appropriate call object for a new call on this object. 328 * Passing operation array and index, allows the stubs generator to 329 * assign the operation indexes and interpret them. The RemoteRef 330 * may need the operation to encode in for the call. 331 */ 332 public RemoteCall newCall(RemoteObject obj, Operation[] ops, int opnum, 333 long hash) 334 throws RemoteException 335 { 336 clientRefLog.log(Log.BRIEF, "get connection"); 337 338 Connection conn = ref.getChannel().newConnection(); 339 try { 340 clientRefLog.log(Log.VERBOSE, "create call context"); 341 342 /* log information about the outgoing call */ 343 if (clientCallLog.isLoggable(Log.VERBOSE)) { 344 logClientCall(obj, ops[opnum]); 345 } 346 347 RemoteCall call = 348 new StreamRemoteCall(conn, ref.getObjID(), opnum, hash); 349 try { 350 marshalCustomCallData(call.getOutputStream()); 351 } catch (IOException e) { 352 throw new MarshalException("error marshaling " + 353 "custom call data"); 354 } 355 return call; 356 } catch (RemoteException e) { 357 ref.getChannel().free(conn, false); 358 throw e; 359 } 360 } 361 362 /** 363 * Invoke makes the remote call present in the RemoteCall object. 364 * 365 * Invoke will raise any "user" exceptions which 366 * should pass through and not be caught by the stub. If any 367 * exception is raised during the remote invocation, invoke should 368 * take care of cleaning up the connection before raising the 369 * "user" or remote exception. 370 */ 371 public void invoke(RemoteCall call) throws Exception { 372 try { 373 clientRefLog.log(Log.VERBOSE, "execute call"); 374 375 call.executeCall(); 376 377 } catch (RemoteException e) { 378 /* 379 * Call did not complete; connection can't be reused. 380 */ 381 clientRefLog.log(Log.BRIEF, "exception: ", e); 382 free(call, false); 383 throw e; 384 385 } catch (Error e) { 386 /* If errors occurred, the connection is most likely not 387 * reusable. 388 */ 389 clientRefLog.log(Log.BRIEF, "error: ", e); 390 free(call, false); 391 throw e; 392 393 } catch (RuntimeException e) { 394 /* 395 * REMIND: Since runtime exceptions are no longer wrapped, 396 * we can't assue that the connection was left in 397 * a reusable state. Is this okay? 398 */ 399 clientRefLog.log(Log.BRIEF, "exception: ", e); 400 free(call, false); 401 throw e; 402 403 } catch (Exception e) { 404 /* 405 * Assume that these other exceptions are user exceptions 406 * and leave the connection in a reusable state. 407 */ 408 clientRefLog.log(Log.BRIEF, "exception: ", e); 409 free(call, true); 410 /* reraise user (and unknown) exceptions. */ 411 throw e; 412 } 413 414 /* 415 * Don't free the connection if an exception did not 416 * occur because the stub needs to unmarshal the 417 * return value. The connection will be freed 418 * by a call to the "done" method. 419 */ 420 } 421 422 /** 423 * Private method to free a connection. 424 */ 425 private void free(RemoteCall call, boolean reuse) throws RemoteException { 426 Connection conn = ((StreamRemoteCall)call).getConnection(); 427 ref.getChannel().free(conn, reuse); 428 } 429 430 /** 431 * Done should only be called if the invoke returns successfully 432 * (non-exceptionally) to the stub. It allows the remote reference to 433 * clean up (or reuse) the connection. 434 */ 435 public void done(RemoteCall call) throws RemoteException { 436 437 /* Done only uses the connection inside the call to obtain the 438 * channel the connection uses. Once all information is read 439 * from the connection, the connection may be freed. 440 */ 441 clientRefLog.log(Log.BRIEF, "free connection (reuse = true)"); 442 443 /* Free the call connection early. */ 444 free(call, true); 445 446 try { 447 call.done(); 448 } catch (IOException e) { 449 /* WARNING: If the conn has been reused early, then it is 450 * too late to recover from thrown IOExceptions caught 451 * here. This code is relying on StreamRemoteCall.done() 452 * not actually throwing IOExceptions. 453 */ 454 } 455 } 456 457 /** 458 * Log the details of an outgoing call. The method parameter is either of 459 * type java.lang.reflect.Method or java.rmi.server.Operation. 460 */ 461 void logClientCall(Object obj, Object method) { 462 clientCallLog.log(Log.VERBOSE, "outbound call: " + 463 ref + " : " + obj.getClass().getName() + 464 ref.getObjID().toString() + ": " + method); 465 } 466 467 /** 468 * Returns the class of the ref type to be serialized 469 */ 470 public String getRefClass(ObjectOutput out) { 471 return "UnicastRef"; 472 } 473 474 /** 475 * Write out external representation for remote ref. 476 */ 477 public void writeExternal(ObjectOutput out) throws IOException { 478 ref.write(out, false); 479 } 480 481 /** 482 * Read in external representation for remote ref. 483 * @exception ClassNotFoundException If the class for an object 484 * being restored cannot be found. 485 */ 486 public void readExternal(ObjectInput in) 487 throws IOException, ClassNotFoundException 488 { 489 ref = LiveRef.read(in, false); 490 } 491 492 //----------------------------------------------------------------------; 493 /** 494 * Method from object, forward from RemoteObject 495 */ 496 public String remoteToString() { 497 return Util.getUnqualifiedName(getClass()) + " [liveRef: " + ref + "]"; 498 } 499 500 /** 501 * default implementation of hashCode for remote objects 502 */ 503 public int remoteHashCode() { 504 return ref.hashCode(); 505 } 506 507 /** default implementation of equals for remote objects 508 */ 509 public boolean remoteEquals(RemoteRef sub) { 510 if (sub instanceof UnicastRef) 511 return ref.remoteEquals(((UnicastRef)sub).ref); 512 return false; 513 } 514} 515