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 26package sun.rmi.transport.tcp; 27 28import java.io.*; 29import java.net.Socket; 30import java.rmi.*; 31import sun.rmi.runtime.Log; 32import sun.rmi.transport.*; 33 34public class TCPConnection implements Connection { 35 36 private Socket socket; 37 private Channel channel; 38 private InputStream in = null; 39 private OutputStream out = null; 40 private long expiration = Long.MAX_VALUE; 41 private long lastuse = Long.MIN_VALUE; 42 private long roundtrip = 5; // round-trip time for ping 43 44 /** 45 * Constructor used for creating a connection to accept call 46 * (an input connection) 47 */ 48 TCPConnection(TCPChannel ch, Socket s, InputStream in, OutputStream out) 49 { 50 socket = s; 51 channel = ch; 52 this.in = in; 53 this.out = out; 54 } 55 56 /** 57 * Constructor used by subclass when underlying input and output streams 58 * are already available. 59 */ 60 TCPConnection(TCPChannel ch, InputStream in, OutputStream out) 61 { 62 this(ch, null, in, out); 63 } 64 65 /** 66 * Constructor used when socket is available, but not underlying 67 * streams. 68 */ 69 TCPConnection(TCPChannel ch, Socket s) 70 { 71 this(ch, s, null, null); 72 } 73 74 /** 75 * Gets the output stream for this connection 76 */ 77 public OutputStream getOutputStream() throws IOException 78 { 79 if (out == null) 80 out = new BufferedOutputStream(socket.getOutputStream()); 81 return out; 82 } 83 84 /** 85 * Release the output stream for this connection. 86 */ 87 public void releaseOutputStream() throws IOException 88 { 89 if (out != null) 90 out.flush(); 91 } 92 93 /** 94 * Gets the input stream for this connection. 95 */ 96 public InputStream getInputStream() throws IOException 97 { 98 if (in == null) 99 in = new BufferedInputStream(socket.getInputStream()); 100 return in; 101 } 102 103 104 /** 105 * Release the input stream for this connection. 106 */ 107 public void releaseInputStream() 108 { 109 } 110 111 /** 112 * Determine if this connection can be used for multiple operations. 113 * If the socket implements RMISocketInfo, then we can query it about 114 * this; otherwise, assume that it does provide a full-duplex 115 * persistent connection like java.net.Socket. 116 */ 117 public boolean isReusable() 118 { 119 return true; 120 } 121 122 /** 123 * Set the expiration time of this connection. 124 * @param time The time at which the time out expires. 125 */ 126 void setExpiration(long time) 127 { 128 expiration = time; 129 } 130 131 /** 132 * Set the timestamp at which this connection was last used successfully. 133 * The connection will be pinged for liveness if reused long after 134 * this time. 135 * @param time The time at which the connection was last active. 136 */ 137 void setLastUseTime(long time) 138 { 139 lastuse = time; 140 } 141 142 /** 143 * Returns true if the timeout has expired on this connection; 144 * otherwise returns false. 145 * @param time The current time. 146 */ 147 boolean expired(long time) 148 { 149 return expiration <= time; 150 } 151 152 /** 153 * Probes the connection to see if it still alive and connected to 154 * a responsive server. If the connection has been idle for too 155 * long, the server is pinged. ``Too long'' means ``longer than the 156 * last ping round-trip time''. 157 * <P> 158 * This method may misdiagnose a dead connection as live, but it 159 * will never misdiagnose a live connection as dead. 160 * @return true if the connection and server are recently alive 161 */ 162 public boolean isDead() 163 { 164 InputStream i; 165 OutputStream o; 166 167 // skip ping if recently used within 1 RTT 168 long start = System.currentTimeMillis(); 169 if ((roundtrip > 0) && (start < lastuse + roundtrip)) 170 return (false); // still alive and warm 171 172 // Get the streams 173 try { 174 i = getInputStream(); 175 o = getOutputStream(); 176 } catch (IOException e) { 177 return (true); // can't even get a stream, must be very dead 178 } 179 180 // Write the ping byte and read the reply byte 181 int response = 0; 182 try { 183 o.write(TransportConstants.Ping); 184 o.flush(); 185 response = i.read(); 186 } catch (IOException ex) { 187 TCPTransport.tcpLog.log(Log.VERBOSE, "exception: ", ex); 188 TCPTransport.tcpLog.log(Log.BRIEF, "server ping failed"); 189 190 return (true); // server failed the ping test 191 } 192 193 if (response == TransportConstants.PingAck) { 194 // save most recent RTT for future use 195 roundtrip = (System.currentTimeMillis() - start) * 2; 196 // clock-correction may make roundtrip < 0; doesn't matter 197 return (false); // it's alive and 5-by-5 198 } 199 200 if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) { 201 TCPTransport.tcpLog.log(Log.BRIEF, 202 (response == -1 ? "server has been deactivated" : 203 "server protocol error: ping response = " + response)); 204 } 205 return (true); 206 } 207 208 /** 209 * Close the connection. */ 210 public void close() throws IOException 211 { 212 TCPTransport.tcpLog.log(Log.BRIEF, "close connection"); 213 214 if (socket != null) 215 socket.close(); 216 else { 217 in.close(); 218 out.close(); 219 } 220 } 221 222 /** 223 * Returns the channel for this connection. 224 */ 225 public Channel getChannel() 226 { 227 return channel; 228 } 229} 230