1 /* UnicastRef.java -- 2 Copyright (c) 1996, 1997, 1998, 1999, 2002, 2005, 2006 3 Free Software Foundation, Inc. 4 5 This file is part of GNU Classpath. 6 7 GNU Classpath is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 GNU Classpath is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GNU Classpath; see the file COPYING. If not, write to the 19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 02110-1301 USA. 21 22 Linking this library statically or dynamically with other modules is 23 making a combined work based on this library. Thus, the terms and 24 conditions of the GNU General Public License cover the whole 25 combination. 26 27 As a special exception, the copyright holders of this library give you 28 permission to link this library with independent modules to produce an 29 executable, regardless of the license terms of these independent 30 modules, and to copy and distribute the resulting executable under 31 terms of your choice, provided that you also meet, for each linked 32 independent module, the terms and conditions of the license of that 33 module. An independent module is a module which is not derived from 34 or based on this library. If you modify this library, you may extend 35 this exception to your version of the library, but you are not 36 obligated to do so. If you do not wish to do so, delete this 37 exception statement from your version. */ 38 39 40 package gnu.java.rmi.server; 41 42 import gnu.java.rmi.dgc.LeaseRenewingTask; 43 44 import java.io.DataInputStream; 45 import java.io.DataOutputStream; 46 import java.io.IOException; 47 import java.io.ObjectInput; 48 import java.io.ObjectInputStream; 49 import java.io.ObjectOutput; 50 import java.io.ObjectOutputStream; 51 import java.lang.reflect.InvocationTargetException; 52 import java.lang.reflect.Method; 53 import java.rmi.ConnectException; 54 import java.rmi.Remote; 55 import java.rmi.RemoteException; 56 import java.rmi.dgc.Lease; 57 import java.rmi.server.ObjID; 58 import java.rmi.server.Operation; 59 import java.rmi.server.RMIClientSocketFactory; 60 import java.rmi.server.RemoteCall; 61 import java.rmi.server.RemoteObject; 62 import java.rmi.server.RemoteRef; 63 import java.rmi.server.UID; 64 65 public class UnicastRef 66 implements RemoteRef, ProtocolConstants 67 { 68 69 /** 70 * Use serial version UID for iteroperability 71 */ 72 private static final long serialVersionUID = 1; 73 74 public ObjID objid; 75 76 UnicastConnectionManager manager; 77 78 /** 79 * Used by serialization, and let subclass capable of having default 80 * constructor 81 */ 82 // must be public otherwise java.rmi.RemoteObject cannot instantiate this 83 // class 84 // -- iP UnicastRef()85 public UnicastRef() 86 { 87 } 88 UnicastRef(ObjID objid, String host, int port, RMIClientSocketFactory csf)89 public UnicastRef(ObjID objid, String host, int port, 90 RMIClientSocketFactory csf) 91 { 92 this(objid); 93 manager = UnicastConnectionManager.getInstance(host, port, csf); 94 } 95 UnicastRef(ObjID objid)96 public UnicastRef(ObjID objid) 97 { 98 this.objid = objid; 99 } 100 invoke(Remote obj, Method method, Object[] params, long opnum)101 public Object invoke(Remote obj, Method method, Object[] params, long opnum) 102 throws Exception 103 { 104 // Check if client and server are in the same VM, then local call can be 105 // used to 106 // replace remote call, but it's somewhat violating remote semantic. 107 Object svrobj = manager.serverobj; 108 109 // Make sure that the server object is compatible. It could be loaded from a 110 // different 111 // classloader --iP 112 if (svrobj != null && method.getDeclaringClass().isInstance(svrobj)) 113 { 114 // local call 115 Object ret = null; 116 try 117 { 118 ret = method.invoke(svrobj, params); 119 } 120 catch (InvocationTargetException e) 121 { 122 throw (Exception) e.getTargetException(); 123 } 124 // System.out.println("\n\n ***** local call: " + method + "\nreturn: " 125 // + ret + "\n\n"); 126 return ret; 127 } 128 // System.out.println("***************** remote call:" + 129 // manager.serverPort); 130 return (invokeCommon(obj, method, params, - 1, opnum)); 131 } 132 133 /** 134 * The ordinary number of the DGC messages. 135 */ 136 static long dgcSequence; 137 138 /** 139 * The DGC object id, also serves as a synchronization target to increment the 140 * dgcSequence safely. 141 */ 142 static final ObjID dgcId = new ObjID(ObjID.DGC_ID); 143 144 ObjID[] this_id; 145 146 /** 147 * The number of the method "dirty" in the DGC. 148 */ 149 static int DIRTY = 1; 150 151 /** 152 * The DGC interface hash code. 153 */ 154 static final long dgcInterfaceHash = - 669196253586618813L; 155 156 /** 157 * Notify the DGC of the remote side that we still hold this object. 158 */ notifyDGC(Lease lease)159 public Lease notifyDGC(Lease lease) throws Exception 160 { 161 long seq; 162 synchronized (dgcId) 163 { 164 seq = dgcSequence++; 165 } 166 167 if (this_id == null) 168 this_id = new ObjID[] { objid }; 169 170 UnicastConnection conn; 171 try 172 { 173 conn = manager.getConnection(); 174 } 175 catch (IOException e1) 176 { 177 throw new RemoteException("connection failed to host: " 178 + manager.serverName, e1); 179 } 180 181 ObjectOutputStream out; 182 DataOutputStream dout; 183 try 184 { 185 dout = conn.getDataOutputStream(); 186 dout.writeByte(MESSAGE_CALL); 187 188 out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream 189 190 dgcId.write(out); 191 // The number of the operation is 1 ("dirty") 192 out.writeInt(DIRTY); 193 out.writeLong(dgcInterfaceHash); 194 195 RMIObjectOutputStream rout = (RMIObjectOutputStream) out; 196 197 rout.writeValue(this_id, this_id.getClass()); 198 rout.writeLong(seq); 199 rout.writeValue(lease, lease.getClass()); 200 201 out.flush(); 202 } 203 catch (IOException e2) 204 { 205 throw new RemoteException("DGC call failed: ", e2); 206 } 207 208 int returncode; 209 Object returnval; 210 DataInputStream din; 211 ObjectInputStream in; 212 UID ack; 213 try 214 { 215 din = conn.getDataInputStream(); 216 217 if ((returncode = din.readUnsignedByte()) != MESSAGE_CALL_ACK) 218 { 219 conn.disconnect(); 220 throw new RemoteException("DGC Call not acked:" + returncode); 221 } 222 223 in = conn.startObjectInputStream(); // (re)start ObjectInputStream 224 returncode = in.readUnsignedByte(); 225 ack = UID.read(in); 226 227 if (returncode == RETURN_NACK) 228 { 229 returnval = in.readObject(); // get Exception 230 231 } 232 else 233 { 234 returnval = ((RMIObjectInputStream) in).readValue(Lease.class); 235 } 236 } 237 catch (IOException e3) 238 { 239 throw new RemoteException("DGC call return failed: ", e3); 240 } 241 242 manager.discardConnection(conn); 243 244 if (returncode != RETURN_ACK && returnval != null) 245 { 246 if (returncode == RETURN_NACK) 247 throw (Exception) returnval; 248 else 249 throw new RemoteException("DGC unexpected returncode: " + returncode); 250 } 251 252 return (Lease) returnval; 253 } 254 /** 255 * Invoke the remote method on the given object. This part is overridden by 256 * the activatable objects. 257 */ invokeCommon(Remote obj, Method method, Object[] params, int opnum, long hash)258 protected Object invokeCommon(Remote obj, Method method, Object[] params, 259 int opnum, long hash) throws Exception 260 { 261 UnicastConnection conn; 262 try 263 { 264 conn = manager.getConnection(); 265 return invokeCommon(conn, obj, method, params, opnum, hash); 266 } 267 catch (IOException e1) 268 { 269 throw new RemoteException("connection failed to host: " 270 + manager.serverName, e1); 271 } 272 } 273 274 /** 275 * Invoke the remote method on the given object when connection is already 276 * established. 277 */ invokeCommon(UnicastConnection conn, Remote obj, Method method, Object[] params, int opnum, long hash)278 protected Object invokeCommon(UnicastConnection conn, Remote obj, 279 Method method, Object[] params, int opnum, 280 long hash) throws Exception 281 { 282 ObjectOutputStream out; 283 DataOutputStream dout; 284 try 285 { 286 dout = conn.getDataOutputStream(); 287 dout.writeByte(MESSAGE_CALL); 288 289 out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream 290 291 objid.write(out); 292 out.writeInt(opnum); 293 out.writeLong(hash); 294 295 // must handle primitive class and their wrapper classes 296 Class clss[] = method.getParameterTypes(); 297 for (int i = 0; i < clss.length; i++) 298 ((RMIObjectOutputStream) out).writeValue(params[i], clss[i]); 299 300 out.flush(); 301 } 302 catch (IOException e2) 303 { 304 throw new RemoteException("call failed: ", e2); 305 } 306 307 int returncode; 308 Object returnval; 309 DataInputStream din; 310 ObjectInputStream in; 311 UID ack; 312 try 313 { 314 din = conn.getDataInputStream(); 315 316 if ((returncode = din.readUnsignedByte()) != MESSAGE_CALL_ACK) 317 { 318 conn.disconnect(); 319 throw new RemoteException("Call not acked:" + returncode); 320 } 321 322 in = conn.startObjectInputStream(); // (re)start ObjectInputStream 323 returncode = in.readUnsignedByte(); 324 ack = UID.read(in); 325 326 Class cls = method.getReturnType(); 327 328 if (returncode == RETURN_NACK) 329 { 330 returnval = in.readObject(); // get Exception 331 332 } 333 else if (cls == Void.TYPE) 334 { 335 returnval = null; 336 // in.readObject() // not required! returntype 'void' means no field 337 // is returned. 338 } 339 else 340 { 341 returnval = ((RMIObjectInputStream) in).readValue(cls); // get 342 // returnvalue 343 } 344 } 345 catch (IOException e3) 346 { 347 // for debug: e3.printStackTrace(); 348 throw new RemoteException("call return failed: ", e3); 349 } 350 351 /* 352 * if DGCAck is necessary?? //According to RMI wire protocol, send a DGCAck // 353 * to indicate receiving return value dout.writeByte(MESSAGE_DGCACK); 354 * ack.write(dout); out.flush(); 355 */ 356 357 manager.discardConnection(conn); 358 359 if (returncode != RETURN_ACK && returnval != null) 360 { 361 if (returncode == RETURN_NACK) 362 throw (Exception) returnval; 363 else 364 throw new RemoteException("unexpected returncode: " + returncode); 365 } 366 367 return (returnval); 368 } 369 370 /** 371 * @deprecated 372 */ newCall(RemoteObject obj, Operation[] op, int opnum, long hash)373 public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum, 374 long hash) throws RemoteException 375 { 376 UnicastConnection conn; 377 378 try 379 { 380 conn = manager.getConnection(); 381 } 382 catch (IOException e1) 383 { 384 throw new ConnectException("connection failed to host: " 385 + manager.serverName, e1); 386 } 387 388 // obj: useless? 389 390 return (new UnicastRemoteCall(conn, objid, opnum, hash)); 391 } 392 393 /** 394 * @deprecated 395 */ invoke(RemoteCall call)396 public void invoke(RemoteCall call) throws Exception 397 { 398 UnicastRemoteCall c = (UnicastRemoteCall) call; 399 call.executeCall(); 400 } 401 402 /** 403 * @deprecated 404 */ done(RemoteCall call)405 public void done(RemoteCall call) throws RemoteException 406 { 407 UnicastRemoteCall c = (UnicastRemoteCall) call; 408 try 409 { 410 c.done(); 411 } 412 catch (IOException e) 413 { 414 } 415 UnicastConnection conn = c.getConnection(); 416 manager.discardConnection(conn); 417 } 418 writeExternal(ObjectOutput out)419 public void writeExternal(ObjectOutput out) throws IOException 420 { 421 if (manager == null) 422 { 423 throw new IOException("no connection"); 424 } 425 manager.write(out); 426 objid.write(out); 427 // This byte is somewhat confusing when interoperating with JDK 428 out.writeByte(0); // RETURN_ACK); 429 } 430 readExternal(ObjectInput in)431 public void readExternal(ObjectInput in) throws IOException, 432 ClassNotFoundException 433 { 434 manager = UnicastConnectionManager.read(in); 435 objid = ObjID.read(in); 436 byte ack = in.readByte(); 437 // This byte is somewhat confusing when interoperating with JDK 438 if (ack != RETURN_ACK && ack != 0/* jdk ack value */) 439 { 440 throw new IOException("no ack found"); 441 } 442 443 // Notify the DGC of the remote side that we hold the reference to the 444 // received object. Do not notify if the client and server are on the 445 // same virtual machine. 446 if (manager.serverobj == null) 447 LeaseRenewingTask.scheduleLeases(this); 448 } 449 remoteEquals(RemoteRef ref)450 public boolean remoteEquals(RemoteRef ref) 451 { 452 throw new Error("Not implemented"); 453 } 454 remoteHashCode()455 public int remoteHashCode() 456 { 457 throw new Error("Not implemented"); 458 } 459 getRefClass(ObjectOutput out)460 public String getRefClass(ObjectOutput out) 461 { 462 return ("UnicastRef"); 463 } 464 465 /** 466 * Return the string representing the remote reference information. 467 */ remoteToString()468 public String remoteToString() 469 { 470 if (manager!=null) 471 return manager.toString(); 472 else 473 return "null manager"; 474 } 475 dump(UnicastConnection conn)476 public void dump(UnicastConnection conn) 477 { 478 try 479 { 480 DataInputStream din = conn.getDataInputStream(); 481 for (;;) 482 { 483 int b = din.readUnsignedByte(); 484 System.out.print(Integer.toHexString(b)); 485 if (b >= 32 && b < 128) 486 { 487 System.out.print(": " + (char) b); 488 } 489 System.out.println(); 490 } 491 } 492 catch (IOException _) 493 { 494 } 495 } 496 497 /** 498 * Check if this UnicastRef points to the object as the passed UnicastRef. 499 * Both the object Id and manager must be the same. 500 * 501 * @return true if the passed reference points to the same remote object as 502 * this reference, false otherwise. 503 */ equals(Object other)504 public boolean equals(Object other) 505 { 506 if (other instanceof UnicastRef) 507 { 508 UnicastRef r = (UnicastRef) other; 509 return r.manager.equals(manager) && r.objid.equals(objid); 510 } 511 else 512 return false; 513 } 514 515 /** 516 * Get the hash code of this UnicastRef, combining hash code of the manager 517 * with hash code of the object id. 518 */ hashCode()519 public int hashCode() 520 { 521 return manager.hashCode() ^ objid.hashCode(); 522 } 523 524 } 525