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 }