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