View Javadoc

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