1 /* 2 * Copyright (c) 1997, 2013, 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 */ ActivatableRef()60 public ActivatableRef() 61 {} 62 63 /** 64 * Create a ActivatableRef with the specified id 65 */ ActivatableRef(ActivationID id, RemoteRef ref)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 */ getStub(ActivationDesc desc, ActivationID id)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 */ invoke(Remote obj, java.lang.reflect.Method method, Object[] params, long opnum)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 setting up multiplexed connection or reusing 162 * cached connection; retry call 163 */ 164 exception = e; 165 } catch (MarshalException e) { 166 /* 167 * Failure during parameter serialization; call may 168 * have reached server, so call retry not possible. 169 */ 170 throw e; 171 } catch (ServerError e) { 172 /* 173 * Call reached server; propagate remote exception. 174 */ 175 throw e; 176 } catch (ServerException e) { 177 /* 178 * Call reached server; propagate remote exception 179 */ 180 throw e; 181 } catch (RemoteException e) { 182 /* 183 * This is a catch-all for other RemoteExceptions. 184 * UnmarshalException being the only one relevant. 185 * 186 * StubNotFoundException should never show up because 187 * it is generally thrown when attempting to locate 188 * a stub. 189 * 190 * UnexpectedException should never show up because 191 * it is only thrown by a stub and would be wrapped 192 * in a ServerException if it was propagated by a 193 * remote call. 194 */ 195 synchronized (this) { 196 if (localRef == ref) { 197 ref = null; // this may be overly conservative 198 } 199 } 200 201 throw e; 202 } 203 204 if (retries > 1) { 205 /* 206 * Activate object, since object could not be reached. 207 */ 208 synchronized (this) { 209 if (localRef.remoteEquals(ref) || ref == null) { 210 RemoteRef newRef = activate(force); 211 212 if (newRef.remoteEquals(localRef) && 213 exception instanceof NoSuchObjectException && 214 force == false) { 215 /* 216 * If last exception was NoSuchObjectException, 217 * then old value of ref is definitely wrong, 218 * so make sure that it is different. 219 */ 220 newRef = activate(true); 221 } 222 223 localRef = newRef; 224 force = true; 225 } else { 226 localRef = ref; 227 force = false; 228 } 229 } 230 } 231 } 232 233 /* 234 * Retries unsuccessful, so throw last exception 235 */ 236 throw exception; 237 } 238 239 /** 240 * private method to obtain the ref for a call. 241 */ getRef()242 private synchronized RemoteRef getRef() 243 throws RemoteException 244 { 245 if (ref == null) { 246 ref = activate(false); 247 } 248 249 return ref; 250 } 251 252 /** 253 * private method to activate the remote object. 254 * 255 * NOTE: the caller must be synchronized on "this" before 256 * calling this method. 257 */ activate(boolean force)258 private RemoteRef activate(boolean force) 259 throws RemoteException 260 { 261 assert Thread.holdsLock(this); 262 263 ref = null; 264 try { 265 /* 266 * Activate the object and retrieve the remote reference 267 * from inside the stub returned as the result. Then 268 * set this activatable ref's internal ref to be the 269 * ref inside the ref of the stub. In more clear terms, 270 * the stub returned from the activate call contains an 271 * ActivatableRef. We need to set the ref in *this* 272 * ActivatableRef to the ref inside the ActivatableRef 273 * retrieved from the stub. The ref type embedded in the 274 * ActivatableRef is typically a UnicastRef. 275 */ 276 277 Remote proxy = id.activate(force); 278 ActivatableRef newRef = null; 279 280 if (proxy instanceof RemoteStub) { 281 newRef = (ActivatableRef) ((RemoteStub) proxy).getRef(); 282 } else { 283 /* 284 * Assume that proxy is an instance of a dynamic proxy 285 * class. If that assumption is not correct, or either of 286 * the casts below fails, the resulting exception will be 287 * wrapped in an ActivateFailedException below. 288 */ 289 RemoteObjectInvocationHandler handler = 290 (RemoteObjectInvocationHandler) 291 Proxy.getInvocationHandler(proxy); 292 newRef = (ActivatableRef) handler.getRef(); 293 } 294 ref = newRef.ref; 295 return ref; 296 297 } catch (ConnectException e) { 298 throw new ConnectException("activation failed", e); 299 } catch (RemoteException e) { 300 throw new ConnectIOException("activation failed", e); 301 } catch (UnknownObjectException e) { 302 throw new NoSuchObjectException("object not registered"); 303 } catch (ActivationException e) { 304 throw new ActivateFailedException("activation failed", e); 305 } 306 } 307 308 /** 309 * This call is used by the old 1.1 stub protocol and is 310 * unsupported since activation requires 1.2 stubs. 311 */ newCall(RemoteObject obj, Operation[] ops, int opnum, long hash)312 public synchronized RemoteCall newCall(RemoteObject obj, 313 Operation[] ops, 314 int opnum, 315 long hash) 316 throws RemoteException 317 { 318 throw new UnsupportedOperationException(versionComplaint); 319 } 320 321 /** 322 * This call is used by the old 1.1 stub protocol and is 323 * unsupported since activation requires 1.2 stubs. 324 */ invoke(RemoteCall call)325 public void invoke(RemoteCall call) throws Exception 326 { 327 throw new UnsupportedOperationException(versionComplaint); 328 } 329 330 /** 331 * This call is used by the old 1.1 stub protocol and is 332 * unsupported since activation requires 1.2 stubs. 333 */ done(RemoteCall call)334 public void done(RemoteCall call) throws RemoteException { 335 throw new UnsupportedOperationException(versionComplaint); 336 } 337 338 /** 339 * Returns the class of the ref type to be serialized 340 */ getRefClass(ObjectOutput out)341 public String getRefClass(ObjectOutput out) 342 { 343 return "ActivatableRef"; 344 } 345 346 /** 347 * Write out external representation for remote ref. 348 */ writeExternal(ObjectOutput out)349 public void writeExternal(ObjectOutput out) throws IOException 350 { 351 RemoteRef localRef = ref; 352 353 out.writeObject(id); 354 if (localRef == null) { 355 out.writeUTF(""); 356 } else { 357 out.writeUTF(localRef.getRefClass(out)); 358 localRef.writeExternal(out); 359 } 360 } 361 362 /** 363 * Read in external representation for remote ref. 364 * @exception ClassNotFoundException If the class for an object 365 * being restored cannot be found. 366 */ readExternal(ObjectInput in)367 public void readExternal(ObjectInput in) 368 throws IOException, ClassNotFoundException 369 { 370 id = (ActivationID)in.readObject(); 371 ref = null; 372 String className = in.readUTF(); 373 374 if (className.equals("")) return; 375 376 try { 377 Class<?> refClass = Class.forName(RemoteRef.packagePrefix + "." + 378 className); 379 ref = (RemoteRef)refClass.newInstance(); 380 ref.readExternal(in); 381 } catch (InstantiationException e) { 382 throw new UnmarshalException("Unable to create remote reference", 383 e); 384 } catch (IllegalAccessException e) { 385 throw new UnmarshalException("Illegal access creating remote reference"); 386 } 387 } 388 389 //----------------------------------------------------------------------; 390 /** 391 * Method from object, forward from RemoteObject 392 */ remoteToString()393 public String remoteToString() { 394 return Util.getUnqualifiedName(getClass()) + 395 " [remoteRef: " + ref + "]"; 396 } 397 398 /** 399 * default implementation of hashCode for remote objects 400 */ remoteHashCode()401 public int remoteHashCode() { 402 return id.hashCode(); 403 } 404 405 /** default implementation of equals for remote objects 406 */ remoteEquals(RemoteRef ref)407 public boolean remoteEquals(RemoteRef ref) { 408 if (ref instanceof ActivatableRef) 409 return id.equals(((ActivatableRef)ref).id); 410 return false; 411 } 412 } 413