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 */ 25package sun.rmi.transport; 26 27import java.io.*; 28import java.util.*; 29import java.rmi.RemoteException; 30import java.rmi.server.UID; 31import sun.rmi.server.MarshalInputStream; 32import sun.rmi.runtime.Log; 33 34/** 35 * Special stream to keep track of refs being unmarshaled so that 36 * refs can be ref-counted locally. 37 * 38 * @author Ann Wollrath 39 */ 40class ConnectionInputStream extends MarshalInputStream { 41 42 /** indicates whether ack is required for DGC */ 43 private boolean dgcAckNeeded = false; 44 45 /** Hashtable mapping Endpoints to lists of LiveRefs to register */ 46 private Map<Endpoint, List<LiveRef>> incomingRefTable = new HashMap<>(5); 47 48 /** identifier for gc ack*/ 49 private UID ackID; 50 51 /** 52 * Constructs a marshal input stream using the underlying 53 * stream "in". 54 */ 55 ConnectionInputStream(InputStream in) throws IOException { 56 super(in); 57 } 58 59 void readID() throws IOException { 60 ackID = UID.read((DataInput) this); 61 } 62 63 /** 64 * Save reference in order to send "dirty" call after all args/returns 65 * have been unmarshaled. Save in hashtable incomingRefTable. This 66 * table is keyed on endpoints, and holds objects of type 67 * IncomingRefTableEntry. 68 */ 69 void saveRef(LiveRef ref) { 70 Endpoint ep = ref.getEndpoint(); 71 72 // check whether endpoint is already in the hashtable 73 List<LiveRef> refList = incomingRefTable.get(ep); 74 75 if (refList == null) { 76 refList = new ArrayList<LiveRef>(); 77 incomingRefTable.put(ep, refList); 78 } 79 80 // add ref to list of refs for endpoint ep 81 refList.add(ref); 82 } 83 84 /** 85 * Discard the saved incoming refs so there is nothing to register 86 * when {@code registerRefs} is called. 87 */ 88 void discardRefs() { 89 incomingRefTable.clear(); 90 } 91 92 /** 93 * Add references to DGC table (and possibly send dirty call). 94 * RegisterRefs now calls DGCClient.referenced on all 95 * refs with the same endpoint at once to achieve batching of 96 * calls to the DGC 97 */ 98 void registerRefs() throws IOException { 99 if (!incomingRefTable.isEmpty()) { 100 for (Map.Entry<Endpoint, List<LiveRef>> entry : 101 incomingRefTable.entrySet()) { 102 DGCClient.registerRefs(entry.getKey(), entry.getValue()); 103 } 104 } 105 } 106 107 /** 108 * Indicate that an ack is required to the distributed 109 * collector. 110 */ 111 void setAckNeeded() { 112 dgcAckNeeded = true; 113 } 114 115 /** 116 * Done with input stream for remote call. Send DGC ack if necessary. 117 * Allow sending of ack to fail without flagging an error. 118 */ 119 void done(Connection c) { 120 /* 121 * WARNING: The connection c may have already been freed. It 122 * is only be safe to use c to obtain c's channel. 123 */ 124 125 if (dgcAckNeeded) { 126 Connection conn = null; 127 Channel ch = null; 128 boolean reuse = true; 129 130 DGCImpl.dgcLog.log(Log.VERBOSE, "send ack"); 131 132 try { 133 ch = c.getChannel(); 134 conn = ch.newConnection(); 135 DataOutputStream out = 136 new DataOutputStream(conn.getOutputStream()); 137 out.writeByte(TransportConstants.DGCAck); 138 if (ackID == null) { 139 ackID = new UID(); 140 } 141 ackID.write((DataOutput) out); 142 conn.releaseOutputStream(); 143 144 /* 145 * Fix for 4221173: if this connection is on top of an 146 * HttpSendSocket, the DGCAck won't actually get sent until a 147 * read operation is attempted on the socket. Calling 148 * available() is the most innocuous way of triggering the 149 * write. 150 */ 151 conn.getInputStream().available(); 152 conn.releaseInputStream(); 153 } catch (RemoteException e) { 154 reuse = false; 155 } catch (IOException e) { 156 reuse = false; 157 } 158 try { 159 if (conn != null) 160 ch.free(conn, reuse); 161 } catch (RemoteException e){ 162 // eat exception 163 } 164 } 165 } 166} 167