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