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