1 /* 2 * Copyright (c) 1997, 2012, 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.lang.reflect.Constructor; 30 import java.lang.reflect.InvocationTargetException; 31 import java.net.ServerSocket; 32 import java.rmi.MarshalledObject; 33 import java.rmi.NoSuchObjectException; 34 import java.rmi.Remote; 35 import java.rmi.RemoteException; 36 import java.rmi.activation.Activatable; 37 import java.rmi.activation.ActivationDesc; 38 import java.rmi.activation.ActivationException; 39 import java.rmi.activation.ActivationGroup; 40 import java.rmi.activation.ActivationGroupID; 41 import java.rmi.activation.ActivationID; 42 import java.rmi.activation.UnknownObjectException; 43 import java.rmi.server.RMIClassLoader; 44 import java.rmi.server.RMIServerSocketFactory; 45 import java.rmi.server.RMISocketFactory; 46 import java.rmi.server.UnicastRemoteObject; 47 import java.security.AccessController; 48 import java.security.PrivilegedActionException; 49 import java.security.PrivilegedExceptionAction; 50 import java.util.ArrayList; 51 import java.util.Hashtable; 52 import java.util.List; 53 import sun.rmi.registry.RegistryImpl; 54 55 /** 56 * The default activation group implementation. 57 * 58 * @author Ann Wollrath 59 * @since 1.2 60 * @see java.rmi.activation.ActivationGroup 61 */ 62 public class ActivationGroupImpl extends ActivationGroup { 63 64 // use serialVersionUID from JDK 1.2.2 for interoperability 65 private static final long serialVersionUID = 5758693559430427303L; 66 67 /** maps persistent IDs to activated remote objects */ 68 private final Hashtable<ActivationID,ActiveEntry> active = 69 new Hashtable<>(); 70 private boolean groupInactive = false; 71 private final ActivationGroupID groupID; 72 private final List<ActivationID> lockedIDs = new ArrayList<>(); 73 74 /** 75 * Creates a default activation group implementation. 76 * 77 * @param id the group's identifier 78 * @param data ignored 79 */ 80 public ActivationGroupImpl(ActivationGroupID id, MarshalledObject<?> data) 81 throws RemoteException 82 { 83 super(id); 84 groupID = id; 85 86 /* 87 * Unexport activation group impl and attempt to export it on 88 * an unshared anonymous port. See 4692286. 89 */ 90 unexportObject(this, true); 91 RMIServerSocketFactory ssf = new ServerSocketFactoryImpl(); 92 UnicastRemoteObject.exportObject(this, 0, null, ssf); 93 94 if (System.getSecurityManager() == null) { 95 try { 96 // Provide a default security manager. 97 System.setSecurityManager(new SecurityManager()); 98 99 } catch (Exception e) { 100 throw new RemoteException("unable to set security manager", e); 101 } 102 } 103 } 104 105 /** 106 * Trivial server socket factory used to export the activation group 107 * impl on an unshared port. 108 */ 109 private static class ServerSocketFactoryImpl 110 implements RMIServerSocketFactory 111 { 112 public ServerSocket createServerSocket(int port) throws IOException 113 { 114 RMISocketFactory sf = RMISocketFactory.getSocketFactory(); 115 if (sf == null) { 116 sf = RMISocketFactory.getDefaultSocketFactory(); 117 } 118 return sf.createServerSocket(port); 119 } 120 } 121 122 /* 123 * Obtains a lock on the ActivationID id before returning. Allows only one 124 * thread at a time to hold a lock on a particular id. If the lock for id 125 * is in use, all requests for an equivalent (in the Object.equals sense) 126 * id will wait for the id to be notified and use the supplied id as the 127 * next lock. The caller of "acquireLock" must execute the "releaseLock" 128 * method" to release the lock and "notifyAll" waiters for the id lock 129 * obtained from this method. The typical usage pattern is as follows: 130 * 131 * try { 132 * acquireLock(id); 133 * // do stuff pertaining to id... 134 * } finally { 135 * releaseLock(id); 136 * checkInactiveGroup(); 137 * } 138 */ 139 private void acquireLock(ActivationID id) { 140 141 ActivationID waitForID; 142 143 for (;;) { 144 145 synchronized (lockedIDs) { 146 int index = lockedIDs.indexOf(id); 147 if (index < 0) { 148 lockedIDs.add(id); 149 return; 150 } else { 151 waitForID = lockedIDs.get(index); 152 } 153 } 154 155 synchronized (waitForID) { 156 synchronized (lockedIDs) { 157 int index = lockedIDs.indexOf(waitForID); 158 if (index < 0) continue; 159 ActivationID actualID = lockedIDs.get(index); 160 if (actualID != waitForID) 161 /* 162 * don't wait on an id that won't be notified. 163 */ 164 continue; 165 } 166 167 try { 168 waitForID.wait(); 169 } catch (InterruptedException ignore) { 170 } 171 } 172 } 173 174 } 175 176 /* 177 * Releases the id lock obtained via the "acquireLock" method and then 178 * notifies all threads waiting on the lock. 179 */ 180 private void releaseLock(ActivationID id) { 181 synchronized (lockedIDs) { 182 id = lockedIDs.remove(lockedIDs.indexOf(id)); 183 } 184 185 synchronized (id) { 186 id.notifyAll(); 187 } 188 } 189 190 /** 191 * Creates a new instance of an activatable remote object. The 192 * <code>Activator</code> calls this method to create an activatable 193 * object in this group. This method should be idempotent; a call to 194 * activate an already active object should return the previously 195 * activated object. 196 * 197 * Note: this method assumes that the Activator will only invoke 198 * newInstance for the same object in a serial fashion (i.e., 199 * the activator will not allow the group to see concurrent requests 200 * to activate the same object. 201 * 202 * @param id the object's activation identifier 203 * @param desc the object's activation descriptor 204 * @return a marshalled object containing the activated object's stub 205 */ 206 public MarshalledObject<? extends Remote> 207 newInstance(final ActivationID id, 208 final ActivationDesc desc) 209 throws ActivationException, RemoteException 210 { 211 RegistryImpl.checkAccess("ActivationInstantiator.newInstance"); 212 213 if (!groupID.equals(desc.getGroupID())) 214 throw new ActivationException("newInstance in wrong group"); 215 216 try { 217 acquireLock(id); 218 synchronized (this) { 219 if (groupInactive == true) 220 throw new InactiveGroupException("group is inactive"); 221 } 222 223 ActiveEntry entry = active.get(id); 224 if (entry != null) 225 return entry.mobj; 226 227 String className = desc.getClassName(); 228 229 final Class<? extends Remote> cl = 230 RMIClassLoader.loadClass(desc.getLocation(), className) 231 .asSubclass(Remote.class); 232 Remote impl = null; 233 234 final Thread t = Thread.currentThread(); 235 final ClassLoader savedCcl = t.getContextClassLoader(); 236 ClassLoader objcl = cl.getClassLoader(); 237 final ClassLoader ccl = covers(objcl, savedCcl) ? objcl : savedCcl; 238 239 /* 240 * Fix for 4164971: allow non-public activatable class 241 * and/or constructor, create the activatable object in a 242 * privileged block 243 */ 244 try { 245 /* 246 * The code below is in a doPrivileged block to 247 * protect against user code which code might have set 248 * a global socket factory (in which case application 249 * code would be on the stack). 250 */ 251 impl = AccessController.doPrivileged( 252 new PrivilegedExceptionAction<Remote>() { 253 public Remote run() throws InstantiationException, 254 NoSuchMethodException, IllegalAccessException, 255 InvocationTargetException 256 { 257 Constructor<? extends Remote> constructor = 258 cl.getDeclaredConstructor( 259 ActivationID.class, MarshalledObject.class); 260 constructor.setAccessible(true); 261 try { 262 /* 263 * Fix for 4289544: make sure to set the 264 * context class loader to be the class 265 * loader of the impl class before 266 * constructing that class. 267 */ 268 t.setContextClassLoader(ccl); 269 return constructor.newInstance(id, 270 desc.getData()); 271 } finally { 272 t.setContextClassLoader(savedCcl); 273 } 274 } 275 }); 276 } catch (PrivilegedActionException pae) { 277 Throwable e = pae.getException(); 278 279 // narrow the exception's type and rethrow it 280 if (e instanceof InstantiationException) { 281 throw (InstantiationException) e; 282 } else if (e instanceof NoSuchMethodException) { 283 throw (NoSuchMethodException) e; 284 } else if (e instanceof IllegalAccessException) { 285 throw (IllegalAccessException) e; 286 } else if (e instanceof InvocationTargetException) { 287 throw (InvocationTargetException) e; 288 } else if (e instanceof RuntimeException) { 289 throw (RuntimeException) e; 290 } else if (e instanceof Error) { 291 throw (Error) e; 292 } 293 } 294 295 entry = new ActiveEntry(impl); 296 active.put(id, entry); 297 return entry.mobj; 298 299 } catch (NoSuchMethodException | NoSuchMethodError e) { 300 /* user forgot to provide activatable constructor? 301 * or code recompiled and user forgot to provide 302 * activatable constructor? 303 */ 304 throw new ActivationException 305 ("Activatable object must provide an activation"+ 306 " constructor", e ); 307 308 } catch (InvocationTargetException e) { 309 throw new ActivationException("exception in object constructor", 310 e.getTargetException()); 311 312 } catch (Exception e) { 313 throw new ActivationException("unable to activate object", e); 314 } finally { 315 releaseLock(id); 316 checkInactiveGroup(); 317 } 318 } 319 320 321 /** 322 * The group's <code>inactiveObject</code> method is called 323 * indirectly via a call to the <code>Activatable.inactive</code> 324 * method. A remote object implementation must call 325 * <code>Activatable</code>'s <code>inactive</code> method when 326 * that object deactivates (the object deems that it is no longer 327 * active). If the object does not call 328 * <code>Activatable.inactive</code> when it deactivates, the 329 * object will never be garbage collected since the group keeps 330 * strong references to the objects it creates. <p> 331 * 332 * The group's <code>inactiveObject</code> method 333 * unexports the remote object from the RMI runtime so that the 334 * object can no longer receive incoming RMI calls. This call will 335 * only succeed if the object has no pending/executing calls. If 336 * the object does have pending/executing RMI calls, then false 337 * will be returned. 338 * 339 * If the object has no pending/executing calls, the object is 340 * removed from the RMI runtime and the group informs its 341 * <code>ActivationMonitor</code> (via the monitor's 342 * <code>inactiveObject</code> method) that the remote object is 343 * not currently active so that the remote object will be 344 * re-activated by the activator upon a subsequent activation 345 * request. 346 * 347 * @param id the object's activation identifier 348 * @returns true if the operation succeeds (the operation will 349 * succeed if the object in currently known to be active and is 350 * either already unexported or is currently exported and has no 351 * pending/executing calls); false is returned if the object has 352 * pending/executing calls in which case it cannot be deactivated 353 * @exception UnknownObjectException if object is unknown (may already 354 * be inactive) 355 * @exception RemoteException if call informing monitor fails 356 */ 357 public boolean inactiveObject(ActivationID id) 358 throws ActivationException, UnknownObjectException, RemoteException 359 { 360 361 try { 362 acquireLock(id); 363 synchronized (this) { 364 if (groupInactive == true) 365 throw new ActivationException("group is inactive"); 366 } 367 368 ActiveEntry entry = active.get(id); 369 if (entry == null) { 370 // REMIND: should this be silent? 371 throw new UnknownObjectException("object not active"); 372 } 373 374 try { 375 if (Activatable.unexportObject(entry.impl, false) == false) 376 return false; 377 } catch (NoSuchObjectException allowUnexportedObjects) { 378 } 379 380 try { 381 super.inactiveObject(id); 382 } catch (UnknownObjectException allowUnregisteredObjects) { 383 } 384 385 active.remove(id); 386 387 } finally { 388 releaseLock(id); 389 checkInactiveGroup(); 390 } 391 392 return true; 393 } 394 395 /* 396 * Determines if the group has become inactive and 397 * marks it as such. 398 */ 399 private void checkInactiveGroup() { 400 boolean groupMarkedInactive = false; 401 synchronized (this) { 402 if (active.size() == 0 && lockedIDs.size() == 0 && 403 groupInactive == false) 404 { 405 groupInactive = true; 406 groupMarkedInactive = true; 407 } 408 } 409 410 if (groupMarkedInactive) { 411 try { 412 super.inactiveGroup(); 413 } catch (Exception ignoreDeactivateFailure) { 414 } 415 416 try { 417 UnicastRemoteObject.unexportObject(this, true); 418 } catch (NoSuchObjectException allowUnexportedGroup) { 419 } 420 } 421 } 422 423 /** 424 * The group's <code>activeObject</code> method is called when an 425 * object is exported (either by <code>Activatable</code> object 426 * construction or an explicit call to 427 * <code>Activatable.exportObject</code>. The group must inform its 428 * <code>ActivationMonitor</code> that the object is active (via 429 * the monitor's <code>activeObject</code> method) if the group 430 * hasn't already done so. 431 * 432 * @param id the object's identifier 433 * @param obj the remote object implementation 434 * @exception UnknownObjectException if object is not registered 435 * @exception RemoteException if call informing monitor fails 436 */ 437 public void activeObject(ActivationID id, Remote impl) 438 throws ActivationException, UnknownObjectException, RemoteException 439 { 440 441 try { 442 acquireLock(id); 443 synchronized (this) { 444 if (groupInactive == true) 445 throw new ActivationException("group is inactive"); 446 } 447 if (!active.contains(id)) { 448 ActiveEntry entry = new ActiveEntry(impl); 449 active.put(id, entry); 450 // created new entry, so inform monitor of active object 451 try { 452 super.activeObject(id, entry.mobj); 453 } catch (RemoteException e) { 454 // daemon can still find it by calling newInstance 455 } 456 } 457 } finally { 458 releaseLock(id); 459 checkInactiveGroup(); 460 } 461 } 462 463 /** 464 * Entry in table for active object. 465 */ 466 private static class ActiveEntry { 467 Remote impl; 468 MarshalledObject<Remote> mobj; 469 470 ActiveEntry(Remote impl) throws ActivationException { 471 this.impl = impl; 472 try { 473 this.mobj = new MarshalledObject<Remote>(impl); 474 } catch (IOException e) { 475 throw new 476 ActivationException("failed to marshal remote object", e); 477 } 478 } 479 } 480 481 /** 482 * Returns true if the first argument is either equal to, or is a 483 * descendant of, the second argument. Null is treated as the root of 484 * the tree. 485 */ 486 private static boolean covers(ClassLoader sub, ClassLoader sup) { 487 if (sup == null) { 488 return true; 489 } else if (sub == null) { 490 return false; 491 } 492 do { 493 if (sub == sup) { 494 return true; 495 } 496 sub = sub.getParent(); 497 } while (sub != null); 498 return false; 499 } 500 }