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