1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/AbstractPoolEntry.java $
3 * $Revision: 658775 $
4 * $Date: 2008-05-21 10:30:45 -0700 (Wed, 21 May 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;
35
36import org.apache.http.HttpHost;
37import org.apache.http.params.HttpParams;
38import org.apache.http.protocol.HttpContext;
39import org.apache.http.conn.routing.HttpRoute;
40import org.apache.http.conn.routing.RouteTracker;
41import org.apache.http.conn.ClientConnectionOperator;
42import org.apache.http.conn.OperatedClientConnection;
43
44
45
46/**
47 * A pool entry for use by connection manager implementations.
48 * Pool entries work in conjunction with an
49 * {@link AbstractClientConnAdapter adapter}.
50 * The adapter is handed out to applications that obtain a connection.
51 * The pool entry stores the underlying connection and tracks the
52 * {@link HttpRoute route} established.
53 * The adapter delegates methods for establishing the route to
54 * it's pool entry.
55 * <br/>
56 * If the managed connections is released or revoked, the adapter
57 * gets disconnected, but the pool entry still contains the
58 * underlying connection and the established route.
59 *
60 * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
61 * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
62 *
63 *
64 * <!-- empty lines to avoid svn diff problems -->
65 * @version   $Revision: 658775 $
66 *
67 * @since 4.0
68 */
69public abstract class AbstractPoolEntry {
70
71    /** The connection operator. */
72    protected final ClientConnectionOperator connOperator;
73
74    /** The underlying connection being pooled or used. */
75    protected final OperatedClientConnection connection;
76
77    /** The route for which this entry gets allocated. */
78    //@@@ currently accessed from connection manager(s) as attribute
79    //@@@ avoid that, derived classes should decide whether update is allowed
80    //@@@ SCCM: yes, TSCCM: no
81    protected volatile HttpRoute route;
82
83    /** Connection state object */
84    protected volatile Object state;
85
86    /** The tracked route, or <code>null</code> before tracking starts. */
87    protected volatile RouteTracker tracker;
88
89
90    /**
91     * Creates a new pool entry.
92     *
93     * @param connOperator     the Connection Operator for this entry
94     * @param route   the planned route for the connection,
95     *                or <code>null</code>
96     */
97    protected AbstractPoolEntry(ClientConnectionOperator connOperator,
98                                HttpRoute route) {
99        super();
100        if (connOperator == null) {
101            throw new IllegalArgumentException("Connection operator may not be null");
102        }
103        this.connOperator = connOperator;
104        this.connection = connOperator.createConnection();
105        this.route = route;
106        this.tracker = null;
107    }
108
109    /**
110     * Returns the state object associated with this pool entry.
111     *
112     * @return The state object
113     */
114    public Object getState() {
115        return state;
116    }
117
118    /**
119     * Assigns a state object to this pool entry.
120     *
121     * @param state The state object
122     */
123    public void setState(final Object state) {
124        this.state = state;
125    }
126
127    /**
128     * Opens the underlying connection.
129     *
130     * @param route         the route along which to open the connection
131     * @param context       the context for opening the connection
132     * @param params        the parameters for opening the connection
133     *
134     * @throws IOException  in case of a problem
135     */
136    public void open(HttpRoute route,
137                     HttpContext context, HttpParams params)
138        throws IOException {
139
140        if (route == null) {
141            throw new IllegalArgumentException
142                ("Route must not be null.");
143        }
144        //@@@ is context allowed to be null? depends on operator?
145        if (params == null) {
146            throw new IllegalArgumentException
147                ("Parameters must not be null.");
148        }
149        if ((this.tracker != null) && this.tracker.isConnected()) {
150            throw new IllegalStateException("Connection already open.");
151        }
152
153        // - collect the arguments
154        // - call the operator
155        // - update the tracking data
156        // In this order, we can be sure that only a successful
157        // opening of the connection will be tracked.
158
159        //@@@ verify route against planned route?
160
161        this.tracker = new RouteTracker(route);
162        final HttpHost proxy  = route.getProxyHost();
163
164        connOperator.openConnection
165            (this.connection,
166             (proxy != null) ? proxy : route.getTargetHost(),
167             route.getLocalAddress(),
168             context, params);
169
170        RouteTracker localTracker = tracker; // capture volatile
171
172        // If this tracker was reset while connecting,
173        // fail early.
174        if (localTracker == null) {
175            throw new IOException("Request aborted");
176        }
177
178        if (proxy == null) {
179            localTracker.connectTarget(this.connection.isSecure());
180        } else {
181            localTracker.connectProxy(proxy, this.connection.isSecure());
182        }
183
184    } // open
185
186
187    /**
188     * Tracks tunnelling of the connection to the target.
189     * The tunnel has to be established outside by sending a CONNECT
190     * request to the (last) proxy.
191     *
192     * @param secure    <code>true</code> if the tunnel should be
193     *                  considered secure, <code>false</code> otherwise
194     * @param params    the parameters for tunnelling the connection
195     *
196     * @throws IOException  in case of a problem
197     */
198    public void tunnelTarget(boolean secure, HttpParams params)
199        throws IOException {
200
201        if (params == null) {
202            throw new IllegalArgumentException
203                ("Parameters must not be null.");
204        }
205
206        //@@@ check for proxy in planned route?
207        if ((this.tracker == null) || !this.tracker.isConnected()) {
208            throw new IllegalStateException("Connection not open.");
209        }
210        if (this.tracker.isTunnelled()) {
211            throw new IllegalStateException
212                ("Connection is already tunnelled.");
213        }
214
215        // LOG.debug?
216
217        this.connection.update(null, tracker.getTargetHost(),
218                               secure, params);
219        this.tracker.tunnelTarget(secure);
220
221    } // tunnelTarget
222
223
224    /**
225     * Tracks tunnelling of the connection to a chained proxy.
226     * The tunnel has to be established outside by sending a CONNECT
227     * request to the previous proxy.
228     *
229     * @param next      the proxy to which the tunnel was established.
230     *  See {@link org.apache.http.conn.ManagedClientConnection#tunnelProxy
231     *                                  ManagedClientConnection.tunnelProxy}
232     *                  for details.
233     * @param secure    <code>true</code> if the tunnel should be
234     *                  considered secure, <code>false</code> otherwise
235     * @param params    the parameters for tunnelling the connection
236     *
237     * @throws IOException  in case of a problem
238     */
239    public void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
240        throws IOException {
241
242        if (next == null) {
243            throw new IllegalArgumentException
244                ("Next proxy must not be null.");
245        }
246        if (params == null) {
247            throw new IllegalArgumentException
248                ("Parameters must not be null.");
249        }
250
251        //@@@ check for proxy in planned route?
252        if ((this.tracker == null) || !this.tracker.isConnected()) {
253            throw new IllegalStateException("Connection not open.");
254        }
255
256        // LOG.debug?
257
258        this.connection.update(null, next, secure, params);
259        this.tracker.tunnelProxy(next, secure);
260
261    } // tunnelProxy
262
263
264    /**
265     * Layers a protocol on top of an established tunnel.
266     *
267     * @param context   the context for layering
268     * @param params    the parameters for layering
269     *
270     * @throws IOException  in case of a problem
271     */
272    public void layerProtocol(HttpContext context, HttpParams params)
273        throws IOException {
274
275        //@@@ is context allowed to be null? depends on operator?
276        if (params == null) {
277            throw new IllegalArgumentException
278                ("Parameters must not be null.");
279        }
280
281        if ((this.tracker == null) || !this.tracker.isConnected()) {
282            throw new IllegalStateException("Connection not open.");
283        }
284        if (!this.tracker.isTunnelled()) {
285            //@@@ allow this?
286            throw new IllegalStateException
287                ("Protocol layering without a tunnel not supported.");
288        }
289        if (this.tracker.isLayered()) {
290            throw new IllegalStateException
291                ("Multiple protocol layering not supported.");
292        }
293
294        // - collect the arguments
295        // - call the operator
296        // - update the tracking data
297        // In this order, we can be sure that only a successful
298        // layering on top of the connection will be tracked.
299
300        final HttpHost target = tracker.getTargetHost();
301
302        connOperator.updateSecureConnection(this.connection, target,
303                                             context, params);
304
305        this.tracker.layerProtocol(this.connection.isSecure());
306
307    } // layerProtocol
308
309
310    /**
311     * Shuts down the entry.
312     *
313     * If {@link #open(HttpRoute, HttpContext, HttpParams)} is in progress,
314     * this will cause that open to possibly throw an {@link IOException}.
315     */
316    protected void shutdownEntry() {
317        tracker = null;
318    }
319
320
321} // class AbstractPoolEntry
322
323