Coverage Report - org.mule.transport.http.HttpClientMessageDispatcher
 
Classes in this File Line Coverage Branch Coverage Complexity
HttpClientMessageDispatcher
0%
0/143
0%
0/56
0
 
 1  
 /*
 2  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 3  
  * The software in this package is published under the terms of the CPAL v1.0
 4  
  * license, a copy of which has been included with this distribution in the
 5  
  * LICENSE.txt file.
 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  
  * <code>HttpClientMessageDispatcher</code> dispatches Mule events over HTTP.
 45  
  */
 46  
 public class HttpClientMessageDispatcher extends AbstractMessageDispatcher
 47  
 {
 48  
     /**
 49  
      * Range start for http error status codes.
 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  0
     private volatile HttpClient client = null;
 55  
     private final Transformer sendTransformer;
 56  
 
 57  
     public HttpClientMessageDispatcher(OutboundEndpoint endpoint)
 58  
     {
 59  0
         super(endpoint);
 60  0
         this.httpConnector = (HttpConnector) endpoint.getConnector();
 61  0
         List<Transformer> ts = httpConnector.getDefaultOutboundTransformers(null);
 62  0
         if (ts.size() == 1)
 63  
         {
 64  0
             this.sendTransformer = ts.get(0);
 65  
         }
 66  0
         else if (ts.size() == 0)
 67  
         {
 68  0
             this.sendTransformer = new ObjectToHttpClientMethodRequest();
 69  0
             this.sendTransformer.setMuleContext(httpConnector.getMuleContext());
 70  0
             this.sendTransformer.setEndpoint(endpoint);
 71  
         }
 72  
         else
 73  
         {
 74  0
             this.sendTransformer = new TransformerChain(ts);
 75  
         }
 76  0
     }
 77  
 
 78  
     @Override
 79  
     protected void doInitialise() throws InitialisationException
 80  
     {
 81  0
         super.doInitialise();
 82  0
         sendTransformer.initialise();
 83  0
     }
 84  
 
 85  
     @Override
 86  
     protected void doConnect() throws Exception
 87  
     {
 88  0
         if (client == null)
 89  
         {
 90  0
             client = httpConnector.doClientConnect();
 91  
         }
 92  0
     }
 93  
 
 94  
     @Override
 95  
     protected void doDisconnect() throws Exception
 96  
     {
 97  0
         client = null;
 98  0
     }
 99  
 
 100  
     @Override
 101  
     protected void doDispatch(MuleEvent event) throws Exception
 102  
     {
 103  0
         HttpMethod httpMethod = getMethod(event);
 104  0
         httpConnector.setupClientAuthorization(event, httpMethod, client, endpoint);
 105  
 
 106  
         try
 107  
         {
 108  0
             execute(event, httpMethod);
 109  
 
 110  0
             if (returnException(event, httpMethod))
 111  
             {
 112  0
                 logger.error(httpMethod.getResponseBodyAsString());
 113  
 
 114  0
                 Exception cause = new Exception(String.format("Http call returned a status of: %1d %1s",
 115  
                     httpMethod.getStatusCode(), httpMethod.getStatusText()));
 116  0
                 throw new DispatchException(event, (OutboundEndpoint) endpoint, cause);
 117  
             }
 118  0
             else if (httpMethod.getStatusCode() >= REDIRECT_STATUS_CODE_RANGE_START)
 119  
             {
 120  0
                 if (logger.isInfoEnabled())
 121  
                 {
 122  0
                     logger.info("Received a redirect response code: " + httpMethod.getStatusCode() + " " + httpMethod.getStatusText());
 123  
                 }
 124  
             }
 125  
         }
 126  
         finally
 127  
         {
 128  0
             httpMethod.releaseConnection();
 129  0
         }
 130  0
     }
 131  
 
 132  
     protected HttpMethod execute(MuleEvent event, HttpMethod httpMethod) throws Exception
 133  
     {
 134  
         // TODO set connection timeout buffer etc
 135  
         try
 136  
         {
 137  0
             URI uri = event.getEndpoint().getEndpointURI().getUri();
 138  
 
 139  0
             this.processCookies(event);
 140  0
             this.processMuleSession(event, httpMethod);
 141  
 
 142  
             // TODO can we use the return code for better reporting?
 143  0
             client.executeMethod(getHostConfig(uri), httpMethod);
 144  
 
 145  0
             return httpMethod;
 146  
         }
 147  0
         catch (IOException e)
 148  
         {
 149  
             // TODO employ dispatcher reconnection strategy at this point
 150  0
             throw new DispatchException(event, (OutboundEndpoint) endpoint, e);
 151  
         }
 152  0
         catch (Exception e)
 153  
         {
 154  0
             throw new DispatchException(event, (OutboundEndpoint) endpoint, e);
 155  
         }
 156  
 
 157  
     }
 158  
 
 159  
     private void processMuleSession(MuleEvent event, HttpMethod httpMethod)
 160  
     {
 161  0
         String muleSession = event.getMessage().getOutboundProperty(MuleProperties.MULE_SESSION_PROPERTY);
 162  
 
 163  0
         if (muleSession != null)
 164  
         {
 165  0
             httpMethod.setRequestHeader(new Header(HttpConstants.HEADER_MULE_SESSION, muleSession));
 166  
         }
 167  0
     }
 168  
 
 169  
     protected void processCookies(MuleEvent event)
 170  
     {
 171  0
         MuleMessage msg = event.getMessage();
 172  
 
 173  0
         Object cookiesProperty = msg.getInboundProperty(HttpConnector.HTTP_COOKIES_PROPERTY);
 174  0
         String cookieSpecProperty = (String) msg.getInboundProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY);
 175  0
         processCookies(cookiesProperty, cookieSpecProperty, event);
 176  
 
 177  0
         cookiesProperty = msg.getOutboundProperty(HttpConnector.HTTP_COOKIES_PROPERTY);
 178  0
         cookieSpecProperty = (String) msg.getOutboundProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY);
 179  0
         processCookies(cookiesProperty, cookieSpecProperty, event);
 180  
 
 181  0
         cookiesProperty = endpoint.getProperty(HttpConnector.HTTP_COOKIES_PROPERTY);
 182  0
         cookieSpecProperty = (String) endpoint.getProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY);
 183  0
         processCookies(cookiesProperty, cookieSpecProperty, event);
 184  0
     }
 185  
 
 186  
     private void processCookies(Object cookieObject, String policy, MuleEvent event)
 187  
     {
 188  0
         URI uri = this.getEndpoint().getEndpointURI().getUri();
 189  0
         CookieHelper.addCookiesToClient(this.client, cookieObject, policy, event, uri);
 190  0
     }
 191  
 
 192  
     protected HttpMethod getMethod(MuleEvent event) throws TransformerException
 193  
     {
 194  
         // Configure timeout. This is done here because MuleEvent.getTimeout() takes
 195  
         // precedence and is not available before send/dispatch.
 196  
         // Given that dispatchers are borrowed from a thread pool mutating client
 197  
         // here is ok even though it is not ideal.
 198  0
         client.getHttpConnectionManager().getParams().setConnectionTimeout(event.getTimeout());
 199  0
         client.getHttpConnectionManager().getParams().setSoTimeout(event.getTimeout());
 200  
 
 201  0
         MuleMessage msg = event.getMessage();
 202  0
         setPropertyFromEndpoint(event, msg, HttpConnector.HTTP_CUSTOM_HEADERS_MAP_PROPERTY);
 203  
 
 204  
         HttpMethod httpMethod;
 205  0
         Object body = event.getMessage().getPayload();
 206  
 
 207  0
         if (body instanceof HttpMethod)
 208  
         {
 209  0
             httpMethod = (HttpMethod) body;
 210  
         }
 211  
         else
 212  
         {
 213  0
             httpMethod = (HttpMethod) sendTransformer.transform(msg);
 214  
         }
 215  
 
 216  0
         httpMethod.setFollowRedirects("true".equalsIgnoreCase((String)endpoint.getProperty("followRedirects")));
 217  0
         return httpMethod;
 218  
     }
 219  
 
 220  
     protected void setPropertyFromEndpoint(MuleEvent event, MuleMessage msg, String prop)
 221  
     {
 222  0
         Object o = msg.getOutboundProperty(prop);
 223  0
         if (o == null)
 224  
         {
 225  0
             o = event.getEndpoint().getProperty(prop);
 226  0
             if (o != null)
 227  
             {
 228  0
                 msg.setOutboundProperty(prop, o);
 229  
             }
 230  
         }
 231  0
     }
 232  
 
 233  
     protected HttpMethod createEntityMethod(MuleEvent event, Object body, EntityEnclosingMethod postMethod) throws TransformerException
 234  
     {
 235  
         HttpMethod httpMethod;
 236  0
         if (body instanceof String)
 237  
         {
 238  0
             httpMethod = (HttpMethod) sendTransformer.transform(body.toString());
 239  
         }
 240  0
         else if (body instanceof byte[])
 241  
         {
 242  0
             byte[] buffer = event.transformMessage(DataType.BYTE_ARRAY_DATA_TYPE);
 243  0
             postMethod.setRequestEntity(new ByteArrayRequestEntity(buffer, event.getEncoding()));
 244  0
             httpMethod = postMethod;
 245  0
         }
 246  
         else
 247  
         {
 248  0
             if (!(body instanceof OutputHandler))
 249  
             {
 250  0
                 body = event.transformMessage(DataTypeFactory.create(OutputHandler.class));
 251  
             }
 252  
 
 253  0
             OutputHandler outputHandler = (OutputHandler) body;
 254  0
             postMethod.setRequestEntity(new StreamPayloadRequestEntity(outputHandler, event));
 255  0
             postMethod.setContentChunked(true);
 256  0
             httpMethod = postMethod;
 257  
         }
 258  
 
 259  0
         return httpMethod;
 260  
     }
 261  
 
 262  
     @Override
 263  
     protected MuleMessage doSend(MuleEvent event) throws Exception
 264  
     {
 265  0
         HttpMethod httpMethod = getMethod(event);
 266  0
         httpConnector.setupClientAuthorization(event, httpMethod, client, endpoint);
 267  
 
 268  0
         httpMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new MuleHttpMethodRetryHandler());
 269  0
         boolean releaseConn = false;
 270  
         try
 271  
         {
 272  0
             httpMethod = execute(event, httpMethod);
 273  
 
 274  0
             DefaultExceptionPayload ep = null;
 275  
 
 276  0
             if (returnException(event, httpMethod))
 277  
             {
 278  0
                 ep = new DefaultExceptionPayload(new DispatchException(event, (OutboundEndpoint) endpoint,
 279  
                         new HttpResponseException(httpMethod.getStatusText(), httpMethod.getStatusCode())));
 280  
             }
 281  0
             else if (httpMethod.getStatusCode() >= REDIRECT_STATUS_CODE_RANGE_START)
 282  
             {
 283  
                 try
 284  
                 {
 285  0
                     return handleRedirect(httpMethod, event);
 286  
                 }
 287  0
                 catch (Exception e)
 288  
                 {
 289  0
                     ep = new DefaultExceptionPayload(new DispatchException(event, (OutboundEndpoint) endpoint, e));
 290  0
                     return getResponseFromMethod(httpMethod, ep);
 291  
                 }
 292  
             }
 293  0
             releaseConn = httpMethod.getResponseBodyAsStream() == null;
 294  0
             return getResponseFromMethod(httpMethod, ep);
 295  
         }
 296  0
         catch (Exception e)
 297  
         {
 298  0
             releaseConn = true;
 299  0
             if (e instanceof DispatchException)
 300  
             {
 301  0
                 throw (DispatchException) e;
 302  
             }
 303  0
             throw new DispatchException(event, (OutboundEndpoint) endpoint, e);
 304  
         }
 305  
         finally
 306  
         {
 307  0
             if (releaseConn)
 308  
             {
 309  0
                 httpMethod.releaseConnection();
 310  
             }
 311  
         }
 312  
     }
 313  
 
 314  
     protected MuleMessage handleRedirect(HttpMethod method, MuleEvent event) throws HttpResponseException, MuleException, IOException
 315  
     {
 316  0
         String followRedirects = (String)endpoint.getProperty("followRedirects");
 317  0
         if (followRedirects==null || "false".equalsIgnoreCase(followRedirects))
 318  
         {
 319  0
             if (logger.isInfoEnabled())
 320  
             {
 321  0
                 logger.info("Received a redirect, but followRedirects=false. Response code: " + method.getStatusCode() + " " + method.getStatusText());
 322  
             }
 323  0
             return getResponseFromMethod(method, null);
 324  
         }
 325  0
         Header locationHeader = method.getResponseHeader(HttpConstants.HEADER_LOCATION);
 326  0
         if (locationHeader == null)
 327  
         {
 328  0
             throw new HttpResponseException(method.getStatusText(), method.getStatusCode());
 329  
         }
 330  0
         OutboundEndpoint out = new EndpointURIEndpointBuilder(locationHeader.getValue(),
 331  
             httpConnector.getMuleContext()).buildOutboundEndpoint();
 332  0
         MuleEvent result = out.process(event);
 333  0
         if (result != null)
 334  
         {
 335  0
             return result.getMessage();
 336  
         }
 337  
         else
 338  
         {
 339  0
             return null;
 340  
         }
 341  
     }
 342  
 
 343  
     protected MuleMessage getResponseFromMethod(HttpMethod httpMethod, ExceptionPayload ep)
 344  
         throws IOException, MuleException
 345  
     {
 346  0
         MuleMessage message = createMuleMessage(httpMethod);
 347  
 
 348  0
         if (logger.isDebugEnabled())
 349  
         {
 350  0
             logger.debug("Http response is: " + message.getOutboundProperty(HttpConnector.HTTP_STATUS_PROPERTY));
 351  
         }
 352  
 
 353  0
         message.setExceptionPayload(ep);
 354  0
         return message;
 355  
     }
 356  
 
 357  
     protected boolean returnException(MuleEvent event, HttpMethod httpMethod)
 358  
     {
 359  0
         String disableCheck = event.getMessage().getInvocationProperty(HttpConnector.HTTP_DISABLE_STATUS_CODE_EXCEPTION_CHECK);
 360  0
         if (disableCheck == null)
 361  
         {
 362  0
             disableCheck = event.getMessage().getOutboundProperty(HttpConnector.HTTP_DISABLE_STATUS_CODE_EXCEPTION_CHECK);
 363  
         }
 364  0
         return httpMethod.getStatusCode() >= ERROR_STATUS_CODE_RANGE_START
 365  
                 && !BooleanUtils.toBoolean(disableCheck);
 366  
     }
 367  
 
 368  
     protected HostConfiguration getHostConfig(URI uri) throws Exception
 369  
     {
 370  0
         Protocol protocol = Protocol.getProtocol(uri.getScheme().toLowerCase());
 371  
 
 372  0
         String host = uri.getHost();
 373  0
         int port = uri.getPort();
 374  0
         HostConfiguration config = new HostConfiguration();
 375  0
         config.setHost(host, port, protocol);
 376  0
         if (StringUtils.isNotBlank(httpConnector.getProxyHostname()))
 377  
         {
 378  
             // add proxy support
 379  0
             config.setProxy(httpConnector.getProxyHostname(), httpConnector.getProxyPort());
 380  
         }
 381  0
         return config;
 382  
     }
 383  
 
 384  
     @Override
 385  
     protected void doDispose()
 386  
     {
 387  
         // template method
 388  0
     }
 389  
 
 390  
 }