View Javadoc

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