Coverage Report - org.mule.providers.http.HttpClientMessageDispatcher
 
Classes in this File Line Coverage Branch Coverage Complexity
HttpClientMessageDispatcher
0%
0/128
0%
0/22
3.188
HttpClientMessageDispatcher$StreamPayloadRequestEntity
0%
0/9
N/A
3.188
 
 1  
 /*
 2  
  * $Id: HttpClientMessageDispatcher.java 7976 2007-08-21 14:26:13Z dirk.olmes $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.providers.http;
 12  
 
 13  
 import org.mule.impl.MuleMessage;
 14  
 import org.mule.impl.message.ExceptionPayload;
 15  
 import org.mule.providers.AbstractMessageDispatcher;
 16  
 import org.mule.providers.http.i18n.HttpMessages;
 17  
 import org.mule.providers.http.transformers.HttpClientMethodResponseToObject;
 18  
 import org.mule.providers.http.transformers.ObjectToHttpClientMethodRequest;
 19  
 import org.mule.providers.streaming.StreamMessageAdapter;
 20  
 import org.mule.umo.UMOEvent;
 21  
 import org.mule.umo.UMOMessage;
 22  
 import org.mule.umo.endpoint.UMOImmutableEndpoint;
 23  
 import org.mule.umo.provider.DispatchException;
 24  
 import org.mule.umo.provider.ReceiveException;
 25  
 import org.mule.umo.provider.UMOMessageAdapter;
 26  
 import org.mule.umo.provider.UMOStreamMessageAdapter;
 27  
 import org.mule.umo.transformer.TransformerException;
 28  
 import org.mule.umo.transformer.UMOTransformer;
 29  
 import org.mule.util.StringUtils;
 30  
 
 31  
 import java.io.IOException;
 32  
 import java.io.OutputStream;
 33  
 import java.net.URI;
 34  
 import java.net.URISyntaxException;
 35  
 import java.util.Iterator;
 36  
 import java.util.Map;
 37  
 import java.util.Properties;
 38  
 
 39  
 import org.apache.commons.codec.binary.Base64;
 40  
 import org.apache.commons.httpclient.Cookie;
 41  
 import org.apache.commons.httpclient.Header;
 42  
 import org.apache.commons.httpclient.HostConfiguration;
 43  
 import org.apache.commons.httpclient.HttpClient;
 44  
 import org.apache.commons.httpclient.HttpMethod;
 45  
 import org.apache.commons.httpclient.HttpState;
 46  
 import org.apache.commons.httpclient.HttpStatus;
 47  
 import org.apache.commons.httpclient.UsernamePasswordCredentials;
 48  
 import org.apache.commons.httpclient.auth.AuthScope;
 49  
 import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
 50  
 import org.apache.commons.httpclient.methods.GetMethod;
 51  
 import org.apache.commons.httpclient.methods.PostMethod;
 52  
 import org.apache.commons.httpclient.methods.RequestEntity;
 53  
 import org.apache.commons.httpclient.params.HttpMethodParams;
 54  
 import org.apache.commons.httpclient.protocol.Protocol;
 55  
 import org.apache.commons.io.IOUtils;
 56  
 
 57  
 /**
 58  
  * <code>HttpClientMessageDispatcher</code> dispatches Mule events over HTTP.
 59  
  */
 60  
 public class HttpClientMessageDispatcher extends AbstractMessageDispatcher
 61  
 {
 62  
     private final HttpConnector connector;
 63  0
     private volatile HttpClient client = null;
 64  
     private final UMOTransformer receiveTransformer;
 65  
 
 66  
     public HttpClientMessageDispatcher(UMOImmutableEndpoint endpoint)
 67  
     {
 68  0
         super(endpoint);
 69  0
         this.connector = (HttpConnector) endpoint.getConnector();
 70  0
         this.receiveTransformer = new HttpClientMethodResponseToObject();
 71  0
     }
 72  
 
 73  
     protected void doConnect() throws Exception
 74  
     {
 75  0
         if (client == null)
 76  
         {
 77  0
             HttpState state = new HttpState();
 78  
 
 79  0
             if (connector.getProxyUsername() != null)
 80  
             {
 81  0
                 state.setProxyCredentials(new AuthScope(null, -1, null, null),
 82  
                     new UsernamePasswordCredentials(connector.getProxyUsername(),
 83  
                         connector.getProxyPassword()));
 84  
             }
 85  
 
 86  0
             client = new HttpClient();
 87  0
             client.setState(state);
 88  0
             client.setHttpConnectionManager(connector.getClientConnectionManager());
 89  
             //RM* This isn't a good idea since if the connection is not re-used a HEAD request is sent for
 90  
             //every invocation.
 91  
             // test the connection via HEAD
 92  
 //            HeadMethod method = new HeadMethod(endpoint.getEndpointURI().getAddress());
 93  
 //            try
 94  
 //            {
 95  
 //                client.executeMethod(getHostConfig(endpoint.getEndpointURI().getUri()), method);
 96  
 //            }
 97  
 //            catch (Exception e)
 98  
 //            {
 99  
 //                throw new ConnectException(
 100  
 //                    HttpMessages.failedToConnect(endpoint.getEndpointURI().getUri()), e, this);
 101  
 //            }
 102  
         }
 103  
 
 104  0
     }
 105  
 
 106  
     protected void doDisconnect() throws Exception
 107  
     {
 108  0
         client = null;
 109  0
     }
 110  
 
 111  
     protected void doDispatch(UMOEvent event) throws Exception
 112  
     {
 113  0
         HttpMethod httpMethod = getMethod(event);
 114  0
         execute(event, httpMethod, true);
 115  0
         if (httpMethod.getStatusCode() >= 400)
 116  
         {
 117  0
             logger.error(httpMethod.getResponseBodyAsString());
 118  0
             throw new DispatchException(event.getMessage(), event.getEndpoint(), new Exception(
 119  
                 "Http call returned a status of: " + httpMethod.getStatusCode() + " "
 120  
                                 + httpMethod.getStatusText()));
 121  
         }
 122  0
     }
 123  
 
 124  
     /**
 125  
      * Make a specific request to the underlying transport
 126  
      * 
 127  
      * @param timeout the maximum time the operation should block before returning.
 128  
      *            The call should return immediately if there is data available. If
 129  
      *            no data becomes available before the timeout elapses, null will be
 130  
      *            returned
 131  
      * @return the result of the request wrapped in a UMOMessage object. Null will be
 132  
      *         returned if no data was avaialable
 133  
      * @throws Exception if the call to the underlying protocal cuases an exception
 134  
      */
 135  
     protected UMOMessage doReceive(long timeout) throws Exception
 136  
     {
 137  0
         HttpMethod httpMethod = new GetMethod(endpoint.getEndpointURI().getAddress());
 138  0
         httpMethod.setDoAuthentication(true);
 139  0
         if (endpoint.getEndpointURI().getUserInfo() != null
 140  
             && endpoint.getProperty(HttpConstants.HEADER_AUTHORIZATION) == null)
 141  
         {
 142  
             // Add User Creds
 143  0
             StringBuffer header = new StringBuffer(128);
 144  0
             header.append("Basic ");
 145  0
             header.append(new String(Base64.encodeBase64(endpoint.getEndpointURI().getUserInfo().getBytes(
 146  
                 endpoint.getEncoding()))));
 147  0
             httpMethod.addRequestHeader(HttpConstants.HEADER_AUTHORIZATION, header.toString());
 148  
         }
 149  
         try
 150  
         {
 151  0
             HttpClient client = new HttpClient();
 152  0
             client.executeMethod(httpMethod);
 153  
 
 154  0
             if (httpMethod.getStatusCode() == HttpStatus.SC_OK)
 155  
             {
 156  0
                 return (UMOMessage) receiveTransformer.transform(httpMethod);
 157  
             }
 158  
             else
 159  
             {
 160  0
                 throw new ReceiveException(
 161  
                     HttpMessages.requestFailedWithStatus(httpMethod.getStatusLine().toString()),
 162  
                     endpoint, timeout);
 163  
             }
 164  
         }
 165  0
         catch (ReceiveException e)
 166  
         {
 167  0
             throw e;
 168  
         }
 169  0
         catch (Exception e)
 170  
         {
 171  0
             throw new ReceiveException(endpoint, timeout, e);
 172  
         }
 173  
         finally
 174  
         {
 175  0
             httpMethod.releaseConnection();
 176  
         }
 177  
     }
 178  
 
 179  
     protected HttpMethod execute(UMOEvent event, HttpMethod httpMethod, boolean closeConnection)
 180  
         throws Exception
 181  
     {
 182  
         // TODO set connection timeout buffer etc
 183  
         try
 184  
         {
 185  0
             URI uri = event.getEndpoint().getEndpointURI().getUri();
 186  
 
 187  0
             this.processCookies(event);
 188  
 
 189  
             // TODO can we use the return code for better reporting?
 190  0
             client.executeMethod(getHostConfig(uri), httpMethod);
 191  
 
 192  0
             return httpMethod;
 193  
         }
 194  0
         catch (IOException e)
 195  
         {
 196  
             // TODO employ dispatcher reconnection strategy at this point
 197  0
             throw new DispatchException(event.getMessage(), event.getEndpoint(), e);
 198  
         }
 199  0
         catch (Exception e)
 200  
         {
 201  0
             throw new DispatchException(event.getMessage(), event.getEndpoint(), e);
 202  
         }
 203  
         finally
 204  
         {
 205  0
             if (httpMethod != null && closeConnection)
 206  
             {
 207  0
                 httpMethod.releaseConnection();
 208  
             }
 209  
         }
 210  
     }
 211  
 
 212  
     protected void processCookies(UMOEvent event)
 213  
     {
 214  0
         UMOMessage msg = event.getMessage();
 215  0
         Cookie[] cookies = (Cookie[]) msg.removeProperty(HttpConnector.HTTP_COOKIES_PROPERTY);
 216  0
         if (cookies != null && cookies.length > 0)
 217  
         {
 218  0
             String policy = (String) msg.removeProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY);
 219  0
             client.getParams().setCookiePolicy(CookieHelper.getCookiePolicy(policy));
 220  0
             client.getState().addCookies(cookies);
 221  
         }
 222  0
     }
 223  
 
 224  
     protected HttpMethod getMethod(UMOEvent event) throws TransformerException
 225  
     {
 226  0
         UMOMessage msg = event.getMessage();
 227  0
         String method = msg.getStringProperty(HttpConnector.HTTP_METHOD_PROPERTY, HttpConstants.METHOD_POST);
 228  0
         URI uri = event.getEndpoint().getEndpointURI().getUri();
 229  
         HttpMethod httpMethod;
 230  0
         Object body = event.getTransformedMessage();
 231  
 
 232  0
         if (body instanceof HttpMethod)
 233  
         {
 234  0
             httpMethod = (HttpMethod)body;
 235  
         }
 236  0
         else if (HttpConstants.METHOD_GET.equalsIgnoreCase(method))
 237  
         {
 238  0
             httpMethod = new GetMethod(uri.toString());
 239  
         }
 240  
         else
 241  
         {
 242  0
             PostMethod postMethod = new PostMethod(uri.toString());
 243  
 
 244  0
             if (body instanceof String)
 245  
             {
 246  0
                 ObjectToHttpClientMethodRequest trans = new ObjectToHttpClientMethodRequest();
 247  0
                 httpMethod = (HttpMethod)trans.transform(body.toString());
 248  
             }
 249  0
             else if (body instanceof UMOStreamMessageAdapter)
 250  
             {
 251  0
                 UMOStreamMessageAdapter sma = (UMOStreamMessageAdapter)body;
 252  0
                 Map headers = sma.getOutputHandler().getHeaders(event);
 253  0
                 for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();)
 254  
                 {
 255  0
                     Map.Entry entry = (Map.Entry)iterator.next();
 256  0
                     postMethod.addRequestHeader((String)entry.getKey(), (String)entry.getValue());
 257  
                 }
 258  0
                 postMethod.setRequestEntity(new StreamPayloadRequestEntity((StreamMessageAdapter)body, event));
 259  0
                 postMethod.setContentChunked(true);
 260  0
                 httpMethod = postMethod;
 261  
             }
 262  
             else
 263  
             {
 264  0
                 byte[] buffer = event.getTransformedMessageAsBytes();
 265  0
                 postMethod.setRequestEntity(new ByteArrayRequestEntity(buffer, event.getEncoding()));
 266  0
                 httpMethod = postMethod;
 267  
             }
 268  
 
 269  
         }
 270  0
         httpMethod.setDoAuthentication(true);
 271  0
         if (event.getCredentials() != null)
 272  
         {
 273  0
             String authScopeHost = msg.getStringProperty("http.auth.scope.host", null);
 274  0
             int authScopePort = msg.getIntProperty("http.auth.scope.port", -1);
 275  0
             String authScopeRealm = msg.getStringProperty("http.auth.scope.realm", null);
 276  0
             String authScopeScheme = msg.getStringProperty("http.auth.scope.scheme", null);
 277  0
             client.getState().setCredentials(
 278  
                 new AuthScope(authScopeHost, authScopePort, authScopeRealm, authScopeScheme),
 279  
                 new UsernamePasswordCredentials(event.getCredentials().getUsername(), new String(
 280  
                     event.getCredentials().getPassword())));
 281  0
             client.getParams().setAuthenticationPreemptive(true);
 282  
         }
 283  
         else
 284  
         {
 285  
             // don't use preemptive if there are no credentials to send
 286  0
             client.getParams().setAuthenticationPreemptive(false);
 287  
         }
 288  0
         return httpMethod;
 289  
     }
 290  
 
 291  
     /*
 292  
      * (non-Javadoc)
 293  
      * 
 294  
      * @see org.mule.umo.provider.UMOConnector#send(org.mule.umo.UMOEvent)
 295  
      */
 296  
     protected UMOMessage doSend(UMOEvent event) throws Exception
 297  
     {        
 298  0
         HttpMethod httpMethod = getMethod(event);
 299  0
         httpMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new MuleHttpMethodRetryHandler());
 300  
 
 301  0
         httpMethod = execute(event, httpMethod, false);
 302  
         
 303  
         try
 304  
         {
 305  0
             Properties h = new Properties();
 306  0
             Header[] headers = httpMethod.getResponseHeaders();
 307  0
             for (int i = 0; i < headers.length; i++)
 308  
             {
 309  0
                 h.setProperty(headers[i].getName(), headers[i].getValue());
 310  
             }
 311  
 
 312  0
             String status = String.valueOf(httpMethod.getStatusCode());
 313  
 
 314  0
             h.setProperty(HttpConnector.HTTP_STATUS_PROPERTY, status);
 315  0
             if (logger.isDebugEnabled())
 316  
             {
 317  0
                 logger.debug("Http response is: " + status);
 318  
             }
 319  0
             ExceptionPayload ep = null;
 320  0
             if (httpMethod.getStatusCode() >= 400)
 321  
             {
 322  0
                 ep = new ExceptionPayload(new DispatchException(event.getMessage(), event.getEndpoint(),
 323  
                     new Exception("Http call returned a status of: " + httpMethod.getStatusCode() + " "
 324  
                                   + httpMethod.getStatusText())));
 325  
             }
 326  
             UMOMessage m;
 327  
             // text or binary content?
 328  0
             Header header = httpMethod.getResponseHeader(HttpConstants.HEADER_CONTENT_TYPE);
 329  0
             if ((header != null) && event.isStreaming())
 330  
             {
 331  0
                 HttpStreamMessageAdapter sp = (HttpStreamMessageAdapter)connector.getStreamMessageAdapter(
 332  
                     httpMethod.getResponseBodyAsStream(), null);
 333  0
                 sp.setHttpMethod(httpMethod);
 334  0
                 m = new MuleMessage(sp, h);
 335  
             }
 336  
             else
 337  
             {
 338  0
                 Object body = IOUtils.toByteArray(httpMethod.getResponseBodyAsStream());
 339  0
                 if (body == null)
 340  
                 {
 341  0
                     body = StringUtils.EMPTY;
 342  
                 }
 343  0
                 UMOMessageAdapter adapter = connector.getMessageAdapter(new Object[]{body, h});
 344  0
                 m = new MuleMessage(adapter);
 345  
             }
 346  0
             m.setExceptionPayload(ep);
 347  0
             return m;
 348  
         }
 349  0
         catch (Exception e)
 350  
         {
 351  0
             throw new DispatchException(event.getMessage(), event.getEndpoint(), e);
 352  
         }
 353  
         finally
 354  
         {
 355  0
             if (httpMethod != null && !event.isStreaming())
 356  
             {
 357  0
                 httpMethod.releaseConnection();
 358  
             }
 359  
         }
 360  
     }
 361  
 
 362  
     protected HostConfiguration getHostConfig(URI uri) throws URISyntaxException
 363  
     {
 364  0
         Protocol protocol = Protocol.getProtocol(uri.getScheme().toLowerCase());
 365  
 
 366  0
         String host = uri.getHost();
 367  0
         int port = uri.getPort();
 368  0
         HostConfiguration config = new HostConfiguration();
 369  0
         config.setHost(host, port, protocol);
 370  0
         if (StringUtils.isNotBlank(connector.getProxyHostname()))
 371  
         {
 372  
             // add proxy support
 373  0
             config.setProxy(connector.getProxyHostname(), connector.getProxyPort());
 374  
         }
 375  0
         return config;
 376  
     }
 377  
 
 378  
     protected void doDispose()
 379  
     {
 380  
         // template method
 381  0
     }
 382  
 
 383  
     private class StreamPayloadRequestEntity implements RequestEntity
 384  
     {
 385  
         private UMOStreamMessageAdapter messageAdapter;
 386  
         private UMOEvent event;
 387  
 
 388  
         public StreamPayloadRequestEntity(UMOStreamMessageAdapter messageAdapter, UMOEvent event)
 389  0
         {
 390  0
             this.messageAdapter = messageAdapter;
 391  0
             this.event = event;
 392  0
         }
 393  
 
 394  
         public boolean isRepeatable()
 395  
         {
 396  0
             return true;
 397  
         }
 398  
 
 399  
         public void writeRequest(OutputStream outputStream) throws IOException
 400  
         {
 401  0
             messageAdapter.getOutputHandler().write(event, outputStream);
 402  0
         }
 403  
 
 404  
         public long getContentLength()
 405  
         {
 406  0
             return -1L;
 407  
         }
 408  
 
 409  
         public String getContentType()
 410  
         {
 411  0
             return event.getMessage().getStringProperty(HttpConstants.HEADER_CONTENT_TYPE, null);
 412  
         }
 413  
     }
 414  
 
 415  
 }