1 /* 2 * Copyright (c) 1997, 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 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 @SuppressWarnings("deprecation") 45 public class ActivatableRef implements RemoteRef { 46 47 private static final long serialVersionUID = 7579060052569229166L; 48 49 protected ActivationID id; 50 protected RemoteRef ref; 51 transient boolean force = false; 52 53 private static final int MAX_RETRIES = 3; 54 private static final String versionComplaint = 55 "activation requires 1.2 stubs"; 56 57 /** 58 * Create a new (empty) ActivatableRef 59 */ 60 public ActivatableRef() 61 {} 62 63 /** 64 * Create a ActivatableRef with the specified id 65 */ 66 public ActivatableRef(ActivationID id, RemoteRef ref) 67 { 68 this.id = id; 69 this.ref = ref; 70 } 71 72 /** 73 * Returns the stub for the remote object whose class is 74 * specified in the activation descriptor. The ActivatableRef 75 * in the resulting stub has its activation id set to the 76 * activation id supplied as the second argument. 77 */ 78 public static Remote getStub(ActivationDesc desc, ActivationID id) 79 throws StubNotFoundException 80 { 81 String className = desc.getClassName(); 82 83 try { 84 Class<?> cl = 85 RMIClassLoader.loadClass(desc.getLocation(), className); 86 RemoteRef clientRef = new ActivatableRef(id, null); 87 return Util.createProxy(cl, clientRef, false); 88 89 } catch (IllegalArgumentException e) { 90 throw new StubNotFoundException( 91 "class implements an illegal remote interface", e); 92 93 } catch (ClassNotFoundException e) { 94 throw new StubNotFoundException("unable to load class: " + 95 className, e); 96 } catch (MalformedURLException e) { 97 throw new StubNotFoundException("malformed URL", e); 98 } 99 } 100 101 /** 102 * Invoke method on remote object. This method delegates remote 103 * method invocation to the underlying ref type. If the 104 * underlying reference is not known (is null), then the object 105 * must be activated first. If an attempt at method invocation 106 * fails, the object should force reactivation. Method invocation 107 * must preserve "at most once" call semantics. In RMI, "at most 108 * once" applies to parameter deserialization at the remote site 109 * and the remote object's method execution. "At most once" does 110 * not apply to parameter serialization at the client so the 111 * parameters of a call don't need to be buffered in anticipation 112 * of call retry. Thus, a method call is only be retried if the 113 * initial method invocation does not execute at all at the server 114 * (including parameter deserialization). 115 */ 116 public Object invoke(Remote obj, 117 java.lang.reflect.Method method, 118 Object[] params, 119 long opnum) 120 throws Exception 121 { 122 123 boolean force = false; 124 RemoteRef localRef; 125 Exception exception = null; 126 127 /* 128 * Attempt object activation if active ref is unknown. 129 * Throws a RemoteException if object can't be activated. 130 */ 131 synchronized (this) { 132 if (ref == null) { 133 localRef = activate(force); 134 force = true; 135 } else { 136 localRef = ref; 137 } 138 } 139 140 for (int retries = MAX_RETRIES; retries > 0; retries--) { 141 142 try { 143 return localRef.invoke(obj, method, params, opnum); 144 } catch (NoSuchObjectException e) { 145 /* 146 * Object is not active in VM; retry call 147 */ 148 exception = e; 149 } catch (ConnectException e) { 150 /* 151 * Failure during connection setup; retry call 152 */ 153 exception = e; 154 } catch (UnknownHostException e) { 155 /* 156 * Failure during connection setup; retry call. 157 */ 158 exception = e; 159 } catch (ConnectIOException e) { 160 /* 161 * Failure reusing 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 }