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