1 /*
   2  * Copyright (c) 1996, 2008, 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 package sun.rmi.transport;
  26 
  27 import java.rmi.Remote;
  28 import java.rmi.NoSuchObjectException;
  29 import java.rmi.dgc.VMID;
  30 import java.rmi.server.ObjID;
  31 import java.rmi.server.Unreferenced;
  32 import java.security.AccessControlContext;
  33 import java.security.AccessController;
  34 import java.util.*;
  35 import sun.rmi.runtime.Log;
  36 import sun.rmi.runtime.NewThreadAction;
  37 import sun.rmi.server.Dispatcher;
  38 
  39 /**
  40  * A target contains information pertaining to a remote object that
  41  * resides in this address space.  Targets are located via the
  42  * ObjectTable.
  43  */
  44 public final class Target {
  45     /** object id for target */
  46     private final ObjID id;
  47     /** flag indicating whether target is subject to collection */
  48     private final boolean permanent;
  49     /** weak reference to remote object implementation */
  50     private final WeakRef weakImpl;
  51     /** dispatcher for remote object */
  52     private volatile Dispatcher disp;
  53     /** stub for remote object */
  54     private final Remote stub;
  55     /** set of clients that hold references to this target */
  56     private final Vector<VMID> refSet = new Vector<>();
  57     /** table that maps client endpoints to sequence numbers */
  58     private final Hashtable<VMID, SequenceEntry> sequenceTable =
  59         new Hashtable<>(5);
  60     /** access control context in which target was created */
  61     private final AccessControlContext acc;
  62     /** context class loader in which target was created */
  63     private final ClassLoader ccl;
  64     /** number of pending/executing calls */
  65     private int callCount = 0;
  66     /** true if this target has been removed from the object table */
  67     private boolean removed = false;
  68     /**
  69      * the transport through which this target was exported and
  70      * through which remote calls will be allowed
  71      */
  72     private volatile Transport exportedTransport = null;
  73 
  74     /** number to identify next callback thread created here */
  75     private static int nextThreadNum = 0;
  76 
  77     /**
  78      * Construct a Target for a remote object "impl" with
  79      * a specific object id.
  80      *
  81      * If "permanent" is true, then the impl is pinned permanently
  82      * (the impl will not be collected via distributed and/or local
  83      * GC).  If "on" is false, than the impl is subject to
  84      * collection. Permanent objects do not keep a server from
  85      * exiting.
  86      */
  87     public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id,
  88                   boolean permanent)
  89     {
  90         this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue);
  91         this.disp = disp;
  92         this.stub = stub;
  93         this.id = id;
  94         this.acc = AccessController.getContext();
  95 
  96         /*
  97          * Fix for 4149366: so that downloaded parameter types unmarshalled
  98          * for this impl will be compatible with types known only to the
  99          * impl class's class loader (when it's not identical to the
 100          * exporting thread's context class loader), mark the impl's class
 101          * loader as the loader to use as the context class loader in the
 102          * server's dispatch thread while a call to this impl is being
 103          * processed (unless this exporting thread's context class loader is
 104          * a child of the impl's class loader, such as when a registry is
 105          * exported by an application, in which case this thread's context
 106          * class loader is preferred).
 107          */
 108         ClassLoader threadContextLoader =
 109             Thread.currentThread().getContextClassLoader();
 110         ClassLoader serverLoader = impl.getClass().getClassLoader();
 111         if (checkLoaderAncestry(threadContextLoader, serverLoader)) {
 112             this.ccl = threadContextLoader;
 113         } else {
 114             this.ccl = serverLoader;
 115         }
 116 
 117         this.permanent = permanent;
 118         if (permanent) {
 119             pinImpl();
 120         }
 121     }
 122 
 123     /**
 124      * Return true if the first class loader is a child of (or identical
 125      * to) the second class loader.  Either loader may be "null", which is
 126      * considered to be the parent of any non-null class loader.
 127      *
 128      * (utility method added for the 1.2beta4 fix for 4149366)
 129      */
 130     private static boolean checkLoaderAncestry(ClassLoader child,
 131                                                ClassLoader ancestor)
 132     {
 133         if (ancestor == null) {
 134             return true;
 135         } else if (child == null) {
 136             return false;
 137         } else {
 138             for (ClassLoader parent = child;
 139                  parent != null;
 140                  parent = parent.getParent())
 141             {
 142                 if (parent == ancestor) {
 143                     return true;
 144                 }
 145             }
 146             return false;
 147         }
 148     }
 149 
 150     /** Get the stub (proxy) object for this target
 151      */
 152     public Remote getStub() {
 153         return stub;
 154     }
 155 
 156     /**
 157      * Returns the object endpoint for the target.
 158      */
 159     ObjectEndpoint getObjectEndpoint() {
 160         return new ObjectEndpoint(id, exportedTransport);
 161     }
 162 
 163     /**
 164      * Get the weak reference for the Impl of this target.
 165      */
 166     WeakRef getWeakImpl() {
 167         return weakImpl;
 168     }
 169 
 170     /**
 171      * Returns the dispatcher for this remote object target.
 172      */
 173     Dispatcher getDispatcher() {
 174         return disp;
 175     }
 176 
 177     AccessControlContext getAccessControlContext() {
 178         return acc;
 179     }
 180 
 181     ClassLoader getContextClassLoader() {
 182         return ccl;
 183     }
 184 
 185     /**
 186      * Get the impl for this target.
 187      * Note: this may return null if the impl has been garbage collected.
 188      * (currently, there is no need to make this method public)
 189      */
 190     Remote getImpl() {
 191         return (Remote)weakImpl.get();
 192     }
 193 
 194     /**
 195      * Returns true if the target is permanent.
 196      */
 197     boolean isPermanent() {
 198         return permanent;
 199     }
 200 
 201     /**
 202      * Pin impl in target. Pin the WeakRef object so it holds a strong
 203      * reference to the object to it will not be garbage collected locally.
 204      * This way there is a single object responsible for the weak ref
 205      * mechanism.
 206      */
 207     synchronized void pinImpl() {
 208         weakImpl.pin();
 209     }
 210 
 211     /**
 212      * Unpin impl in target.  Weaken the reference to impl so that it
 213      * can be garbage collected locally. But only if there the refSet
 214      * is empty.  All of the weak/strong handling is in WeakRef
 215      */
 216     synchronized void unpinImpl() {
 217         /* only unpin if:
 218          * a) impl is not permanent, and
 219          * b) impl is not already unpinned, and
 220          * c) there are no external references (outside this
 221          *    address space) for the impl
 222          */
 223         if (!permanent && refSet.isEmpty()) {
 224             weakImpl.unpin();
 225         }
 226     }
 227 
 228     /**
 229      * Enable the transport through which remote calls to this target
 230      * are allowed to be set if it has not already been set.
 231      */
 232     void setExportedTransport(Transport exportedTransport) {
 233         if (this.exportedTransport == null) {
 234             this.exportedTransport = exportedTransport;
 235         }
 236     }
 237 
 238     /**
 239      * Add an endpoint to the remembered set.  Also adds a notifier
 240      * to call back if the address space associated with the endpoint
 241      * dies.
 242      */
 243     synchronized void referenced(long sequenceNum, VMID vmid) {
 244         // check sequence number for vmid
 245         SequenceEntry entry = sequenceTable.get(vmid);
 246         if (entry == null) {
 247             sequenceTable.put(vmid, new SequenceEntry(sequenceNum));
 248         } else if (entry.sequenceNum < sequenceNum) {
 249             entry.update(sequenceNum);
 250         } else  {
 251             // late dirty call; ignore.
 252             return;
 253         }
 254 
 255         if (!refSet.contains(vmid)) {
 256             /*
 257              * A Target must be pinned while its refSet is not empty.  It may
 258              * have become unpinned if external LiveRefs only existed in
 259              * serialized form for some period of time, or if a client failed
 260              * to renew its lease due to a transient network failure.  So,
 261              * make sure that it is pinned here; this fixes bugid 4069644.
 262              */
 263             pinImpl();
 264             if (getImpl() == null)      // too late if impl was collected
 265                 return;
 266 
 267             if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
 268                 DGCImpl.dgcLog.log(Log.VERBOSE, "add to dirty set: " + vmid);
 269             }
 270 
 271             refSet.addElement(vmid);
 272 
 273             DGCImpl.getDGCImpl().registerTarget(vmid, this);
 274         }
 275     }
 276 
 277     /**
 278      * Remove endpoint from remembered set.  If set becomes empty,
 279      * remove server from Transport's object table.
 280      */
 281     synchronized void unreferenced(long sequenceNum, VMID vmid, boolean strong)
 282     {
 283         // check sequence number for vmid
 284         SequenceEntry entry = sequenceTable.get(vmid);
 285         if (entry == null || entry.sequenceNum > sequenceNum) {
 286             // late clean call; ignore
 287             return;
 288         } else if (strong) {
 289             // strong clean call; retain sequenceNum
 290             entry.retain(sequenceNum);
 291         } else if (entry.keep == false) {
 292             // get rid of sequence number
 293             sequenceTable.remove(vmid);
 294         }
 295 
 296         if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
 297             DGCImpl.dgcLog.log(Log.VERBOSE, "remove from dirty set: " + vmid);
 298         }
 299 
 300         refSetRemove(vmid);
 301     }
 302 
 303     /**
 304      * Remove endpoint from the reference set.
 305      */
 306     synchronized private void refSetRemove(VMID vmid) {
 307         // remove notification request
 308         DGCImpl.getDGCImpl().unregisterTarget(vmid, this);
 309 
 310         if (refSet.removeElement(vmid) && refSet.isEmpty()) {
 311             // reference set is empty, so server can be garbage collected.
 312             // remove object from table.
 313             if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
 314                 DGCImpl.dgcLog.log(Log.VERBOSE,
 315                     "reference set is empty: target = " + this);
 316             }
 317 
 318             /*
 319              * If the remote object implements the Unreferenced interface,
 320              * invoke its unreferenced callback in a separate thread.
 321              */
 322             Remote obj = getImpl();
 323             if (obj instanceof Unreferenced) {
 324                 final Unreferenced unrefObj = (Unreferenced) obj;
 325                 final Thread t =
 326                     java.security.AccessController.doPrivileged(
 327                         new NewThreadAction(new Runnable() {
 328                             public void run() {
 329                                 unrefObj.unreferenced();
 330                             }
 331                         }, "Unreferenced-" + nextThreadNum++, false, true));
 332                 // REMIND: access to nextThreadNum not synchronized; you care?
 333                 /*
 334                  * We must manually set the context class loader appropriately
 335                  * for threads that may invoke user code (see bugid 4171278).
 336                  */
 337                 java.security.AccessController.doPrivileged(
 338                     new java.security.PrivilegedAction<Void>() {
 339                         public Void run() {
 340                         t.setContextClassLoader(ccl);
 341                         return null;
 342                     }
 343                 });
 344 
 345                 t.start();
 346             }
 347 
 348             unpinImpl();
 349         }
 350     }
 351 
 352     /**
 353      * Mark this target as not accepting new calls if any of the
 354      * following conditions exist: a) the force parameter is true,
 355      * b) the target's call count is zero, or c) the object is already
 356      * not accepting calls. Returns true if target is marked as not
 357      * accepting new calls; returns false otherwise.
 358      */
 359     synchronized boolean unexport(boolean force) {
 360 
 361         if ((force == true) || (callCount == 0) || (disp == null)) {
 362             disp = null;
 363             /*
 364              * Fix for 4331349: unpin object so that it may be gc'd.
 365              * Also, unregister all vmids referencing this target
 366              * so target can be gc'd.
 367              */
 368             unpinImpl();
 369             DGCImpl dgc = DGCImpl.getDGCImpl();
 370             Enumeration<VMID> enum_ = refSet.elements();
 371             while (enum_.hasMoreElements()) {
 372                 VMID vmid = enum_.nextElement();
 373                 dgc.unregisterTarget(vmid, this);
 374             }
 375             return true;
 376         } else {
 377             return false;
 378         }
 379     }
 380 
 381     /**
 382      * Mark this target as having been removed from the object table.
 383      */
 384     synchronized void markRemoved() {
 385         if (!(!removed)) { throw new AssertionError(); }
 386 
 387         removed = true;
 388         if (!permanent && callCount == 0) {
 389             ObjectTable.decrementKeepAliveCount();
 390         }
 391 
 392         if (exportedTransport != null) {
 393             exportedTransport.targetUnexported();
 394         }
 395     }
 396 
 397     /**
 398      * Increment call count.
 399      */
 400     synchronized void incrementCallCount() throws NoSuchObjectException {
 401 
 402         if (disp != null) {
 403             callCount ++;
 404         } else {
 405             throw new NoSuchObjectException("object not accepting new calls");
 406         }
 407     }
 408 
 409     /**
 410      * Decrement call count.
 411      */
 412     synchronized void decrementCallCount() {
 413 
 414         if (--callCount < 0) {
 415             throw new Error("internal error: call count less than zero");
 416         }
 417 
 418         /*
 419          * The "keep-alive count" is the number of non-permanent remote
 420          * objects that are either in the object table or still have calls
 421          * in progress.  Therefore, this state change may affect the
 422          * keep-alive count: if this target is for a non-permanent remote
 423          * object that has been removed from the object table and now has a
 424          * call count of zero, it needs to be decremented.
 425          */
 426         if (!permanent && removed && callCount == 0) {
 427             ObjectTable.decrementKeepAliveCount();
 428         }
 429     }
 430 
 431     /**
 432      * Returns true if remembered set is empty; otherwise returns
 433      * false
 434      */
 435     boolean isEmpty() {
 436         return refSet.isEmpty();
 437     }
 438 
 439     /**
 440      * This method is called if the address space associated with the
 441      * vmid dies.  In that case, the vmid should be removed
 442      * from the reference set.
 443      */
 444     synchronized public void vmidDead(VMID vmid) {
 445         if (DGCImpl.dgcLog.isLoggable(Log.BRIEF)) {
 446             DGCImpl.dgcLog.log(Log.BRIEF, "removing endpoint " +
 447                             vmid + " from reference set");
 448         }
 449 
 450         sequenceTable.remove(vmid);
 451         refSetRemove(vmid);
 452     }
 453 }
 454 
 455 class SequenceEntry {
 456     long sequenceNum;
 457     boolean keep;
 458 
 459     SequenceEntry(long sequenceNum) {
 460         this.sequenceNum = sequenceNum;
 461         keep = false;
 462     }
 463 
 464     void retain(long sequenceNum) {
 465         this.sequenceNum = sequenceNum;
 466         keep = true;
 467     }
 468 
 469     void update(long sequenceNum) {
 470         this.sequenceNum = sequenceNum;
 471     }
 472 }