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