1 /* 2 * Copyright (c) 1996, 2001, 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.io.*; 28 import java.util.*; 29 import java.rmi.RemoteException; 30 import java.rmi.server.UID; 31 import sun.rmi.server.MarshalInputStream; 32 import 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 */ 40 class 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 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 refList = (List) incomingRefTable.get(ep); 74 75 if (refList == null) { 76 refList = new ArrayList(); 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 * Add references to DGC table (and possibly send dirty call). 86 * RegisterRefs now calls DGCClient.referenced on all 87 * refs with the same endpoint at once to achieve batching of 88 * calls to the DGC 89 */ 90 void registerRefs() throws IOException { 91 if (!incomingRefTable.isEmpty()) { 92 Set entrySet = incomingRefTable.entrySet(); 93 Iterator iter = entrySet.iterator(); 94 while (iter.hasNext()) { 95 Map.Entry entry = (Map.Entry) iter.next(); 96 Endpoint ep = (Endpoint) entry.getKey(); 97 List refList = (List) entry.getValue(); 98 DGCClient.registerRefs(ep, refList); 99 } 100 } 101 } 102 103 /** 104 * Indicate that an ack is required to the distributed 105 * collector. 106 */ 107 void setAckNeeded() { 108 dgcAckNeeded = true; 109 } 110 111 /** 112 * Done with input stream for remote call. Send DGC ack if necessary. 113 * Allow sending of ack to fail without flagging an error. 114 */ 115 void done(Connection c) { 116 /* 117 * WARNING: The connection c may have already been freed. It 118 * is only be safe to use c to obtain c's channel. 119 */ 120 121 if (dgcAckNeeded) { 122 Connection conn = null; 123 Channel ch = null; 124 boolean reuse = true; 125 126 DGCImpl.dgcLog.log(Log.VERBOSE, "send ack"); 127 128 try { 129 ch = c.getChannel(); 130 conn = ch.newConnection(); 131 DataOutputStream out = 132 new DataOutputStream(conn.getOutputStream()); 133 out.writeByte(TransportConstants.DGCAck); 134 if (ackID == null) { 135 ackID = new UID(); 136 } 137 ackID.write((DataOutput) out); 138 conn.releaseOutputStream(); 139 140 /* 141 * Fix for 4221173: if this connection is on top of an 142 * HttpSendSocket, the DGCAck won't actually get sent until a 143 * read operation is attempted on the socket. Calling 144 * available() is the most innocuous way of triggering the 145 * write. 146 */ 147 conn.getInputStream().available(); 148 conn.releaseInputStream(); 149 } catch (RemoteException e) { 150 reuse = false; 151 } catch (IOException e) { 152 reuse = false; 153 } 154 try { 155 if (conn != null) 156 ch.free(conn, reuse); 157 } catch (RemoteException e){ 158 // eat exception 159 } 160 } 161 } 162 }