View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.nio.protocol;
29  
30  import java.io.IOException;
31  import java.util.concurrent.Future;
32  
33  import org.apache.http.ConnectionClosedException;
34  import org.apache.http.ConnectionReuseStrategy;
35  import org.apache.http.HttpException;
36  import org.apache.http.HttpRequest;
37  import org.apache.http.HttpResponse;
38  import org.apache.http.concurrent.BasicFuture;
39  import org.apache.http.concurrent.FutureCallback;
40  import org.apache.http.impl.DefaultConnectionReuseStrategy;
41  import org.apache.http.nio.ContentDecoder;
42  import org.apache.http.nio.ContentEncoder;
43  import org.apache.http.nio.IOControl;
44  import org.apache.http.nio.NHttpClientConnection;
45  import org.apache.http.protocol.HttpContext;
46  import org.apache.http.protocol.HttpCoreContext;
47  import org.apache.http.protocol.HttpProcessor;
48  import org.apache.http.util.Args;
49  
50  /**
51   * Basic implementation of {@link HttpAsyncClientExchangeHandler} that executes
52   * a single HTTP request / response exchange.
53   *
54   * @param <T> the result type of request execution.
55   * @since 4.3
56   */
57  public class BasicAsyncClientExchangeHandler<T> implements HttpAsyncClientExchangeHandler {
58  
59      private final HttpAsyncRequestProducer requestProducer;
60      private final HttpAsyncResponseConsumer<T> responseConsumer;
61      private final BasicFuture<T> future;
62      private final HttpContext localContext;
63      private final NHttpClientConnection conn;
64      private final HttpProcessor httppocessor;
65      private final ConnectionReuseStrategy connReuseStrategy;
66  
67      private volatile boolean requestSent;
68      private volatile boolean keepAlive;
69  
70      /**
71       * Creates new instance of BasicAsyncRequestExecutionHandler.
72       *
73       * @param requestProducer the request producer.
74       * @param responseConsumer the response consumer.
75       * @param callback the future callback invoked when the operation is completed.
76       * @param localContext the local execution context.
77       * @param conn the actual connection.
78       * @param httppocessor the HTTP protocol processor.
79       * @param connReuseStrategy the connection re-use strategy.
80       */
81      public BasicAsyncClientExchangeHandler(
82              final HttpAsyncRequestProducer requestProducer,
83              final HttpAsyncResponseConsumer<T> responseConsumer,
84              final FutureCallback<T> callback,
85              final HttpContext localContext,
86              final NHttpClientConnection conn,
87              final HttpProcessor httppocessor,
88              final ConnectionReuseStrategy connReuseStrategy) {
89          super();
90          this.requestProducer = Args.notNull(requestProducer, "Request producer");
91          this.responseConsumer = Args.notNull(responseConsumer, "Response consumer");
92          this.future = new BasicFuture<T>(callback);
93          this.localContext = Args.notNull(localContext, "HTTP context");
94          this.conn = Args.notNull(conn, "HTTP connection");
95          this.httppocessor = Args.notNull(httppocessor, "HTTP processor");
96          this.connReuseStrategy = connReuseStrategy != null ? connReuseStrategy :
97              DefaultConnectionReuseStrategy.INSTANCE;
98      }
99  
100     /**
101      * Creates new instance of BasicAsyncRequestExecutionHandler.
102      *
103      * @param requestProducer the request producer.
104      * @param responseConsumer the response consumer.
105      * @param localContext the local execution context.
106      * @param conn the actual connection.
107      * @param httppocessor the HTTP protocol processor.
108      */
109     public BasicAsyncClientExchangeHandler(
110             final HttpAsyncRequestProducer requestProducer,
111             final HttpAsyncResponseConsumer<T> responseConsumer,
112             final HttpContext localContext,
113             final NHttpClientConnection conn,
114             final HttpProcessor httppocessor) {
115         this(requestProducer, responseConsumer, null, localContext, conn, httppocessor, null);
116     }
117 
118     public Future<T> getFuture() {
119         return this.future;
120     }
121 
122     private void releaseResources() {
123         try {
124             this.responseConsumer.close();
125         } catch (final IOException ex) {
126         }
127         try {
128             this.requestProducer.close();
129         } catch (final IOException ex) {
130         }
131     }
132 
133     @Override
134     public void close() throws IOException {
135         releaseResources();
136         if (!this.future.isDone()) {
137             this.future.cancel();
138         }
139     }
140 
141     @Override
142     public HttpRequest generateRequest() throws IOException, HttpException {
143         if (isDone()) {
144             return null;
145         }
146         final HttpRequest request = this.requestProducer.generateRequest();
147         this.localContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
148         this.localContext.setAttribute(HttpCoreContext.HTTP_CONNECTION, this.conn);
149         this.httppocessor.process(request, this.localContext);
150         return request;
151     }
152 
153     @Override
154     public void produceContent(
155             final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
156         this.requestProducer.produceContent(encoder, ioctrl);
157     }
158 
159     @Override
160     public void requestCompleted() {
161         this.requestProducer.requestCompleted(this.localContext);
162         this.requestSent = true;
163     }
164 
165     @Override
166     public void responseReceived(final HttpResponse response) throws IOException, HttpException {
167         this.localContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
168         this.httppocessor.process(response, this.localContext);
169         this.responseConsumer.responseReceived(response);
170         this.keepAlive = this.connReuseStrategy.keepAlive(response, this.localContext);
171     }
172 
173     @Override
174     public void consumeContent(
175             final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
176         this.responseConsumer.consumeContent(decoder, ioctrl);
177     }
178 
179     @Override
180     public void responseCompleted() throws IOException {
181         try {
182             if (!this.keepAlive) {
183                 this.conn.close();
184             }
185             this.responseConsumer.responseCompleted(this.localContext);
186             final T result = this.responseConsumer.getResult();
187             final Exception ex = this.responseConsumer.getException();
188             if (result != null) {
189                 this.future.completed(result);
190             } else {
191                 this.future.failed(ex);
192             }
193             releaseResources();
194         } catch (final RuntimeException ex) {
195             failed(ex);
196             throw ex;
197         }
198     }
199 
200     @Override
201     public void inputTerminated() {
202         failed(new ConnectionClosedException("Connection closed"));
203     }
204 
205     @Override
206     public void failed(final Exception ex) {
207         try {
208             if (!this.requestSent) {
209                 this.requestProducer.failed(ex);
210             }
211             this.responseConsumer.failed(ex);
212         } finally {
213             try {
214                 this.future.failed(ex);
215             } finally {
216                 releaseResources();
217             }
218         }
219     }
220 
221     @Override
222     public boolean cancel() {
223         try {
224             final boolean cancelled = this.responseConsumer.cancel();
225             this.future.cancel();
226             releaseResources();
227             return cancelled;
228         } catch (final RuntimeException ex) {
229             failed(ex);
230             throw ex;
231         }
232     }
233 
234     @Override
235     public boolean isDone() {
236         return this.responseConsumer.isDone();
237     }
238 
239 }