1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java $
3 * $Revision: 677250 $
4 * $Date: 2008-07-16 04:45:47 -0700 (Wed, 16 Jul 2008) $
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements.  See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership.  The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with 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,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied.  See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 *
25 * This software consists of voluntary contributions made by many
26 * individuals on behalf of the Apache Software Foundation.  For more
27 * information on the Apache Software Foundation, please see
28 * <http://www.apache.org/>.
29 *
30 */
31
32package org.apache.http.impl.client;
33
34import java.io.IOException;
35import java.net.URI;
36import java.lang.reflect.UndeclaredThrowableException;
37
38import org.apache.commons.logging.Log;
39import org.apache.commons.logging.LogFactory;
40import org.apache.http.ConnectionReuseStrategy;
41import org.apache.http.HttpException;
42import org.apache.http.HttpHost;
43import org.apache.http.HttpRequest;
44import org.apache.http.HttpRequestInterceptor;
45import org.apache.http.HttpResponse;
46import org.apache.http.HttpResponseInterceptor;
47import org.apache.http.HttpEntity;
48import org.apache.http.auth.AuthSchemeRegistry;
49import org.apache.http.client.AuthenticationHandler;
50import org.apache.http.client.ClientProtocolException;
51import org.apache.http.client.RequestDirector;
52import org.apache.http.client.ResponseHandler;
53import org.apache.http.client.CookieStore;
54import org.apache.http.client.CredentialsProvider;
55import org.apache.http.client.HttpClient;
56import org.apache.http.client.HttpRequestRetryHandler;
57import org.apache.http.client.RedirectHandler;
58import org.apache.http.client.UserTokenHandler;
59import org.apache.http.client.methods.HttpUriRequest;
60import org.apache.http.conn.ClientConnectionManager;
61import org.apache.http.conn.ConnectionKeepAliveStrategy;
62import org.apache.http.conn.routing.HttpRoutePlanner;
63import org.apache.http.cookie.CookieSpecRegistry;
64import org.apache.http.params.HttpParams;
65import org.apache.http.protocol.BasicHttpProcessor;
66import org.apache.http.protocol.DefaultedHttpContext;
67import org.apache.http.protocol.HttpContext;
68import org.apache.http.protocol.HttpProcessor;
69import org.apache.http.protocol.HttpRequestExecutor;
70
71/**
72 * Convenience base class for HTTP client implementations.
73 *
74 * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
75 * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
76 *
77 * <!-- empty lines to avoid svn diff problems -->
78 * @version   $Revision: 677250 $
79 *
80 * @since 4.0
81 */
82public abstract class AbstractHttpClient implements HttpClient {
83
84    private final Log log = LogFactory.getLog(getClass());
85
86    /** The parameters. */
87    private HttpParams defaultParams;
88
89    /** The request executor. */
90    private HttpRequestExecutor requestExec;
91
92    /** The connection manager. */
93    private ClientConnectionManager connManager;
94
95    /** The connection re-use strategy. */
96    private ConnectionReuseStrategy reuseStrategy;
97
98    /** The connection keep-alive strategy. */
99    private ConnectionKeepAliveStrategy keepAliveStrategy;
100
101    /** The cookie spec registry. */
102    private CookieSpecRegistry supportedCookieSpecs;
103
104    /** The authentication scheme registry. */
105    private AuthSchemeRegistry supportedAuthSchemes;
106
107    /** The HTTP processor. */
108    private BasicHttpProcessor httpProcessor;
109
110    /** The request retry handler. */
111    private HttpRequestRetryHandler retryHandler;
112
113    /** The redirect handler. */
114    private RedirectHandler redirectHandler;
115
116    /** The target authentication handler. */
117    private AuthenticationHandler targetAuthHandler;
118
119    /** The proxy authentication handler. */
120    private AuthenticationHandler proxyAuthHandler;
121
122    /** The cookie store. */
123    private CookieStore cookieStore;
124
125    /** The credentials provider. */
126    private CredentialsProvider credsProvider;
127
128    /** The route planner. */
129    private HttpRoutePlanner routePlanner;
130
131    /** The user token handler. */
132    private UserTokenHandler userTokenHandler;
133
134
135    /**
136     * Creates a new HTTP client.
137     *
138     * @param conman    the connection manager
139     * @param params    the parameters
140     */
141    protected AbstractHttpClient(
142            final ClientConnectionManager conman,
143            final HttpParams params) {
144        defaultParams        = params;
145        connManager          = conman;
146    } // constructor
147
148    protected abstract HttpParams createHttpParams();
149
150
151    protected abstract HttpContext createHttpContext();
152
153
154    protected abstract HttpRequestExecutor createRequestExecutor();
155
156
157    protected abstract ClientConnectionManager createClientConnectionManager();
158
159
160    protected abstract AuthSchemeRegistry createAuthSchemeRegistry();
161
162
163    protected abstract CookieSpecRegistry createCookieSpecRegistry();
164
165
166    protected abstract ConnectionReuseStrategy createConnectionReuseStrategy();
167
168
169    protected abstract ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy();
170
171
172    protected abstract BasicHttpProcessor createHttpProcessor();
173
174
175    protected abstract HttpRequestRetryHandler createHttpRequestRetryHandler();
176
177
178    protected abstract RedirectHandler createRedirectHandler();
179
180
181    protected abstract AuthenticationHandler createTargetAuthenticationHandler();
182
183
184    protected abstract AuthenticationHandler createProxyAuthenticationHandler();
185
186
187    protected abstract CookieStore createCookieStore();
188
189
190    protected abstract CredentialsProvider createCredentialsProvider();
191
192
193    protected abstract HttpRoutePlanner createHttpRoutePlanner();
194
195
196    protected abstract UserTokenHandler createUserTokenHandler();
197
198
199    // non-javadoc, see interface HttpClient
200    public synchronized final HttpParams getParams() {
201        if (defaultParams == null) {
202            defaultParams = createHttpParams();
203        }
204        return defaultParams;
205    }
206
207
208    /**
209     * Replaces the parameters.
210     * The implementation here does not update parameters of dependent objects.
211     *
212     * @param params    the new default parameters
213     */
214    public synchronized void setParams(HttpParams params) {
215        defaultParams = params;
216    }
217
218
219    public synchronized final ClientConnectionManager getConnectionManager() {
220        if (connManager == null) {
221            connManager = createClientConnectionManager();
222        }
223        return connManager;
224    }
225
226
227    public synchronized final HttpRequestExecutor getRequestExecutor() {
228        if (requestExec == null) {
229            requestExec = createRequestExecutor();
230        }
231        return requestExec;
232    }
233
234
235    public synchronized final AuthSchemeRegistry getAuthSchemes() {
236        if (supportedAuthSchemes == null) {
237            supportedAuthSchemes = createAuthSchemeRegistry();
238        }
239        return supportedAuthSchemes;
240    }
241
242
243    public synchronized void setAuthSchemes(final AuthSchemeRegistry authSchemeRegistry) {
244        supportedAuthSchemes = authSchemeRegistry;
245    }
246
247
248    public synchronized final CookieSpecRegistry getCookieSpecs() {
249        if (supportedCookieSpecs == null) {
250            supportedCookieSpecs = createCookieSpecRegistry();
251        }
252        return supportedCookieSpecs;
253    }
254
255
256    public synchronized void setCookieSpecs(final CookieSpecRegistry cookieSpecRegistry) {
257        supportedCookieSpecs = cookieSpecRegistry;
258    }
259
260
261    public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
262        if (reuseStrategy == null) {
263            reuseStrategy = createConnectionReuseStrategy();
264        }
265        return reuseStrategy;
266    }
267
268
269    public synchronized void setReuseStrategy(final ConnectionReuseStrategy reuseStrategy) {
270        this.reuseStrategy = reuseStrategy;
271    }
272
273
274    public synchronized final ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
275        if (keepAliveStrategy == null) {
276            keepAliveStrategy = createConnectionKeepAliveStrategy();
277        }
278        return keepAliveStrategy;
279    }
280
281
282    public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy keepAliveStrategy) {
283        this.keepAliveStrategy = keepAliveStrategy;
284    }
285
286
287    public synchronized final HttpRequestRetryHandler getHttpRequestRetryHandler() {
288        if (retryHandler == null) {
289            retryHandler = createHttpRequestRetryHandler();
290        }
291        return retryHandler;
292    }
293
294
295    public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler retryHandler) {
296        this.retryHandler = retryHandler;
297    }
298
299
300    public synchronized final RedirectHandler getRedirectHandler() {
301        if (redirectHandler == null) {
302            redirectHandler = createRedirectHandler();
303        }
304        return redirectHandler;
305    }
306
307
308    public synchronized void setRedirectHandler(final RedirectHandler redirectHandler) {
309        this.redirectHandler = redirectHandler;
310    }
311
312
313    public synchronized final AuthenticationHandler getTargetAuthenticationHandler() {
314        if (targetAuthHandler == null) {
315            targetAuthHandler = createTargetAuthenticationHandler();
316        }
317        return targetAuthHandler;
318    }
319
320
321    public synchronized void setTargetAuthenticationHandler(
322            final AuthenticationHandler targetAuthHandler) {
323        this.targetAuthHandler = targetAuthHandler;
324    }
325
326
327    public synchronized final AuthenticationHandler getProxyAuthenticationHandler() {
328        if (proxyAuthHandler == null) {
329            proxyAuthHandler = createProxyAuthenticationHandler();
330        }
331        return proxyAuthHandler;
332    }
333
334
335    public synchronized void setProxyAuthenticationHandler(
336            final AuthenticationHandler proxyAuthHandler) {
337        this.proxyAuthHandler = proxyAuthHandler;
338    }
339
340
341    public synchronized final CookieStore getCookieStore() {
342        if (cookieStore == null) {
343            cookieStore = createCookieStore();
344        }
345        return cookieStore;
346    }
347
348
349    public synchronized void setCookieStore(final CookieStore cookieStore) {
350        this.cookieStore = cookieStore;
351    }
352
353
354    public synchronized final CredentialsProvider getCredentialsProvider() {
355        if (credsProvider == null) {
356            credsProvider = createCredentialsProvider();
357        }
358        return credsProvider;
359    }
360
361
362    public synchronized void setCredentialsProvider(final CredentialsProvider credsProvider) {
363        this.credsProvider = credsProvider;
364    }
365
366
367    public synchronized final HttpRoutePlanner getRoutePlanner() {
368        if (this.routePlanner == null) {
369            this.routePlanner = createHttpRoutePlanner();
370        }
371        return this.routePlanner;
372    }
373
374
375    public synchronized void setRoutePlanner(final HttpRoutePlanner routePlanner) {
376        this.routePlanner = routePlanner;
377    }
378
379
380    public synchronized final UserTokenHandler getUserTokenHandler() {
381        if (this.userTokenHandler == null) {
382            this.userTokenHandler = createUserTokenHandler();
383        }
384        return this.userTokenHandler;
385    }
386
387
388    public synchronized void setUserTokenHandler(final UserTokenHandler userTokenHandler) {
389        this.userTokenHandler = userTokenHandler;
390    }
391
392
393    protected synchronized final BasicHttpProcessor getHttpProcessor() {
394        if (httpProcessor == null) {
395            httpProcessor = createHttpProcessor();
396        }
397        return httpProcessor;
398    }
399
400
401    public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp) {
402        getHttpProcessor().addInterceptor(itcp);
403    }
404
405
406    public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp, int index) {
407        getHttpProcessor().addInterceptor(itcp, index);
408    }
409
410
411    public synchronized HttpResponseInterceptor getResponseInterceptor(int index) {
412        return getHttpProcessor().getResponseInterceptor(index);
413    }
414
415
416    public synchronized int getResponseInterceptorCount() {
417        return getHttpProcessor().getResponseInterceptorCount();
418    }
419
420
421    public synchronized void clearResponseInterceptors() {
422        getHttpProcessor().clearResponseInterceptors();
423    }
424
425
426    public void removeResponseInterceptorByClass(Class<? extends HttpResponseInterceptor> clazz) {
427        getHttpProcessor().removeResponseInterceptorByClass(clazz);
428    }
429
430
431    public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp) {
432        getHttpProcessor().addInterceptor(itcp);
433    }
434
435
436    public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp, int index) {
437        getHttpProcessor().addInterceptor(itcp, index);
438    }
439
440
441    public synchronized HttpRequestInterceptor getRequestInterceptor(int index) {
442        return getHttpProcessor().getRequestInterceptor(index);
443    }
444
445
446    public synchronized int getRequestInterceptorCount() {
447        return getHttpProcessor().getRequestInterceptorCount();
448    }
449
450
451    public synchronized void clearRequestInterceptors() {
452        getHttpProcessor().clearRequestInterceptors();
453    }
454
455
456    public void removeRequestInterceptorByClass(Class<? extends HttpRequestInterceptor> clazz) {
457        getHttpProcessor().removeRequestInterceptorByClass(clazz);
458    }
459
460
461    // non-javadoc, see interface HttpClient
462    public final HttpResponse execute(HttpUriRequest request)
463        throws IOException, ClientProtocolException {
464
465        return execute(request, (HttpContext) null);
466    }
467
468
469    /**
470     * Maps to {@link HttpClient#execute(HttpHost,HttpRequest,HttpContext)
471     *                           execute(target, request, context)}.
472     * The target is determined from the URI of the request.
473     *
474     * @param request   the request to execute
475     * @param context   the request-specific execution context,
476     *                  or <code>null</code> to use a default context
477     */
478    public final HttpResponse execute(HttpUriRequest request,
479                                      HttpContext context)
480        throws IOException, ClientProtocolException {
481
482        if (request == null) {
483            throw new IllegalArgumentException
484                ("Request must not be null.");
485        }
486
487        return execute(determineTarget(request), request, context);
488    }
489
490    private HttpHost determineTarget(HttpUriRequest request) {
491        // A null target may be acceptable if there is a default target.
492        // Otherwise, the null target is detected in the director.
493        HttpHost target = null;
494
495        URI requestURI = request.getURI();
496        if (requestURI.isAbsolute()) {
497            target = new HttpHost(
498                    requestURI.getHost(),
499                    requestURI.getPort(),
500                    requestURI.getScheme());
501        }
502        return target;
503    }
504
505    // non-javadoc, see interface HttpClient
506    public final HttpResponse execute(HttpHost target, HttpRequest request)
507        throws IOException, ClientProtocolException {
508
509        return execute(target, request, (HttpContext) null);
510    }
511
512
513    // non-javadoc, see interface HttpClient
514    public final HttpResponse execute(HttpHost target, HttpRequest request,
515                                      HttpContext context)
516        throws IOException, ClientProtocolException {
517
518        if (request == null) {
519            throw new IllegalArgumentException
520                ("Request must not be null.");
521        }
522        // a null target may be acceptable, this depends on the route planner
523        // a null context is acceptable, default context created below
524
525        HttpContext execContext = null;
526        RequestDirector director = null;
527
528        // Initialize the request execution context making copies of
529        // all shared objects that are potentially threading unsafe.
530        synchronized (this) {
531
532            HttpContext defaultContext = createHttpContext();
533            if (context == null) {
534                execContext = defaultContext;
535            } else {
536                execContext = new DefaultedHttpContext(context, defaultContext);
537            }
538            // Create a director for this request
539            director = createClientRequestDirector(
540                    getRequestExecutor(),
541                    getConnectionManager(),
542                    getConnectionReuseStrategy(),
543                    getConnectionKeepAliveStrategy(),
544                    getRoutePlanner(),
545                    getHttpProcessor().copy(),
546                    getHttpRequestRetryHandler(),
547                    getRedirectHandler(),
548                    getTargetAuthenticationHandler(),
549                    getProxyAuthenticationHandler(),
550                    getUserTokenHandler(),
551                    determineParams(request));
552        }
553
554        try {
555            return director.execute(target, request, execContext);
556        } catch(HttpException httpException) {
557            throw new ClientProtocolException(httpException);
558        }
559    } // execute
560
561
562    protected RequestDirector createClientRequestDirector(
563            final HttpRequestExecutor requestExec,
564            final ClientConnectionManager conman,
565            final ConnectionReuseStrategy reustrat,
566            final ConnectionKeepAliveStrategy kastrat,
567            final HttpRoutePlanner rouplan,
568            final HttpProcessor httpProcessor,
569            final HttpRequestRetryHandler retryHandler,
570            final RedirectHandler redirectHandler,
571            final AuthenticationHandler targetAuthHandler,
572            final AuthenticationHandler proxyAuthHandler,
573            final UserTokenHandler stateHandler,
574            final HttpParams params) {
575        return new DefaultRequestDirector(
576                requestExec,
577                conman,
578                reustrat,
579                kastrat,
580                rouplan,
581                httpProcessor,
582                retryHandler,
583                redirectHandler,
584                targetAuthHandler,
585                proxyAuthHandler,
586                stateHandler,
587                params);
588    }
589
590    /**
591     * Obtains parameters for executing a request.
592     * The default implementation in this class creates a new
593     * {@link ClientParamsStack} from the request parameters
594     * and the client parameters.
595     * <br/>
596     * This method is called by the default implementation of
597     * {@link #execute(HttpHost,HttpRequest,HttpContext)}
598     * to obtain the parameters for the
599     * {@link DefaultRequestDirector}.
600     *
601     * @param req    the request that will be executed
602     *
603     * @return  the parameters to use
604     */
605    protected HttpParams determineParams(HttpRequest req) {
606        return new ClientParamsStack
607            (null, getParams(), req.getParams(), null);
608    }
609
610
611    // non-javadoc, see interface HttpClient
612    public <T> T execute(
613            final HttpUriRequest request,
614            final ResponseHandler<? extends T> responseHandler)
615                throws IOException, ClientProtocolException {
616        return execute(request, responseHandler, null);
617    }
618
619
620    // non-javadoc, see interface HttpClient
621    public <T> T execute(
622            final HttpUriRequest request,
623            final ResponseHandler<? extends T> responseHandler,
624            final HttpContext context)
625                throws IOException, ClientProtocolException {
626        HttpHost target = determineTarget(request);
627        return execute(target, request, responseHandler, context);
628    }
629
630
631    // non-javadoc, see interface HttpClient
632    public <T> T execute(
633            final HttpHost target,
634            final HttpRequest request,
635            final ResponseHandler<? extends T> responseHandler)
636                throws IOException, ClientProtocolException {
637        return execute(target, request, responseHandler, null);
638    }
639
640
641    // non-javadoc, see interface HttpClient
642    public <T> T execute(
643            final HttpHost target,
644            final HttpRequest request,
645            final ResponseHandler<? extends T> responseHandler,
646            final HttpContext context)
647                throws IOException, ClientProtocolException {
648        if (responseHandler == null) {
649            throw new IllegalArgumentException
650                ("Response handler must not be null.");
651        }
652
653        HttpResponse response = execute(target, request, context);
654
655        T result;
656        try {
657            result = responseHandler.handleResponse(response);
658        } catch (Throwable t) {
659            HttpEntity entity = response.getEntity();
660            if (entity != null) {
661                try {
662                    entity.consumeContent();
663                } catch (Throwable t2) {
664                    // Log this exception. The original exception is more
665                    // important and will be thrown to the caller.
666                    this.log.warn("Error consuming content after an exception.", t2);
667                }
668            }
669
670            if (t instanceof Error) {
671                throw (Error) t;
672            }
673
674            if (t instanceof RuntimeException) {
675                throw (RuntimeException) t;
676            }
677
678            if (t instanceof IOException) {
679                throw (IOException) t;
680            }
681
682            throw new UndeclaredThrowableException(t);
683        }
684
685        // Handling the response was successful. Ensure that the content has
686        // been fully consumed.
687        HttpEntity entity = response.getEntity();
688        if (entity != null) {
689            // Let this exception go to the caller.
690            entity.consumeContent();
691        }
692
693        return result;
694    }
695
696
697} // class AbstractHttpClient
698