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