1
2
3
4
5
6
7 package org.mule.transport.http;
8
9 import org.mule.api.ExceptionPayload;
10 import org.mule.api.MuleEvent;
11 import org.mule.api.MuleException;
12 import org.mule.api.MuleMessage;
13 import org.mule.api.config.MuleProperties;
14 import org.mule.api.endpoint.OutboundEndpoint;
15 import org.mule.api.lifecycle.InitialisationException;
16 import org.mule.api.transformer.DataType;
17 import org.mule.api.transformer.Transformer;
18 import org.mule.api.transformer.TransformerException;
19 import org.mule.api.transport.DispatchException;
20 import org.mule.api.transport.OutputHandler;
21 import org.mule.endpoint.EndpointURIEndpointBuilder;
22 import org.mule.message.DefaultExceptionPayload;
23 import org.mule.transformer.TransformerChain;
24 import org.mule.transformer.types.DataTypeFactory;
25 import org.mule.transport.AbstractMessageDispatcher;
26 import org.mule.transport.http.transformers.ObjectToHttpClientMethodRequest;
27 import org.mule.util.StringUtils;
28
29 import java.io.IOException;
30 import java.net.URI;
31 import java.util.List;
32
33 import org.apache.commons.httpclient.Header;
34 import org.apache.commons.httpclient.HostConfiguration;
35 import org.apache.commons.httpclient.HttpClient;
36 import org.apache.commons.httpclient.HttpMethod;
37 import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
38 import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
39 import org.apache.commons.httpclient.params.HttpMethodParams;
40 import org.apache.commons.httpclient.protocol.Protocol;
41 import org.apache.commons.lang.BooleanUtils;
42
43
44
45
46 public class HttpClientMessageDispatcher extends AbstractMessageDispatcher
47 {
48
49
50
51 public static final int ERROR_STATUS_CODE_RANGE_START = 400;
52 public static final int REDIRECT_STATUS_CODE_RANGE_START = 300;
53 protected final HttpConnector httpConnector;
54 private volatile HttpClient client = null;
55 private final Transformer sendTransformer;
56
57 public HttpClientMessageDispatcher(OutboundEndpoint endpoint)
58 {
59 super(endpoint);
60 this.httpConnector = (HttpConnector) endpoint.getConnector();
61 List<Transformer> ts = httpConnector.getDefaultOutboundTransformers(null);
62 if (ts.size() == 1)
63 {
64 this.sendTransformer = ts.get(0);
65 }
66 else if (ts.size() == 0)
67 {
68 this.sendTransformer = new ObjectToHttpClientMethodRequest();
69 this.sendTransformer.setMuleContext(httpConnector.getMuleContext());
70 this.sendTransformer.setEndpoint(endpoint);
71 }
72 else
73 {
74 this.sendTransformer = new TransformerChain(ts);
75 }
76 }
77
78 @Override
79 protected void doInitialise() throws InitialisationException
80 {
81 super.doInitialise();
82 sendTransformer.initialise();
83 }
84
85 @Override
86 protected void doConnect() throws Exception
87 {
88 if (client == null)
89 {
90 client = httpConnector.doClientConnect();
91 }
92 }
93
94 @Override
95 protected void doDisconnect() throws Exception
96 {
97 client = null;
98 }
99
100 @Override
101 protected void doDispatch(MuleEvent event) throws Exception
102 {
103 HttpMethod httpMethod = getMethod(event);
104 httpConnector.setupClientAuthorization(event, httpMethod, client, endpoint);
105
106 try
107 {
108 execute(event, httpMethod);
109
110 if (returnException(event, httpMethod))
111 {
112 logger.error(httpMethod.getResponseBodyAsString());
113
114 Exception cause = new Exception(String.format("Http call returned a status of: %1d %1s",
115 httpMethod.getStatusCode(), httpMethod.getStatusText()));
116 throw new DispatchException(event, (OutboundEndpoint) endpoint, cause);
117 }
118 else if (httpMethod.getStatusCode() >= REDIRECT_STATUS_CODE_RANGE_START)
119 {
120 if (logger.isInfoEnabled())
121 {
122 logger.info("Received a redirect response code: " + httpMethod.getStatusCode() + " " + httpMethod.getStatusText());
123 }
124 }
125 }
126 finally
127 {
128 httpMethod.releaseConnection();
129 }
130 }
131
132 protected HttpMethod execute(MuleEvent event, HttpMethod httpMethod) throws Exception
133 {
134
135 try
136 {
137 URI uri = event.getEndpoint().getEndpointURI().getUri();
138
139 this.processCookies(event);
140 this.processMuleSession(event, httpMethod);
141
142
143 client.executeMethod(getHostConfig(uri), httpMethod);
144
145 return httpMethod;
146 }
147 catch (IOException e)
148 {
149
150 throw new DispatchException(event, (OutboundEndpoint) endpoint, e);
151 }
152 catch (Exception e)
153 {
154 throw new DispatchException(event, (OutboundEndpoint) endpoint, e);
155 }
156
157 }
158
159 private void processMuleSession(MuleEvent event, HttpMethod httpMethod)
160 {
161 String muleSession = event.getMessage().getOutboundProperty(MuleProperties.MULE_SESSION_PROPERTY);
162
163 if (muleSession != null)
164 {
165 httpMethod.setRequestHeader(new Header(HttpConstants.HEADER_MULE_SESSION, muleSession));
166 }
167 }
168
169 protected void processCookies(MuleEvent event)
170 {
171 MuleMessage msg = event.getMessage();
172
173 Object cookiesProperty = msg.getInboundProperty(HttpConnector.HTTP_COOKIES_PROPERTY);
174 String cookieSpecProperty = (String) msg.getInboundProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY);
175 processCookies(cookiesProperty, cookieSpecProperty, event);
176
177 cookiesProperty = msg.getOutboundProperty(HttpConnector.HTTP_COOKIES_PROPERTY);
178 cookieSpecProperty = (String) msg.getOutboundProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY);
179 processCookies(cookiesProperty, cookieSpecProperty, event);
180
181 cookiesProperty = endpoint.getProperty(HttpConnector.HTTP_COOKIES_PROPERTY);
182 cookieSpecProperty = (String) endpoint.getProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY);
183 processCookies(cookiesProperty, cookieSpecProperty, event);
184 }
185
186 private void processCookies(Object cookieObject, String policy, MuleEvent event)
187 {
188 URI uri = this.getEndpoint().getEndpointURI().getUri();
189 CookieHelper.addCookiesToClient(this.client, cookieObject, policy, event, uri);
190 }
191
192 protected HttpMethod getMethod(MuleEvent event) throws TransformerException
193 {
194
195
196
197
198 client.getHttpConnectionManager().getParams().setConnectionTimeout(event.getTimeout());
199 client.getHttpConnectionManager().getParams().setSoTimeout(event.getTimeout());
200
201 MuleMessage msg = event.getMessage();
202 setPropertyFromEndpoint(event, msg, HttpConnector.HTTP_CUSTOM_HEADERS_MAP_PROPERTY);
203
204 HttpMethod httpMethod;
205 Object body = event.getMessage().getPayload();
206
207 if (body instanceof HttpMethod)
208 {
209 httpMethod = (HttpMethod) body;
210 }
211 else
212 {
213 httpMethod = (HttpMethod) sendTransformer.transform(msg);
214 }
215
216 httpMethod.setFollowRedirects("true".equalsIgnoreCase((String)endpoint.getProperty("followRedirects")));
217 return httpMethod;
218 }
219
220 protected void setPropertyFromEndpoint(MuleEvent event, MuleMessage msg, String prop)
221 {
222 Object o = msg.getOutboundProperty(prop);
223 if (o == null)
224 {
225 o = event.getEndpoint().getProperty(prop);
226 if (o != null)
227 {
228 msg.setOutboundProperty(prop, o);
229 }
230 }
231 }
232
233 protected HttpMethod createEntityMethod(MuleEvent event, Object body, EntityEnclosingMethod postMethod) throws TransformerException
234 {
235 HttpMethod httpMethod;
236 if (body instanceof String)
237 {
238 httpMethod = (HttpMethod) sendTransformer.transform(body.toString());
239 }
240 else if (body instanceof byte[])
241 {
242 byte[] buffer = event.transformMessage(DataType.BYTE_ARRAY_DATA_TYPE);
243 postMethod.setRequestEntity(new ByteArrayRequestEntity(buffer, event.getEncoding()));
244 httpMethod = postMethod;
245 }
246 else
247 {
248 if (!(body instanceof OutputHandler))
249 {
250 body = event.transformMessage(DataTypeFactory.create(OutputHandler.class));
251 }
252
253 OutputHandler outputHandler = (OutputHandler) body;
254 postMethod.setRequestEntity(new StreamPayloadRequestEntity(outputHandler, event));
255 postMethod.setContentChunked(true);
256 httpMethod = postMethod;
257 }
258
259 return httpMethod;
260 }
261
262 @Override
263 protected MuleMessage doSend(MuleEvent event) throws Exception
264 {
265 HttpMethod httpMethod = getMethod(event);
266 httpConnector.setupClientAuthorization(event, httpMethod, client, endpoint);
267
268 httpMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new MuleHttpMethodRetryHandler());
269 boolean releaseConn = false;
270 try
271 {
272 httpMethod = execute(event, httpMethod);
273
274 DefaultExceptionPayload ep = null;
275
276 if (returnException(event, httpMethod))
277 {
278 ep = new DefaultExceptionPayload(new DispatchException(event, (OutboundEndpoint) endpoint,
279 new HttpResponseException(httpMethod.getStatusText(), httpMethod.getStatusCode())));
280 }
281 else if (httpMethod.getStatusCode() >= REDIRECT_STATUS_CODE_RANGE_START)
282 {
283 try
284 {
285 return handleRedirect(httpMethod, event);
286 }
287 catch (Exception e)
288 {
289 ep = new DefaultExceptionPayload(new DispatchException(event, (OutboundEndpoint) endpoint, e));
290 return getResponseFromMethod(httpMethod, ep);
291 }
292 }
293 releaseConn = httpMethod.getResponseBodyAsStream() == null;
294 return getResponseFromMethod(httpMethod, ep);
295 }
296 catch (Exception e)
297 {
298 releaseConn = true;
299 if (e instanceof DispatchException)
300 {
301 throw (DispatchException) e;
302 }
303 throw new DispatchException(event, (OutboundEndpoint) endpoint, e);
304 }
305 finally
306 {
307 if (releaseConn)
308 {
309 httpMethod.releaseConnection();
310 }
311 }
312 }
313
314 protected MuleMessage handleRedirect(HttpMethod method, MuleEvent event) throws HttpResponseException, MuleException, IOException
315 {
316 String followRedirects = (String)endpoint.getProperty("followRedirects");
317 if (followRedirects==null || "false".equalsIgnoreCase(followRedirects))
318 {
319 if (logger.isInfoEnabled())
320 {
321 logger.info("Received a redirect, but followRedirects=false. Response code: " + method.getStatusCode() + " " + method.getStatusText());
322 }
323 return getResponseFromMethod(method, null);
324 }
325 Header locationHeader = method.getResponseHeader(HttpConstants.HEADER_LOCATION);
326 if (locationHeader == null)
327 {
328 throw new HttpResponseException(method.getStatusText(), method.getStatusCode());
329 }
330 OutboundEndpoint out = new EndpointURIEndpointBuilder(locationHeader.getValue(),
331 httpConnector.getMuleContext()).buildOutboundEndpoint();
332 MuleEvent result = out.process(event);
333 if (result != null)
334 {
335 return result.getMessage();
336 }
337 else
338 {
339 return null;
340 }
341 }
342
343 protected MuleMessage getResponseFromMethod(HttpMethod httpMethod, ExceptionPayload ep)
344 throws IOException, MuleException
345 {
346 MuleMessage message = createMuleMessage(httpMethod);
347
348 if (logger.isDebugEnabled())
349 {
350 logger.debug("Http response is: " + message.getOutboundProperty(HttpConnector.HTTP_STATUS_PROPERTY));
351 }
352
353 message.setExceptionPayload(ep);
354 return message;
355 }
356
357 protected boolean returnException(MuleEvent event, HttpMethod httpMethod)
358 {
359 String disableCheck = event.getMessage().getInvocationProperty(HttpConnector.HTTP_DISABLE_STATUS_CODE_EXCEPTION_CHECK);
360 if (disableCheck == null)
361 {
362 disableCheck = event.getMessage().getOutboundProperty(HttpConnector.HTTP_DISABLE_STATUS_CODE_EXCEPTION_CHECK);
363 }
364 return httpMethod.getStatusCode() >= ERROR_STATUS_CODE_RANGE_START
365 && !BooleanUtils.toBoolean(disableCheck);
366 }
367
368 protected HostConfiguration getHostConfig(URI uri) throws Exception
369 {
370 Protocol protocol = Protocol.getProtocol(uri.getScheme().toLowerCase());
371
372 String host = uri.getHost();
373 int port = uri.getPort();
374 HostConfiguration config = new HostConfiguration();
375 config.setHost(host, port, protocol);
376 if (StringUtils.isNotBlank(httpConnector.getProxyHostname()))
377 {
378
379 config.setProxy(httpConnector.getProxyHostname(), httpConnector.getProxyPort());
380 }
381 return config;
382 }
383
384 @Override
385 protected void doDispose()
386 {
387
388 }
389
390 }