1 /*
   2  * Copyright (c) 1996, 2012, 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.transport;
  27 
  28 import java.rmi.server.UID;
  29 import java.security.AccessController;
  30 import java.security.PrivilegedAction;
  31 import java.util.ArrayList;
  32 import java.util.Collections;
  33 import java.util.HashMap;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.concurrent.Future;
  37 import java.util.concurrent.ScheduledExecutorService;
  38 import java.util.concurrent.TimeUnit;
  39 import sun.rmi.runtime.RuntimeUtil;
  40 
  41 /**
  42  * Holds strong references to a set of remote objects, or live remote
  43  * references to remote objects, after they have been marshalled (as
  44  * remote references) as parts of the arguments or the result of a
  45  * remote invocation.  The purpose is to prevent remote objects or
  46  * live remote references that might otherwise be determined to be
  47  * unreachable in this VM from being locally garbage collected before
  48  * the receiver has had an opportunity to register the unmarshalled
  49  * remote references for DGC.
  50  *
  51  * The references are held strongly until an acknowledgment has been
  52  * received that the receiver has had an opportunity to process the
  53  * remote references or until a timeout has expired.  For remote
  54  * references sent as parts of the arguments of a remote invocation,
  55  * the acknowledgment is the beginning of the response indicating
  56  * completion of the remote invocation.  For remote references sent as
  57  * parts of the result of a remote invocation, a UID is included as
  58  * part of the result, and the acknowledgment is a transport-level
  59  * "DGCAck" message containing that UID.
  60  *
  61  * @author      Ann Wollrath
  62  * @author      Peter Jones
  63  **/
  64 public class DGCAckHandler {
  65 
  66     /** timeout for holding references without receiving an acknowledgment */
  67     private static final long dgcAckTimeout =           // default 5 minutes
  68         AccessController.doPrivileged((PrivilegedAction<Long>) () ->
  69             Long.getLong("sun.rmi.dgc.ackTimeout", 300000));
  70 
  71     /** thread pool for scheduling delayed tasks */
  72     private static final ScheduledExecutorService scheduler =
  73         AccessController.doPrivileged(
  74             new RuntimeUtil.GetInstanceAction()).getScheduler();
  75 
  76     /** table mapping ack ID to handler */
  77     private static final Map<UID,DGCAckHandler> idTable =
  78         Collections.synchronizedMap(new HashMap<UID,DGCAckHandler>());
  79 
  80     private final UID id;
  81     private List<Object> objList = new ArrayList<>(); // null if released
  82     private Future<?> task = null;
  83 
  84     /**
  85      * Creates a new DGCAckHandler, associated with the specified UID
  86      * if the argument is not null.
  87      *
  88      * References added to this DGCAckHandler will be held strongly
  89      * until its "release" method is invoked or (after the
  90      * "startTimer" method has been invoked) the timeout has expired.
  91      * If the argument is not null, then invoking the static
  92      * "received" method with the specified UID is equivalent to
  93      * invoking this instance's "release" method.
  94      **/
  95     DGCAckHandler(UID id) {
  96         this.id = id;
  97         if (id != null) {
  98             assert !idTable.containsKey(id);
  99             idTable.put(id, this);
 100         }
 101     }
 102 
 103     /**
 104      * Adds the specified reference to this DGCAckHandler.
 105      **/
 106     synchronized void add(Object obj) {
 107         if (objList != null) {
 108             objList.add(obj);
 109         }
 110     }
 111 
 112     /**
 113      * Starts the timer for this DGCAckHandler.  After the timeout has
 114      * expired, the references are released even if the acknowledgment
 115      * has not been received.
 116      **/
 117     synchronized void startTimer() {
 118         if (objList != null && task == null) {
 119             task = scheduler.schedule(new Runnable() {
 120                 public void run() {
 121                     release();
 122                 }
 123             }, dgcAckTimeout, TimeUnit.MILLISECONDS);
 124         }
 125     }
 126 
 127     /**
 128      * Releases the references held by this DGCAckHandler.
 129      **/
 130     synchronized void release() {
 131         if (task != null) {
 132             task.cancel(false);
 133             task = null;
 134         }
 135         objList = null;
 136     }
 137 
 138     /**
 139      * Causes the DGCAckHandler associated with the specified UID to
 140      * release its references.
 141      **/
 142     public static void received(UID id) {
 143         DGCAckHandler h = idTable.remove(id);
 144         if (h != null) {
 145             h.release();
 146         }
 147     }
 148 }