View Javadoc

1   /*
2    * $Id: ObjectToHttpClientMethodRequest.java 19830 2010-10-05 02:17:07Z dirk.olmes $
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.transformers;
12  
13  import org.mule.RequestContext;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleMessage;
16  import org.mule.api.config.MuleProperties;
17  import org.mule.api.transformer.TransformerException;
18  import org.mule.api.transport.OutputHandler;
19  import org.mule.api.transport.PropertyScope;
20  import org.mule.message.ds.StringDataSource;
21  import org.mule.transformer.AbstractMessageTransformer;
22  import org.mule.transformer.types.DataTypeFactory;
23  import org.mule.transport.NullPayload;
24  import org.mule.transport.http.HttpConnector;
25  import org.mule.transport.http.HttpConstants;
26  import org.mule.transport.http.StreamPayloadRequestEntity;
27  import org.mule.transport.http.i18n.HttpMessages;
28  import org.mule.util.IOUtils;
29  import org.mule.util.ObjectUtils;
30  import org.mule.util.StringUtils;
31  
32  import java.io.InputStream;
33  import java.io.Serializable;
34  import java.io.UnsupportedEncodingException;
35  import java.net.URI;
36  import java.net.URISyntaxException;
37  import java.net.URLEncoder;
38  import java.util.Iterator;
39  import java.util.Map;
40  
41  import javax.activation.DataHandler;
42  import javax.activation.FileDataSource;
43  import javax.activation.URLDataSource;
44  
45  import org.apache.commons.httpclient.HttpMethod;
46  import org.apache.commons.httpclient.HttpVersion;
47  import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
48  import org.apache.commons.httpclient.methods.DeleteMethod;
49  import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
50  import org.apache.commons.httpclient.methods.GetMethod;
51  import org.apache.commons.httpclient.methods.HeadMethod;
52  import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
53  import org.apache.commons.httpclient.methods.OptionsMethod;
54  import org.apache.commons.httpclient.methods.PostMethod;
55  import org.apache.commons.httpclient.methods.PutMethod;
56  import org.apache.commons.httpclient.methods.StringRequestEntity;
57  import org.apache.commons.httpclient.methods.TraceMethod;
58  import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
59  import org.apache.commons.httpclient.methods.multipart.FilePart;
60  import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
61  import org.apache.commons.httpclient.methods.multipart.Part;
62  import org.apache.commons.httpclient.methods.multipart.StringPart;
63  import org.apache.commons.httpclient.params.HttpMethodParams;
64  import org.apache.commons.lang.SerializationUtils;
65  
66  
67  /**
68   * <code>ObjectToHttpClientMethodRequest</code> transforms a MuleMessage into a
69   * HttpClient HttpMethod that represents an HttpRequest.
70   */
71  
72  public class ObjectToHttpClientMethodRequest extends AbstractMessageTransformer
73  {
74      public ObjectToHttpClientMethodRequest()
75      {
76          setReturnDataType(DataTypeFactory.create(HttpMethod.class));
77          registerSourceType(DataTypeFactory.MULE_MESSAGE);
78          registerSourceType(DataTypeFactory.BYTE_ARRAY);
79          registerSourceType(DataTypeFactory.STRING);
80          registerSourceType(DataTypeFactory.INPUT_STREAM);
81          registerSourceType(DataTypeFactory.create(OutputHandler.class));
82          registerSourceType(DataTypeFactory.create(NullPayload.class));
83      }
84  
85      @Override
86      public Object transformMessage(MuleMessage msg, String outputEncoding) throws TransformerException
87      {
88          String method = detectHttpMethod(msg);
89          try
90          {
91              // TODO It makes testing much harder if we use the endpoint on the
92              // transformer since we need to create correct message types and
93              // endpoints
94              // URI uri = getEndpoint().getEndpointURI().getUri();
95              final URI uri = getURI(msg);
96              HttpMethod httpMethod;
97  
98              if (HttpConstants.METHOD_GET.equals(method))
99              {
100                 httpMethod = createGetMethod(msg, outputEncoding);
101             }
102             else if (HttpConstants.METHOD_POST.equalsIgnoreCase(method))
103             {
104                 httpMethod = createPostMethod(msg, outputEncoding);
105             }
106             else if (HttpConstants.METHOD_PUT.equalsIgnoreCase(method))
107             {
108                 httpMethod = createPutMethod(msg, outputEncoding);
109             }
110             else if (HttpConstants.METHOD_DELETE.equalsIgnoreCase(method))
111             {
112                 httpMethod = new DeleteMethod(uri.toString());
113             }
114             else if (HttpConstants.METHOD_HEAD.equalsIgnoreCase(method))
115             {
116                 httpMethod = new HeadMethod(uri.toString());
117             }
118             else if (HttpConstants.METHOD_OPTIONS.equalsIgnoreCase(method))
119             {
120                 httpMethod = new OptionsMethod(uri.toString());
121             }
122             else if (HttpConstants.METHOD_TRACE.equalsIgnoreCase(method))
123             {
124                 httpMethod = new TraceMethod(uri.toString());
125             }
126             else
127             {
128                 throw new TransformerException(HttpMessages.unsupportedMethod(method));
129             }
130 
131             // Allow the user to set HttpMethodParams as an object on the message
132             final HttpMethodParams params = (HttpMethodParams) msg.removeProperty(
133                 HttpConnector.HTTP_PARAMS_PROPERTY, PropertyScope.OUTBOUND);
134             if (params != null)
135             {
136                 httpMethod.setParams(params);
137             }
138             else
139             {
140                 // TODO we should probably set other properties here
141                 final String httpVersion = msg.getOutboundProperty(HttpConnector.HTTP_VERSION_PROPERTY,
142                     HttpConstants.HTTP11);
143                 if (HttpConstants.HTTP10.equals(httpVersion))
144                 {
145                     httpMethod.getParams().setVersion(HttpVersion.HTTP_1_0);
146                 }
147                 else
148                 {
149                     httpMethod.getParams().setVersion(HttpVersion.HTTP_1_1);
150                 }
151             }
152 
153             setHeaders(httpMethod, msg);
154 
155             return httpMethod;
156         }
157         catch (final Exception e)
158         {
159             throw new TransformerException(this, e);
160         }
161     }
162 
163     protected String detectHttpMethod(MuleMessage msg)
164     {
165         String method = msg.getOutboundProperty(HttpConnector.HTTP_METHOD_PROPERTY, null);
166         if (method == null)
167         {
168             method = msg.getInvocationProperty(HttpConnector.HTTP_METHOD_PROPERTY, HttpConstants.METHOD_POST);
169         }
170         return method;
171     }
172 
173     protected HttpMethod createGetMethod(MuleMessage msg, String outputEncoding) throws Exception
174     {
175         final Object src = msg.getPayload();
176         // TODO It makes testing much harder if we use the endpoint on the
177         // transformer since we need to create correct message types and endpoints
178         // URI uri = getEndpoint().getEndpointURI().getUri();
179         final URI uri = getURI(msg);
180         HttpMethod httpMethod;
181         String query = uri.getRawQuery();
182 
183         httpMethod = new GetMethod(uri.toString());
184         String paramName = msg.getOutboundProperty(HttpConnector.HTTP_GET_BODY_PARAM_PROPERTY, null);
185         if (paramName != null)
186         {
187             paramName = URLEncoder.encode(paramName, outputEncoding);
188 
189             String paramValue;
190             Boolean encode = msg.getInvocationProperty(HttpConnector.HTTP_ENCODE_PARAMVALUE);
191             if (encode == null)
192             {
193                 encode = msg.getOutboundProperty(HttpConnector.HTTP_ENCODE_PARAMVALUE, true);
194             }
195 
196             if (encode)
197             {
198                 paramValue = URLEncoder.encode(src.toString(), outputEncoding);
199             }
200             else
201             {
202                 paramValue = src.toString();
203             }
204 
205             if (!(src instanceof NullPayload) && !StringUtils.EMPTY.equals(src))
206             {
207                 if (query == null)
208                 {
209                     query = paramName + "=" + paramValue;
210                 }
211                 else
212                 {
213                     query += "&" + paramName + "=" + paramValue;
214                 }
215             }
216         }
217 
218         httpMethod.setQueryString(query);
219         return httpMethod;
220     }
221 
222     protected HttpMethod createPostMethod(MuleMessage msg, String outputEncoding) throws Exception
223     {
224         final Object src = msg.getPayload();
225         // TODO It makes testing much harder if we use the endpoint on the
226         // transformer since we need to create correct message types and endpoints
227         // URI uri = getEndpoint().getEndpointURI().getUri();
228         final URI uri = getURI(msg);
229         final PostMethod postMethod = new PostMethod(uri.toString());
230         String paramName = msg.getOutboundProperty(HttpConnector.HTTP_POST_BODY_PARAM_PROPERTY, null);
231         if (paramName == null)
232         {
233             paramName = msg.getInvocationProperty(HttpConnector.HTTP_POST_BODY_PARAM_PROPERTY);
234         }
235 
236         if (src instanceof Map)
237         {
238             for (final Iterator iterator = ((Map) src).entrySet().iterator(); iterator.hasNext();)
239             {
240                 final Map.Entry entry = (Map.Entry) iterator.next();
241                 postMethod.addParameter(entry.getKey().toString(), entry.getValue().toString());
242             }
243         }
244         else if (paramName != null)
245         {
246             postMethod.addParameter(paramName, src.toString());
247 
248         }
249         else
250         {
251             setupEntityMethod(src, outputEncoding, msg, postMethod);
252         }
253 
254         return postMethod;
255     }
256 
257     protected HttpMethod createPutMethod(MuleMessage msg, String outputEncoding) throws Exception
258     {
259         URI uri = getURI(msg);
260         PutMethod putMethod = new PutMethod(uri.toString());
261 
262         Object payload = msg.getPayload();
263         setupEntityMethod(payload, outputEncoding, msg, putMethod);
264         
265         return putMethod;
266     }
267 
268     protected URI getURI(MuleMessage message) throws URISyntaxException, TransformerException
269     {
270         final String endpoint = message.getOutboundProperty(MuleProperties.MULE_ENDPOINT_PROPERTY, null);
271         if (endpoint == null)
272         {
273             throw new TransformerException(
274                 HttpMessages.eventPropertyNotSetCannotProcessRequest(MuleProperties.MULE_ENDPOINT_PROPERTY),
275                 this);
276         }
277         return new URI(endpoint);
278     }
279 
280     protected void setupEntityMethod(Object src,
281                                      String encoding,
282                                      MuleMessage msg,
283                                      EntityEnclosingMethod postMethod)
284         throws UnsupportedEncodingException, TransformerException
285     {
286         // Dont set a POST payload if the body is a Null Payload.
287         // This way client calls can control if a POST body is posted explicitly
288         if (!(msg.getPayload() instanceof NullPayload))
289         {
290             String mimeType = (String) msg.getProperty(HttpConstants.HEADER_CONTENT_TYPE,
291                 PropertyScope.OUTBOUND);
292             if (mimeType == null)
293             {
294                 mimeType = (getEndpoint() != null ? getEndpoint().getMimeType() : null);
295             }
296             if (mimeType == null)
297             {
298                 mimeType = HttpConstants.DEFAULT_CONTENT_TYPE;
299                 logger.info("Content-Type not set on outgoing request, defaulting to: " + mimeType);
300             }
301 
302             if (encoding != null && !"UTF-8".equals(encoding.toUpperCase())
303                 && mimeType.indexOf("charset") == -1)
304             {
305                 mimeType += "; charset=" + encoding;
306             }
307 
308             // Ensure that we have a cached representation of the message if we're
309             // using HTTP 1.0
310             final String httpVersion = msg.getOutboundProperty(HttpConnector.HTTP_VERSION_PROPERTY,
311                 HttpConstants.HTTP11);
312             if (HttpConstants.HTTP10.equals(httpVersion))
313             {
314                 try
315                 {
316                     src = msg.getPayloadAsBytes();
317                 }
318                 catch (final Exception e)
319                 {
320                     throw new TransformerException(this, e);
321                 }
322             }
323 
324             if (msg.getOutboundAttachmentNames() != null && msg.getOutboundAttachmentNames().size() > 0)
325             {
326                 try
327                 {
328                     postMethod.setRequestEntity(createMultiPart(msg, postMethod));
329                     return;
330                 }
331                 catch (final Exception e)
332                 {
333                     throw new TransformerException(this, e);
334                 }
335             }
336             if (src instanceof String)
337             {
338 
339                 postMethod.setRequestEntity(new StringRequestEntity(src.toString(), mimeType, encoding));
340                 return;
341             }
342 
343             if (src instanceof InputStream)
344             {
345                 postMethod.setRequestEntity(new InputStreamRequestEntity((InputStream) src, mimeType));
346             }
347             else if (src instanceof byte[])
348             {
349                 postMethod.setRequestEntity(new ByteArrayRequestEntity((byte[]) src, mimeType));
350             }
351             else if (src instanceof OutputHandler)
352             {
353                 final MuleEvent event = RequestContext.getEvent();
354                 postMethod.setRequestEntity(new StreamPayloadRequestEntity((OutputHandler) src, event));
355             }
356             else
357             {
358                 final byte[] buffer = SerializationUtils.serialize((Serializable) src);
359                 postMethod.setRequestEntity(new ByteArrayRequestEntity(buffer, mimeType));
360             }
361         }
362         else if (msg.getOutboundAttachmentNames() != null && msg.getOutboundAttachmentNames().size() > 0)
363         {
364             try
365             {
366                 postMethod.setRequestEntity(createMultiPart(msg, postMethod));
367             }
368             catch (final Exception e)
369             {
370                 throw new TransformerException(this, e);
371             }
372         }
373     }
374 
375     protected void setHeaders(HttpMethod httpMethod, MuleMessage msg) throws TransformerException
376     {
377         for (String headerName : msg.getOutboundPropertyNames())
378         {
379             String headerValue = ObjectUtils.getString(msg.getOutboundProperty(headerName), null);
380 
381             if (headerName.startsWith(MuleProperties.PROPERTY_PREFIX))
382             {
383                 // Define Mule headers a custom headers
384                 headerName = new StringBuffer(30).append("X-").append(headerName).toString();
385                 httpMethod.addRequestHeader(headerName, headerValue);
386 
387             }
388             else if (!HttpConstants.RESPONSE_HEADER_NAMES.containsKey(headerName)
389                      && !HttpConnector.HTTP_INBOUND_PROPERTIES.contains(headerName)
390                      && !HttpConnector.HTTP_COOKIES_PROPERTY.equals(headerName))
391             {
392 
393                 httpMethod.addRequestHeader(headerName, headerValue);
394             }
395         }
396     }
397 
398     protected MultipartRequestEntity createMultiPart(MuleMessage msg, EntityEnclosingMethod method)
399         throws Exception
400     {
401         Part[] parts;
402         int i = 0;
403         if (msg.getPayload() instanceof NullPayload)
404         {
405             parts = new Part[msg.getOutboundAttachmentNames().size()];
406         }
407         else
408         {
409             parts = new Part[msg.getOutboundAttachmentNames().size() + 1];
410             parts[i++] = new FilePart("payload", new ByteArrayPartSource("payload", msg.getPayloadAsBytes()));
411         }
412 
413         for (final Iterator<String> iterator = msg.getOutboundAttachmentNames().iterator(); iterator.hasNext(); i++)
414         {
415             final String name = iterator.next();
416             String fileName = name;
417             final DataHandler dh = msg.getOutboundAttachment(name);
418             if (dh.getDataSource() instanceof StringDataSource)
419             {
420                 final StringDataSource ds = (StringDataSource) dh.getDataSource();
421                 parts[i] = new StringPart(ds.getName(), IOUtils.toString(ds.getInputStream()));
422             }
423             else
424             {
425                 if (dh.getDataSource() instanceof FileDataSource)
426                 {
427                     fileName = ((FileDataSource) dh.getDataSource()).getFile().getName();
428                 }
429                 else if (dh.getDataSource() instanceof URLDataSource)
430                 {
431                     fileName = ((URLDataSource) dh.getDataSource()).getURL().getFile();
432                     // Don't use the whole file path, just the file name
433                     final int x = fileName.lastIndexOf("/");
434                     if (x > -1)
435                     {
436                         fileName = fileName.substring(x + 1);
437                     }
438                 }
439                 parts[i] = new FilePart(dh.getName(), new ByteArrayPartSource(fileName,
440                     IOUtils.toByteArray(dh.getInputStream())), dh.getContentType(), null);
441             }
442         }
443 
444         return new MultipartRequestEntity(parts, method.getParams());
445     }
446 }