1 /* 2 * Copyright (c) 1997, 2003, 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.Proxy; 32 import java.net.MalformedURLException; 33 import java.net.URL; 34 import java.rmi.*; 35 import java.rmi.activation.*; 36 import java.rmi.server.Operation; 37 import java.rmi.server.RMIClassLoader; 38 import java.rmi.server.RemoteCall; 39 import java.rmi.server.RemoteObject; 40 import java.rmi.server.RemoteObjectInvocationHandler; 41 import java.rmi.server.RemoteRef; 42 import java.rmi.server.RemoteStub; 43 44 public class ActivatableRef implements RemoteRef { 45 46 private static final long serialVersionUID = 7579060052569229166L; 47 48 protected ActivationID id; 49 protected RemoteRef ref; 50 transient boolean force = false; 51 52 private static final int MAX_RETRIES = 3; 53 private static final String versionComplaint = 54 "activation requires 1.2 stubs"; 55 56 /** 57 * Create a new (empty) ActivatableRef 58 */ 59 public ActivatableRef() 60 {} 61 62 /** 63 * Create a ActivatableRef with the specified id 64 */ 65 public ActivatableRef(ActivationID id, RemoteRef ref) 66 { 67 this.id = id; 68 this.ref = ref; 69 } 70 71 /** 72 * Returns the stub for the remote object whose class is 73 * specified in the activation descriptor. The ActivatableRef 74 * in the resulting stub has its activation id set to the 75 * activation id supplied as the second argument. 76 */ 77 public static Remote getStub(ActivationDesc desc, ActivationID id) 78 throws StubNotFoundException 79 { 80 String className = desc.getClassName(); 81 82 try { 83 Class<?> cl = 84 RMIClassLoader.loadClass(desc.getLocation(), className); 85 RemoteRef clientRef = new ActivatableRef(id, null); 86 return Util.createProxy(cl, clientRef, false); 87 88 } catch (IllegalArgumentException e) { 89 throw new StubNotFoundException( 90 "class implements an illegal remote interface", e); 91 92 } catch (ClassNotFoundException e) { 93 throw new StubNotFoundException("unable to load class: " + 94 className, e); 95 } catch (MalformedURLException e) { 96 throw new StubNotFoundException("malformed URL", e); 97 } 98 } 99 100 /** 101 * Invoke method on remote object. This method delegates remote 102 * method invocation to the underlying ref type. If the 103 * underlying reference is not known (is null), then the object 104 * must be activated first. If an attempt at method invocation 105 * fails, the object should force reactivation. Method invocation 106 * must preserve "at most once" call semantics. In RMI, "at most 107 * once" applies to parameter deserialization at the remote site 108 * and the remote object's method execution. "At most once" does 109 * not apply to parameter serialization at the client so the 110 * parameters of a call don't need to be buffered in anticipation 111 * of call retry. Thus, a method call is only be retried if the 112 * initial method invocation does not execute at all at the server 113 * (including parameter deserialization). 114 */ 115 public Object invoke(Remote obj, 116 java.lang.reflect.Method method, 117 Object[] params, 118 long opnum) 119 throws Exception 120 { 121 122 boolean force = false; 123 RemoteRef localRef; 124 Exception exception = null; 125 126 /* 127 * Attempt object activation if active ref is unknown. 128 * Throws a RemoteException if object can't be activated. 129 */ 130 synchronized (this) { 131 if (ref == null) { 132 localRef = activate(force); 133 force = true; 134 } else { 135 localRef = ref; 136 } 137 } 138 139 for (int retries = MAX_RETRIES; retries > 0; retries--) { 140 141 try { 142 return localRef.invoke(obj, method, params, opnum); 143 } catch (NoSuchObjectException e) { 144 /* 145 * Object is not active in VM; retry call 146 */ 147 exception = e; 148 } catch (ConnectException e) { 149 /* 150 * Failure during connection setup; retry call 151 */ 152 exception = e; 153 } catch (UnknownHostException e) { 154 /* 155 * Failure during connection setup; retry call. 156 */ 157 exception = e; 158 } catch (ConnectIOException e) { 159 /* 160 * Failure setting up multiplexed connection or reusing 161 * cached connection; retry call 162 */ 163 exception = e; 164 } catch (MarshalException e) { 165 /* 166 * Failure during parameter serialization; call may 167 * have reached server, so call retry not possible. 168 */ 169 throw e; 170 } catch (ServerError e) { 171 /* 172 * Call reached server; propagate remote exception. 173 */ 174 throw e; 175 } catch (ServerException e) { 176 /* 177 * Call reached server; propagate remote exception 178 */ 179 throw e; 180 } catch (RemoteException e) { 181 /* 182 * This is a catch-all for other RemoteExceptions. 183 * UnmarshalException being the only one relevant. 184 * 185 * StubNotFoundException should never show up because 186 * it is generally thrown when attempting to locate 187 * a stub. 188 * 189 * UnexpectedException should never show up because 190 * it is only thrown by a stub and would be wrapped 191 * in a ServerException if it was propagated by a 192 * remote call. 193 */ 194 synchronized (this) { 195 if (localRef == ref) { 196 ref = null; // this may be overly conservative 197 } 198 } 199 200 throw e; 201 } 202 203 if (retries > 1) { 204 /* 205 * Activate object, since object could not be reached. 206 */ 207 synchronized (this) { 208 if (localRef.remoteEquals(ref) || ref == null) { 209 RemoteRef newRef = activate(force); 210 211 if (newRef.remoteEquals(localRef) && 212 exception instanceof NoSuchObjectException && 213 force == false) { 214 /* 215 * If last exception was NoSuchObjectException, 216 * then old value of ref is definitely wrong, 217 * so make sure that it is different. 218 */ 219 newRef = activate(true); 220 } 221 222 localRef = newRef; 223 force = true; 224 } else { 225 localRef = ref; 226 force = false; 227 } 228 } 229 } 230 } 231 232 /* 233 * Retries unsuccessful, so throw last exception 234 */ 235 throw exception; 236 } 237 238 /** 239 * private method to obtain the ref for a call. 240 */ 241 private synchronized RemoteRef getRef() 242 throws RemoteException 243 { 244 if (ref == null) { 245 ref = activate(false); 246 } 247 248 return ref; 249 } 250 251 /** 252 * private method to activate the remote object. 253 * 254 * NOTE: the caller must be synchronized on "this" before 255 * calling this method. 256 */ 257 private RemoteRef activate(boolean force) 258 throws RemoteException 259 { 260 assert Thread.holdsLock(this); 261 262 ref = null; 263 try { 264 /* 265 * Activate the object and retrieve the remote reference 266 * from inside the stub returned as the result. Then 267 * set this activatable ref's internal ref to be the 268 * ref inside the ref of the stub. In more clear terms, 269 * the stub returned from the activate call contains an 270 * ActivatableRef. We need to set the ref in *this* 271 * ActivatableRef to the ref inside the ActivatableRef 272 * retrieved from the stub. The ref type embedded in the 273 * ActivatableRef is typically a UnicastRef. 274 */ 275 276 Remote proxy = id.activate(force); 277 ActivatableRef newRef = null; 278 279 if (proxy instanceof RemoteStub) { 280 newRef = (ActivatableRef) ((RemoteStub) proxy).getRef(); 281 } else { 282 /* 283 * Assume that proxy is an instance of a dynamic proxy 284 * class. If that assumption is not correct, or either of 285 * the casts below fails, the resulting exception will be 286 * wrapped in an ActivateFailedException below. 287 */ 288 RemoteObjectInvocationHandler handler = 289 (RemoteObjectInvocationHandler) 290 Proxy.getInvocationHandler(proxy); 291 newRef = (ActivatableRef) handler.getRef(); 292 } 293 ref = newRef.ref; 294 return ref; 295 296 } catch (ConnectException e) { 297 throw new ConnectException("activation failed", e); 298 } catch (RemoteException e) { 299 throw new ConnectIOException("activation failed", e); 300 } catch (UnknownObjectException e) { 301 throw new NoSuchObjectException("object not registered"); 302 } catch (ActivationException e) { 303 throw new ActivateFailedException("activation failed", e); 304 } 305 } 306 307 /** 308 * This call is used by the old 1.1 stub protocol and is 309 * unsupported since activation requires 1.2 stubs. 310 */ 311 public synchronized RemoteCall newCall(RemoteObject obj, 312 Operation[] ops, 313 int opnum, 314 long hash) 315 throws RemoteException 316 { 317 throw new UnsupportedOperationException(versionComplaint); 318 } 319 320 /** 321 * This call is used by the old 1.1 stub protocol and is 322 * unsupported since activation requires 1.2 stubs. 323 */ 324 public void invoke(RemoteCall call) throws Exception 325 { 326 throw new UnsupportedOperationException(versionComplaint); 327 } 328 329 /** 330 * This call is used by the old 1.1 stub protocol and is 331 * unsupported since activation requires 1.2 stubs. 332 */ 333 public void done(RemoteCall call) throws RemoteException { 334 throw new UnsupportedOperationException(versionComplaint); 335 } 336 337 /** 338 * Returns the class of the ref type to be serialized 339 */ 340 public String getRefClass(ObjectOutput out) 341 { 342 return "ActivatableRef"; 343 } 344 345 /** 346 * Write out external representation for remote ref. 347 */ 348 public void writeExternal(ObjectOutput out) throws IOException 349 { 350 RemoteRef localRef = ref; 351 352 out.writeObject(id); 353 if (localRef == null) { 354 out.writeUTF(""); 355 } else { 356 out.writeUTF(localRef.getRefClass(out)); 357 localRef.writeExternal(out); 358 } 359 } 360 361 /** 362 * Read in external representation for remote ref. 363 * @exception ClassNotFoundException If the class for an object 364 * being restored cannot be found. 365 */ 366 public void readExternal(ObjectInput in) 367 throws IOException, ClassNotFoundException 368 { 369 id = (ActivationID)in.readObject(); 370 ref = null; 371 String className = in.readUTF(); 372 373 if (className.equals("")) return; 374 375 try { 376 Class<?> refClass = Class.forName(RemoteRef.packagePrefix + "." + 377 className); 378 ref = (RemoteRef)refClass.newInstance(); 379 ref.readExternal(in); 380 } catch (InstantiationException e) { 381 throw new UnmarshalException("Unable to create remote reference", 382 e); 383 } catch (IllegalAccessException e) { 384 throw new UnmarshalException("Illegal access creating remote reference"); 385 } 386 } 387 388 //----------------------------------------------------------------------; 389 /** 390 * Method from object, forward from RemoteObject 391 */ 392 public String remoteToString() { 393 return Util.getUnqualifiedName(getClass()) + 394 " [remoteRef: " + ref + "]"; 395 } 396 397 /** 398 * default implementation of hashCode for remote objects 399 */ 400 public int remoteHashCode() { 401 return id.hashCode(); 402 } 403 404 /** default implementation of equals for remote objects 405 */ 406 public boolean remoteEquals(RemoteRef ref) { 407 if (ref instanceof ActivatableRef) 408 return id.equals(((ActivatableRef)ref).id); 409 return false; 410 } 411 }