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