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.components;
8   
9   import org.mule.DefaultMuleEventContext;
10  import org.mule.DefaultMuleMessage;
11  import org.mule.MessageExchangePattern;
12  import org.mule.api.MuleEvent;
13  import org.mule.api.MuleEventContext;
14  import org.mule.api.MuleMessage;
15  import org.mule.api.endpoint.EndpointBuilder;
16  import org.mule.api.endpoint.OutboundEndpoint;
17  import org.mule.api.expression.ExpressionEvaluator;
18  import org.mule.api.expression.RequiredValueException;
19  import org.mule.api.lifecycle.InitialisationException;
20  import org.mule.api.routing.filter.Filter;
21  import org.mule.component.AbstractComponent;
22  import org.mule.config.i18n.CoreMessages;
23  import org.mule.endpoint.EndpointURIEndpointBuilder;
24  import org.mule.routing.filters.ExpressionFilter;
25  import org.mule.transport.NullPayload;
26  import org.mule.transport.http.HttpConnector;
27  import org.mule.transport.http.HttpConstants;
28  import org.mule.util.StringUtils;
29  
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.util.HashMap;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  
40  /**
41   * This service can used to proxy REST style services as local Mule Components. It
42   * can be configured with a service URL plus a number of properties that allow you to
43   * configure the parameters and error conditions on the service.
44   */
45  public class RestServiceWrapper extends AbstractComponent
46  {
47      public static final String DELETE = HttpConstants.METHOD_DELETE;
48      public static final String GET = HttpConstants.METHOD_GET;
49      public static final String CONTENT_TYPE_VALUE = "application/x-www-form-urlencoded";
50      public static final String HTTP_METHOD = "http.method";
51  
52      /**
53       * logger used by this class
54       */
55      protected transient Log logger = LogFactory.getLog(getClass());
56  
57      private String serviceUrl;
58      private Map requiredParams = new HashMap();
59      private Map optionalParams = new HashMap();
60      private String httpMethod = GET;
61      private List payloadParameterNames;
62      private Filter errorFilter;
63  
64      public String getServiceUrl()
65      {
66          return serviceUrl;
67      }
68  
69      public void setServiceUrl(String serviceUrl)
70      {
71          this.serviceUrl = serviceUrl;
72      }
73  
74      public Map getRequiredParams()
75      {
76          return requiredParams;
77      }
78  
79      /**
80       * Required params that are pulled from the message. If these params don't exist
81       * the call will fail Note that you can use
82       * {@link org.mule.api.expression.ExpressionEvaluator} expressions such as
83       * xpath, header, xquery, etc
84       *
85       * @param requiredParams
86       */
87      public void setRequiredParams(Map requiredParams)
88      {
89          this.requiredParams = requiredParams;
90      }
91  
92      /**
93       * Optional params that are pulled from the message. If these params don't exist
94       * execution will continue. Note that you can use {@link ExpressionEvaluator}
95       * expressions such as xpath, header, xquery, etc
96       */
97      public Map getOptionalParams()
98      {
99          return optionalParams;
100     }
101 
102     public void setOptionalParams(Map optionalParams)
103     {
104         this.optionalParams = optionalParams;
105     }
106 
107     public String getHttpMethod()
108     {
109         return httpMethod;
110     }
111 
112     public void setHttpMethod(String httpMethod)
113     {
114         this.httpMethod = httpMethod;
115     }
116 
117     public List getPayloadParameterNames()
118     {
119         return payloadParameterNames;
120     }
121 
122     public void setPayloadParameterNames(List payloadParameterNames)
123     {
124         this.payloadParameterNames = payloadParameterNames;
125     }
126 
127     public Filter getFilter()
128     {
129         return errorFilter;
130     }
131 
132     public void setFilter(Filter errorFilter)
133     {
134         this.errorFilter = errorFilter;
135     }
136 
137     @Override
138     protected void doInitialise() throws InitialisationException
139     {
140         if (serviceUrl == null)
141         {
142             throw new InitialisationException(CoreMessages.objectIsNull("serviceUrl"), this);
143         }
144         else if (!muleContext.getExpressionManager().isExpression(serviceUrl))
145         {
146             try
147             {
148                 new URL(serviceUrl);
149             }
150             catch (MalformedURLException e)
151             {
152                 throw new InitialisationException(e, this);
153             }
154         }
155 
156         if (errorFilter == null)
157         {
158             // We'll set a default filter that checks the return code
159             errorFilter = new ExpressionFilter("#[header:INBOUND:http.status!=200]");
160             logger.info("Setting default error filter to ExpressionFilter('#[header:INBOUND:http.status!=200]')");
161         }
162     }
163 
164     @Override
165     public Object doInvoke(MuleEvent event) throws Exception
166     {
167         Object requestBody;
168 
169         Object request = event.getMessage().getPayload();
170         String tempUrl = serviceUrl;
171         MuleMessage result;
172         if (muleContext.getExpressionManager().isExpression(serviceUrl))
173         {
174             muleContext.getExpressionManager().validateExpression(serviceUrl);
175             tempUrl = muleContext.getExpressionManager().parse(serviceUrl, event.getMessage(), true);
176         }
177 
178         StringBuffer urlBuffer = new StringBuffer(tempUrl);
179 
180         if (GET.equalsIgnoreCase(this.httpMethod) || DELETE.equalsIgnoreCase(this.httpMethod))
181         {
182             requestBody = NullPayload.getInstance();
183 
184             setRESTParams(urlBuffer, event.getMessage(), request, requiredParams, false, null);
185             setRESTParams(urlBuffer, event.getMessage(), request, optionalParams, true, null);
186         }
187         // if post
188         else
189         {
190             if (event.getMessage().getOutboundProperty(HttpConstants.HEADER_CONTENT_TYPE) == null)
191             {
192                 event.getMessage().setOutboundProperty(HttpConstants.HEADER_CONTENT_TYPE, CONTENT_TYPE_VALUE);
193             }
194 
195             StringBuffer requestBodyBuffer = new StringBuffer();
196             setRESTParams(urlBuffer, event.getMessage(), request, requiredParams, false, requestBodyBuffer);
197             setRESTParams(urlBuffer, event.getMessage(), request, optionalParams, true, requestBodyBuffer);
198             requestBody = requestBodyBuffer.toString();
199         }
200 
201         tempUrl = urlBuffer.toString();
202         logger.info("Invoking REST service: " + tempUrl);
203 
204         event.getMessage().setOutboundProperty(HttpConnector.HTTP_METHOD_PROPERTY, httpMethod);
205 
206         EndpointBuilder endpointBuilder = new EndpointURIEndpointBuilder(tempUrl, muleContext);
207         endpointBuilder.setExchangePattern(MessageExchangePattern.REQUEST_RESPONSE);
208         OutboundEndpoint outboundEndpoint = endpointBuilder.buildOutboundEndpoint();
209 
210         MuleEventContext eventContext = new DefaultMuleEventContext(event);
211         result = eventContext.sendEvent(
212             new DefaultMuleMessage(requestBody, event.getMessage(), muleContext), outboundEndpoint);
213         if (isErrorPayload(result))
214         {
215             handleException(new RestServiceException(CoreMessages.failedToInvokeRestService(tempUrl),
216                 event), result);
217         }
218 
219         return result;
220     }
221 
222     private String getSeparator(String url)
223     {
224         String sep;
225 
226         if (url.indexOf("?") > -1)
227         {
228             sep = "&";
229         }
230         else
231         {
232             sep = "?";
233         }
234 
235         return sep;
236     }
237 
238     private String updateSeparator(String sep)
239     {
240         if (sep.compareTo("?") == 0 || sep.compareTo("") == 0)
241         {
242             return ("&");
243         }
244 
245         return sep;
246     }
247 
248     // if requestBodyBuffer is null, it means that the request is a GET, otherwise it
249     // is a POST and
250     // requestBodyBuffer must contain the body of the http method at the end of this
251     // function call
252     private void setRESTParams(StringBuffer url,
253                                MuleMessage msg,
254                                Object body,
255                                Map args,
256                                boolean optional,
257                                StringBuffer requestBodyBuffer)
258     {
259         String sep;
260 
261         if (requestBodyBuffer == null)
262         {
263             sep = getSeparator(url.toString());
264         }
265         else if(requestBodyBuffer.length() > 0)
266         {
267             sep = "&";
268         }
269         else
270         {
271             sep = StringUtils.EMPTY;
272         }
273 
274         for (Iterator iterator = args.entrySet().iterator(); iterator.hasNext();)
275         {
276             Map.Entry entry = (Map.Entry) iterator.next();
277             String name = (String) entry.getKey();
278             String exp = (String) entry.getValue();
279             Object value = null;
280 
281             if (muleContext.getExpressionManager().isExpression(exp))
282             {
283                 muleContext.getExpressionManager().validateExpression(exp);
284                 try
285                 {
286                     value = muleContext.getExpressionManager().evaluate(exp, msg);
287                 }
288                 catch (RequiredValueException e)
289                 {
290                     //ignore
291                 }
292             }
293             else
294             {
295                 value = exp;
296             }
297 
298             if (value == null)
299             {
300                 if (!optional)
301                 {
302                     throw new IllegalArgumentException(CoreMessages.propertyIsNotSetOnEvent(exp).toString());
303                 }
304             }
305             else if (requestBodyBuffer != null) // implies this is a POST
306             {
307                 requestBodyBuffer.append(sep);
308                 requestBodyBuffer.append(name).append('=').append(value);
309             }
310             else
311             {
312                 url.append(sep);
313                 url.append(name).append('=').append(value);
314             }
315 
316             sep = updateSeparator(sep);
317         }
318 
319         if (!optional && payloadParameterNames != null)
320         {
321             if (body instanceof Object[])
322             {
323                 Object[] requestArray = (Object[]) body;
324                 for (int i = 0; i < payloadParameterNames.size(); i++)
325                 {
326                     if (requestBodyBuffer != null)
327                     {
328                         requestBodyBuffer.append(sep).append(payloadParameterNames.get(i)).append('=').append(
329                                 requestArray[i].toString());
330                     }
331                     else
332                     {
333                         url.append(sep).append(payloadParameterNames.get(i)).append('=').append(
334                                 requestArray[i].toString());
335                     }
336 
337                     sep = updateSeparator(sep);
338                 }
339             }
340             else
341             {
342                 if (payloadParameterNames.get(0) != null)
343                 {
344                     if (requestBodyBuffer != null)
345                     {
346                         requestBodyBuffer.append(payloadParameterNames.get(0)).append('=').append(body.toString());
347                     }
348                     else
349                     {
350                         url.append(sep).append(payloadParameterNames.get(0)).append('=').append(body.toString());
351                     }
352                 }
353             }
354         }
355     }
356 
357     protected boolean isErrorPayload(MuleMessage message)
358     {
359         return errorFilter != null && errorFilter.accept(message);
360     }
361 
362     protected void handleException(RestServiceException e, MuleMessage result) throws Exception
363     {
364         throw e;
365     }
366 
367 }