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