001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.component.jhc;
018
019 import java.io.IOException;
020 import java.io.InterruptedIOException;
021 import java.net.InetSocketAddress;
022 import java.net.SocketAddress;
023 import java.util.Arrays;
024 import java.util.HashSet;
025 import java.util.Iterator;
026 import java.util.Set;
027 import java.util.concurrent.ThreadFactory;
028
029 import org.apache.camel.AsyncCallback;
030 import org.apache.camel.AsyncProcessor;
031 import org.apache.camel.Exchange;
032 import org.apache.camel.Message;
033 import org.apache.camel.impl.DefaultProducer;
034 import org.apache.camel.spi.HeaderFilterStrategy;
035 import org.apache.camel.util.AsyncProcessorHelper;
036 import org.apache.commons.logging.Log;
037 import org.apache.commons.logging.LogFactory;
038 import org.apache.http.Header;
039 import org.apache.http.HttpEntity;
040 import org.apache.http.HttpException;
041 import org.apache.http.HttpRequest;
042 import org.apache.http.HttpResponse;
043 import org.apache.http.entity.ByteArrayEntity;
044 import org.apache.http.impl.DefaultConnectionReuseStrategy;
045 import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
046 import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
047 import org.apache.http.message.BasicHttpEntityEnclosingRequest;
048 import org.apache.http.message.BasicHttpRequest;
049 import org.apache.http.nio.NHttpConnection;
050 import org.apache.http.nio.protocol.BufferingHttpClientHandler;
051 import org.apache.http.nio.protocol.EventListener;
052 import org.apache.http.nio.protocol.HttpRequestExecutionHandler;
053 import org.apache.http.nio.reactor.ConnectingIOReactor;
054 import org.apache.http.nio.reactor.IOEventDispatch;
055 import org.apache.http.nio.reactor.SessionRequest;
056 import org.apache.http.nio.reactor.SessionRequestCallback;
057 import org.apache.http.params.HttpParams;
058 import org.apache.http.protocol.BasicHttpProcessor;
059 import org.apache.http.protocol.HttpContext;
060 import org.apache.http.protocol.RequestConnControl;
061 import org.apache.http.protocol.RequestContent;
062 import org.apache.http.protocol.RequestExpectContinue;
063 import org.apache.http.protocol.RequestTargetHost;
064 import org.apache.http.protocol.RequestUserAgent;
065
066 public class JhcProducer extends DefaultProducer<JhcExchange> implements AsyncProcessor {
067
068 public static final String HTTP_RESPONSE_CODE = "http.responseCode";
069
070 @Deprecated
071 public static final Set<String> HEADERS_TO_SKIP = new HashSet<String>(Arrays.asList(
072 "content-length", "content-type", HTTP_RESPONSE_CODE.toLowerCase()));
073
074 private static final transient Log LOG = LogFactory.getLog(JhcProducer.class);
075
076 private int nbThreads = 2;
077 private ConnectingIOReactor ioReactor;
078 private ThreadFactory threadFactory;
079 private Thread runner;
080
081 public JhcProducer(JhcEndpoint endpoint) {
082 super(endpoint);
083 }
084
085 @Override
086 public JhcEndpoint getEndpoint() {
087 return (JhcEndpoint) super.getEndpoint();
088 }
089
090 @Override
091 protected void doStart() throws Exception {
092 super.doStart();
093 HttpParams params = getEndpoint().getParams();
094 ioReactor = new DefaultConnectingIOReactor(nbThreads, threadFactory, params);
095 BasicHttpProcessor httpproc = new BasicHttpProcessor();
096 httpproc.addInterceptor(new RequestContent());
097 httpproc.addInterceptor(new RequestTargetHost());
098 httpproc.addInterceptor(new RequestConnControl());
099 httpproc.addInterceptor(new RequestUserAgent());
100 httpproc.addInterceptor(new RequestExpectContinue());
101 BufferingHttpClientHandler handler = new BufferingHttpClientHandler(
102 httpproc,
103 new MyHttpRequestExecutionHandler(),
104 new DefaultConnectionReuseStrategy(),
105 params);
106 handler.setEventListener(new EventLogger());
107 final IOEventDispatch ioEventDispatch = new DefaultClientIOEventDispatch(handler, params);
108 runner = new Thread(new Runnable() {
109 public void run() {
110 try {
111 ioReactor.execute(ioEventDispatch);
112 } catch (InterruptedIOException ex) {
113 LOG.info("Interrupted");
114 } catch (IOException e) {
115 LOG.warn("I/O error: " + e.getMessage());
116 }
117 LOG.debug("Shutdown");
118 }
119
120 });
121 runner.start();
122 }
123
124 @Override
125 protected void doStop() throws Exception {
126 ioReactor.shutdown();
127 runner.join();
128 super.doStop();
129 }
130
131 public void process(Exchange exchange) throws Exception {
132 if (LOG.isDebugEnabled()) {
133 LOG.debug("process: " + exchange);
134 }
135 AsyncProcessorHelper.process(this, exchange);
136 }
137
138 public boolean process(Exchange exchange, AsyncCallback callback) {
139 if (LOG.isDebugEnabled()) {
140 LOG.debug("processAsync: " + exchange);
141 }
142 SocketAddress addr = new InetSocketAddress(getEndpoint().getHost(), getEndpoint().getPort());
143 exchange.setProperty(AsyncCallback.class.getName(), callback);
144 SessionRequest req = ioReactor.connect(addr, null, exchange, new MySessionRequestCallback());
145 return false;
146 }
147
148 protected HttpRequest createRequest(Exchange exchange) {
149 String uri = getEndpoint().getEndpointUri();
150 HttpEntity entity = createEntity(exchange);
151 HttpRequest req;
152 if (entity == null) {
153 req = new BasicHttpRequest("GET", getEndpoint().getPath());
154 } else {
155 req = new BasicHttpEntityEnclosingRequest("POST", getEndpoint().getPath());
156 ((BasicHttpEntityEnclosingRequest)req).setEntity(entity);
157 }
158
159 // propagate headers as HTTP headers
160 HeaderFilterStrategy strategy = ((JhcEndpoint)getEndpoint()).getHeaderFilterStrategy();
161 for (String headerName : exchange.getIn().getHeaders().keySet()) {
162 String headerValue = exchange.getIn().getHeader(headerName, String.class);
163 if (strategy != null && !strategy.applyFilterToCamelHeaders(headerName, headerValue)) {
164 req.addHeader(headerName, headerValue);
165 }
166 }
167
168 return req;
169 }
170
171 protected HttpEntity createEntity(Exchange exchange) {
172 Message in = exchange.getIn();
173 HttpEntity entity = in.getBody(HttpEntity.class);
174 if (entity == null) {
175 byte[] data = in.getBody(byte[].class);
176 if (data == null) {
177 return null;
178 }
179 entity = new ByteArrayEntity(data);
180 String contentType = in.getHeader("Content-Type", String.class);
181 if (contentType != null) {
182 ((ByteArrayEntity) entity).setContentType(contentType);
183 }
184 String contentEncoding = in.getHeader("Content-Encoding", String.class);
185 if (contentEncoding != null) {
186 ((ByteArrayEntity) entity).setContentEncoding(contentEncoding);
187 }
188 }
189 return entity;
190 }
191
192 static class MySessionRequestCallback implements SessionRequestCallback {
193
194 public void completed(SessionRequest sessionRequest) {
195 if (LOG.isDebugEnabled()) {
196 LOG.debug("Completed");
197 }
198 }
199
200 public void failed(SessionRequest sessionRequest) {
201 if (LOG.isDebugEnabled()) {
202 LOG.debug("Failed");
203 }
204 }
205
206 public void timeout(SessionRequest sessionRequest) {
207 if (LOG.isDebugEnabled()) {
208 LOG.debug("Timeout");
209 }
210 }
211
212 public void cancelled(SessionRequest sessionRequest) {
213 if (LOG.isDebugEnabled()) {
214 LOG.debug("Cancelled");
215 }
216 }
217 }
218
219 class MyHttpRequestExecutionHandler implements HttpRequestExecutionHandler {
220
221 private static final String REQUEST_SENT = "request-sent";
222 private static final String RESPONSE_RECEIVED = "response-received";
223
224 public void initalizeContext(HttpContext httpContext, Object o) {
225 if (LOG.isDebugEnabled()) {
226 LOG.debug("Initialize context");
227 }
228 httpContext.setAttribute(Exchange.class.getName(), (Exchange) o);
229 }
230
231 public HttpRequest submitRequest(HttpContext httpContext) {
232 if (LOG.isDebugEnabled()) {
233 LOG.debug("Submit request: " + httpContext);
234 }
235 Object flag = httpContext.getAttribute(REQUEST_SENT);
236 if (flag == null) {
237 // Stick some object into the context
238 httpContext.setAttribute(REQUEST_SENT, Boolean.TRUE);
239 Exchange e = (Exchange) httpContext.getAttribute(Exchange.class.getName());
240 return createRequest(e);
241 } else {
242 return null;
243 }
244 }
245
246 public void handleResponse(HttpResponse httpResponse, HttpContext httpContext) throws IOException {
247 if (LOG.isDebugEnabled()) {
248 LOG.debug("Handle response");
249 }
250 httpContext.setAttribute(RESPONSE_RECEIVED, Boolean.TRUE);
251 Exchange e = (Exchange) httpContext.getAttribute(Exchange.class.getName());
252 e.getOut().setBody(httpResponse.getEntity());
253
254 HeaderFilterStrategy strategy = getEndpoint().getHeaderFilterStrategy();
255 for (Iterator it = httpResponse.headerIterator(); it.hasNext();) {
256 Header h = (Header) it.next();
257 if (strategy != null && !strategy.applyFilterToExternalHeaders(h.getName(), h.getValue())) {
258 e.getOut().setHeader(h.getName(), h.getValue());
259 }
260 }
261
262 e.getOut().setHeader(HTTP_RESPONSE_CODE, httpResponse.getStatusLine().getStatusCode());
263 AsyncCallback callback = (AsyncCallback) e.removeProperty(AsyncCallback.class.getName());
264 callback.done(false);
265 }
266
267 public void finalizeContext(HttpContext httpContext) {
268 }
269 }
270
271 static class EventLogger implements EventListener {
272
273 public void connectionOpen(final NHttpConnection conn) {
274 if (LOG.isDebugEnabled()) {
275 LOG.debug("Connection open: " + conn);
276 }
277 }
278
279 public void connectionTimeout(final NHttpConnection conn) {
280 if (LOG.isDebugEnabled()) {
281 LOG.debug("Connection timed out: " + conn);
282 }
283 }
284
285 public void connectionClosed(final NHttpConnection conn) {
286 if (LOG.isDebugEnabled()) {
287 LOG.debug("Connection closed: " + conn);
288 }
289 }
290
291 public void fatalIOException(final IOException ex, final NHttpConnection conn) {
292 if (LOG.isDebugEnabled()) {
293 LOG.debug("I/O error: " + ex.getMessage());
294 }
295 }
296
297 public void fatalProtocolException(final HttpException ex, final NHttpConnection conn) {
298 if (LOG.isDebugEnabled()) {
299 LOG.debug("HTTP error: " + ex.getMessage());
300 }
301 }
302
303 }
304
305 }