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