AbstractClientConnAdapter.java revision 417f3b92ba4549b2f22340e3107d869d2b9c5bb8
1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/AbstractClientConnAdapter.java $
3 * $Revision: 672969 $
4 * $Date: 2008-06-30 18:09:50 -0700 (Mon, 30 Jun 2008) $
5 *
6 * ====================================================================
7 *
8 *  Licensed to the Apache Software Foundation (ASF) under one or more
9 *  contributor license agreements.  See the NOTICE file distributed with
10 *  this work for additional information regarding copyright ownership.
11 *  The ASF licenses this file to You under the Apache License, Version 2.0
12 *  (the "License"); you may not use this file except in compliance with
13 *  the License.  You may obtain a copy of the License at
14 *
15 *      http://www.apache.org/licenses/LICENSE-2.0
16 *
17 *  Unless required by applicable law or agreed to in writing, software
18 *  distributed under the License is distributed on an "AS IS" BASIS,
19 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 *  See the License for the specific language governing permissions and
21 *  limitations under the License.
22 * ====================================================================
23 *
24 * This software consists of voluntary contributions made by many
25 * individuals on behalf of the Apache Software Foundation.  For more
26 * information on the Apache Software Foundation, please see
27 * <http://www.apache.org/>.
28 *
29 */
30
31package org.apache.http.impl.conn;
32
33
34import java.io.IOException;
35import java.io.InterruptedIOException;
36import java.net.InetAddress;
37import java.net.Socket;
38import java.util.concurrent.TimeUnit;
39
40import javax.net.ssl.SSLSocket;
41import javax.net.ssl.SSLSession;
42
43import org.apache.http.HttpException;
44import org.apache.http.HttpRequest;
45import org.apache.http.HttpEntityEnclosingRequest;
46import org.apache.http.HttpResponse;
47import org.apache.http.HttpConnectionMetrics;
48import org.apache.http.conn.OperatedClientConnection;
49import org.apache.http.conn.ManagedClientConnection;
50import org.apache.http.conn.ClientConnectionManager;
51
52
53/**
54 * Abstract adapter from {@link OperatedClientConnection operated} to
55 * {@link ManagedClientConnection managed} client connections.
56 * Read and write methods are delegated to the wrapped connection.
57 * Operations affecting the connection state have to be implemented
58 * by derived classes. Operations for querying the connection state
59 * are delegated to the wrapped connection if there is one, or
60 * return a default value if there is none.
61 * <br/>
62 * This adapter tracks the checkpoints for reusable communication states,
63 * as indicated by {@link #markReusable markReusable} and queried by
64 * {@link #isMarkedReusable isMarkedReusable}.
65 * All send and receive operations will automatically clear the mark.
66 * <br/>
67 * Connection release calls are delegated to the connection manager,
68 * if there is one. {@link #abortConnection abortConnection} will
69 * clear the reusability mark first. The connection manager is
70 * expected to tolerate multiple calls to the release method.
71 *
72 * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
73 *
74 *
75 * <!-- empty lines to avoid svn diff problems -->
76 * @version   $Revision: 672969 $ $Date: 2008-06-30 18:09:50 -0700 (Mon, 30 Jun 2008) $
77 *
78 * @since 4.0
79 */
80public abstract class AbstractClientConnAdapter
81    implements ManagedClientConnection {
82
83    /** Thread that requested this connection. */
84    private final Thread executionThread;
85
86    /**
87     * The connection manager, if any.
88     * This attribute MUST NOT be final, so the adapter can be detached
89     * from the connection manager without keeping a hard reference there.
90     */
91    private volatile ClientConnectionManager connManager;
92
93    /** The wrapped connection. */
94    private volatile OperatedClientConnection wrappedConnection;
95
96    /** The reusability marker. */
97    private volatile boolean markedReusable;
98
99    /** True if the connection has been aborted. */
100    private volatile boolean aborted;
101
102    /** The duration this is valid for while idle (in ms). */
103    private volatile long duration;
104
105    /**
106     * Creates a new connection adapter.
107     * The adapter is initially <i>not</i>
108     * {@link #isMarkedReusable marked} as reusable.
109     *
110     * @param mgr       the connection manager, or <code>null</code>
111     * @param conn      the connection to wrap, or <code>null</code>
112     */
113    protected AbstractClientConnAdapter(ClientConnectionManager mgr,
114                                        OperatedClientConnection conn) {
115        super();
116        executionThread = Thread.currentThread();
117        connManager = mgr;
118        wrappedConnection = conn;
119        markedReusable = false;
120        aborted = false;
121        duration = Long.MAX_VALUE;
122    } // <constructor>
123
124
125    /**
126     * Detaches this adapter from the wrapped connection.
127     * This adapter becomes useless.
128     */
129    protected void detach() {
130        wrappedConnection = null;
131        connManager = null; // base class attribute
132        duration = Long.MAX_VALUE;
133    }
134
135    protected OperatedClientConnection getWrappedConnection() {
136        return wrappedConnection;
137    }
138
139    protected ClientConnectionManager getManager() {
140        return connManager;
141    }
142
143    /**
144     * Asserts that the connection has not been aborted.
145     *
146     * @throws InterruptedIOException   if the connection has been aborted
147     */
148    protected final void assertNotAborted() throws InterruptedIOException {
149        if (aborted) {
150            throw new InterruptedIOException("Connection has been shut down.");
151        }
152    }
153
154    /**
155     * Asserts that there is a wrapped connection to delegate to.
156     *
157     * @throws IllegalStateException    if there is no wrapped connection
158     *                                  or connection has been aborted
159     */
160    protected final void assertValid(
161            final OperatedClientConnection wrappedConn) {
162        if (wrappedConn == null) {
163            throw new IllegalStateException("No wrapped connection.");
164        }
165    }
166
167    // non-javadoc, see interface HttpConnection
168    public boolean isOpen() {
169        OperatedClientConnection conn = getWrappedConnection();
170        if (conn == null)
171            return false;
172
173        return conn.isOpen();
174    }
175
176
177    // non-javadoc, see interface HttpConnection
178    public boolean isStale() {
179        if (aborted)
180            return true;
181        OperatedClientConnection conn = getWrappedConnection();
182        if (conn == null)
183            return true;
184
185        return conn.isStale();
186    }
187
188
189    // non-javadoc, see interface HttpConnection
190    public void setSocketTimeout(int timeout) {
191        OperatedClientConnection conn = getWrappedConnection();
192        assertValid(conn);
193        conn.setSocketTimeout(timeout);
194    }
195
196
197    // non-javadoc, see interface HttpConnection
198    public int getSocketTimeout() {
199        OperatedClientConnection conn = getWrappedConnection();
200        assertValid(conn);
201        return conn.getSocketTimeout();
202    }
203
204
205    // non-javadoc, see interface HttpConnection
206    public HttpConnectionMetrics getMetrics() {
207        OperatedClientConnection conn = getWrappedConnection();
208        assertValid(conn);
209        return conn.getMetrics();
210    }
211
212
213    // non-javadoc, see interface HttpClientConnection
214    public void flush()
215        throws IOException {
216
217        assertNotAborted();
218        OperatedClientConnection conn = getWrappedConnection();
219        assertValid(conn);
220
221        conn.flush();
222    }
223
224
225    // non-javadoc, see interface HttpClientConnection
226    public boolean isResponseAvailable(int timeout)
227        throws IOException {
228
229        assertNotAborted();
230        OperatedClientConnection conn = getWrappedConnection();
231        assertValid(conn);
232
233        return conn.isResponseAvailable(timeout);
234    }
235
236
237    // non-javadoc, see interface HttpClientConnection
238    public void receiveResponseEntity(HttpResponse response)
239        throws HttpException, IOException {
240
241        assertNotAborted();
242        OperatedClientConnection conn = getWrappedConnection();
243        assertValid(conn);
244
245        unmarkReusable();
246        conn.receiveResponseEntity(response);
247    }
248
249
250    // non-javadoc, see interface HttpClientConnection
251    public HttpResponse receiveResponseHeader()
252        throws HttpException, IOException {
253
254        assertNotAborted();
255        OperatedClientConnection conn = getWrappedConnection();
256        assertValid(conn);
257
258        unmarkReusable();
259        return conn.receiveResponseHeader();
260    }
261
262
263    // non-javadoc, see interface HttpClientConnection
264    public void sendRequestEntity(HttpEntityEnclosingRequest request)
265        throws HttpException, IOException {
266
267        assertNotAborted();
268        OperatedClientConnection conn = getWrappedConnection();
269        assertValid(conn);
270
271        unmarkReusable();
272        conn.sendRequestEntity(request);
273    }
274
275
276    // non-javadoc, see interface HttpClientConnection
277    public void sendRequestHeader(HttpRequest request)
278        throws HttpException, IOException {
279
280        assertNotAborted();
281        OperatedClientConnection conn = getWrappedConnection();
282        assertValid(conn);
283
284        unmarkReusable();
285        conn.sendRequestHeader(request);
286    }
287
288
289    // non-javadoc, see interface HttpInetConnection
290    public InetAddress getLocalAddress() {
291        OperatedClientConnection conn = getWrappedConnection();
292        assertValid(conn);
293        return conn.getLocalAddress();
294    }
295
296    // non-javadoc, see interface HttpInetConnection
297    public int getLocalPort() {
298        OperatedClientConnection conn = getWrappedConnection();
299        assertValid(conn);
300        return conn.getLocalPort();
301    }
302
303
304    // non-javadoc, see interface HttpInetConnection
305    public InetAddress getRemoteAddress() {
306        OperatedClientConnection conn = getWrappedConnection();
307        assertValid(conn);
308        return conn.getRemoteAddress();
309    }
310
311    // non-javadoc, see interface HttpInetConnection
312    public int getRemotePort() {
313        OperatedClientConnection conn = getWrappedConnection();
314        assertValid(conn);
315        return conn.getRemotePort();
316    }
317
318    // non-javadoc, see interface ManagedClientConnection
319    public boolean isSecure() {
320        OperatedClientConnection conn = getWrappedConnection();
321        assertValid(conn);
322        return conn.isSecure();
323    }
324
325    // non-javadoc, see interface ManagedClientConnection
326    public SSLSession getSSLSession() {
327        OperatedClientConnection conn = getWrappedConnection();
328        assertValid(conn);
329        if (!isOpen())
330            return null;
331
332        SSLSession result = null;
333        Socket    sock    = conn.getSocket();
334        if (sock instanceof SSLSocket) {
335            result = ((SSLSocket)sock).getSession();
336        }
337        return result;
338    }
339
340    // non-javadoc, see interface ManagedClientConnection
341    public void markReusable() {
342        markedReusable = true;
343    }
344
345    // non-javadoc, see interface ManagedClientConnection
346    public void unmarkReusable() {
347        markedReusable = false;
348    }
349
350    // non-javadoc, see interface ManagedClientConnection
351    public boolean isMarkedReusable() {
352        return markedReusable;
353    }
354
355    public void setIdleDuration(long duration, TimeUnit unit) {
356        if(duration > 0) {
357            this.duration = unit.toMillis(duration);
358        } else {
359            this.duration = -1;
360        }
361    }
362
363    // non-javadoc, see interface ConnectionReleaseTrigger
364    public void releaseConnection() {
365        if (connManager != null) {
366            connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
367        }
368    }
369
370    // non-javadoc, see interface ConnectionReleaseTrigger
371    public void abortConnection() {
372        if (aborted) {
373            return;
374        }
375        aborted = true;
376        unmarkReusable();
377        try {
378            shutdown();
379        } catch (IOException ignore) {
380        }
381        // Usually #abortConnection() is expected to be called from
382        // a helper thread in order to unblock the main execution thread
383        // blocked in an I/O operation. It may be unsafe to call
384        // #releaseConnection() from the helper thread, so we have to rely
385        // on an IOException thrown by the closed socket on the main thread
386        // to trigger the release of the connection back to the
387        // connection manager.
388        //
389        // However, if this method is called from the main execution thread
390        // it should be safe to release the connection immediately. Besides,
391        // this also helps ensure the connection gets released back to the
392        // manager if #abortConnection() is called from the main execution
393        // thread while there is no blocking I/O operation.
394        if (executionThread.equals(Thread.currentThread())) {
395            releaseConnection();
396        }
397    }
398
399} // class AbstractClientConnAdapter
400