1 /*
2  * Copyright (c) 1996, 2016, 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      **/
DGCAckHandler(UID id)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      **/
add(Object obj)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      **/
startTimer()117     synchronized void startTimer() {
118         if (objList != null && task == null) {
119             task = scheduler.schedule(new Runnable() {
120                 public void run() {
121                     if (id != null) {
122                         idTable.remove(id);
123                     }
124                     release();
125                 }
126             }, dgcAckTimeout, TimeUnit.MILLISECONDS);
127         }
128     }
129 
130     /**
131      * Releases the references held by this DGCAckHandler.
132      **/
release()133     synchronized void release() {
134         if (task != null) {
135             task.cancel(false);
136             task = null;
137         }
138         objList = null;
139     }
140 
141     /**
142      * Causes the DGCAckHandler associated with the specified UID to
143      * release its references.
144      **/
received(UID id)145     public static void received(UID id) {
146         DGCAckHandler h = idTable.remove(id);
147         if (h != null) {
148             h.release();
149         }
150     }
151 }
152