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