View Javadoc

1   /*
2    * $Id: RestServiceWrapper.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.components.rest;
12  
13  import org.mule.config.i18n.CoreMessages;
14  import org.mule.impl.MuleMessage;
15  import org.mule.impl.endpoint.MuleEndpointURI;
16  import org.mule.providers.NullPayload;
17  import org.mule.routing.filters.MessagePropertyFilter;
18  import org.mule.routing.filters.RegExFilter;
19  import org.mule.umo.UMOEventContext;
20  import org.mule.umo.UMOFilter;
21  import org.mule.umo.UMOMessage;
22  import org.mule.umo.endpoint.UMOEndpointURI;
23  import org.mule.umo.lifecycle.Callable;
24  import org.mule.umo.lifecycle.Initialisable;
25  import org.mule.umo.lifecycle.InitialisationException;
26  import org.mule.util.properties.MessagePropertyExtractor;
27  import org.mule.util.properties.PropertyExtractor;
28  
29  import java.net.MalformedURLException;
30  import java.net.URL;
31  import java.util.HashMap;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Map;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  /**
40   * This component can used to proxy REST style services as local Mule Components. It
41   * can be configured with a service URL plus a number of properties that allow you to
42   * configure the parameters and error conditions on the service.
43   */
44  public class RestServiceWrapper implements Callable, Initialisable
45  {
46      public static final String REST_SERVICE_URL = "rest.service.url";
47      public static final String GET = "GET";
48      public static final String CONTENT_TYPE = "Content-Type";
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 boolean urlFromMessage = false;
59      private Map requiredParams = new HashMap();
60      private Map optionalParams = new HashMap();
61      private String httpMethod = "GET";
62      private List payloadParameterNames;
63      private UMOFilter errorFilter;
64      private String errorExpression;
65  
66      private PropertyExtractor propertyExtractor = new MessagePropertyExtractor();
67  
68      public String getServiceUrl()
69      {
70          return serviceUrl;
71      }
72  
73      public void setServiceUrl(String serviceUrl)
74      {
75          this.serviceUrl = serviceUrl;
76      }
77  
78      public boolean isUrlFromMessage()
79      {
80          return urlFromMessage;
81      }
82  
83      public void setUrlFromMessage(boolean urlFromMessage)
84      {
85          this.urlFromMessage = urlFromMessage;
86      }
87  
88      public Map getRequiredParams()
89      {
90          return requiredParams;
91      }
92  
93      public void setRequiredParams(Map requiredParams)
94      {
95          this.requiredParams = requiredParams;
96      }
97  
98      public Map getOptionalParams()
99      {
100         return optionalParams;
101     }
102 
103     public void setOptionalParams(Map optionalParams)
104     {
105         this.optionalParams = optionalParams;
106     }
107 
108     public String getHttpMethod()
109     {
110         return httpMethod;
111     }
112 
113     public void setHttpMethod(String httpMethod)
114     {
115         this.httpMethod = httpMethod;
116     }
117 
118     public List getPayloadParameterNames()
119     {
120         return payloadParameterNames;
121     }
122 
123     public void setPayloadParameterNames(List payloadParameterNames)
124     {
125         this.payloadParameterNames = payloadParameterNames;
126     }
127 
128     public UMOFilter getErrorFilter()
129     {
130         return errorFilter;
131     }
132 
133     public void setErrorFilter(UMOFilter errorFilter)
134     {
135         this.errorFilter = errorFilter;
136     }
137 
138     public String getErrorExpression()
139     {
140         return errorExpression;
141     }
142 
143     public void setErrorExpression(String errorExpression)
144     {
145         this.errorExpression = errorExpression;
146     }
147 
148     public void initialise() throws InitialisationException
149     {
150         if (serviceUrl == null && !urlFromMessage)
151         {
152             throw new InitialisationException(CoreMessages.objectIsNull("serviceUrl"), this);
153         }
154         else if (serviceUrl != null)
155         {
156             try
157             {
158                 new URL(serviceUrl);
159             }
160             catch (MalformedURLException e)
161             {
162                 throw new InitialisationException(e, this);
163             }
164         }
165 
166         if (errorFilter == null)
167         {
168             if (errorExpression == null)
169             {
170                 // We'll set a default filter that checks the return code
171                 errorFilter = new MessagePropertyFilter("http.status!=200");
172                 logger.info("Setting default error filter to MessagePropertyFilter('http.status!=200')");
173             }
174             else
175             {
176                 errorFilter = new RegExFilter(errorExpression);
177             }
178         }
179     }
180 
181     public Object onCall(UMOEventContext eventContext) throws Exception
182     {
183         String tempUrl;
184         Object request = eventContext.getTransformedMessage();
185         Object requestBody;
186         if (urlFromMessage)
187         {
188             tempUrl = eventContext.getMessage().getStringProperty(REST_SERVICE_URL, null);
189             if (tempUrl == null)
190             {
191                 throw new IllegalArgumentException(
192                     CoreMessages.propertyIsNotSetOnEvent(REST_SERVICE_URL).toString());
193             }
194         }
195         else
196         {
197             tempUrl = serviceUrl;
198         }
199         StringBuffer urlBuffer = new StringBuffer(tempUrl);
200         
201         if (this.httpMethod.compareToIgnoreCase(GET) == 0)
202         {
203             requestBody = NullPayload.getInstance();
204             
205             setRESTParams(urlBuffer, eventContext.getMessage(), request, requiredParams, false, null);
206             setRESTParams(urlBuffer, eventContext.getMessage(), request, optionalParams, true, null);
207         }
208         else //if post
209         {
210             StringBuffer requestBodyBuffer = new StringBuffer();
211             eventContext.getMessage().setProperty(CONTENT_TYPE, CONTENT_TYPE_VALUE);
212             
213             setRESTParams(urlBuffer, eventContext.getMessage(), request, requiredParams, false, requestBodyBuffer);
214             setRESTParams(urlBuffer, eventContext.getMessage(), request, optionalParams, true, requestBodyBuffer);
215             
216             requestBody = requestBodyBuffer.toString();
217         }
218 
219         tempUrl = urlBuffer.toString();
220         logger.info("Invoking REST service: " + tempUrl);
221 
222         UMOEndpointURI endpointURI = new MuleEndpointURI(tempUrl);
223         eventContext.getMessage().setProperty(HTTP_METHOD, httpMethod);
224         
225 
226         UMOMessage result = eventContext.sendEvent(new MuleMessage(requestBody, eventContext.getMessage()),
227             endpointURI);
228 
229         if (isErrorPayload(result))
230         {
231             handleException(new RestServiceException(
232                 CoreMessages.failedToInvokeRestService(tempUrl), result), result);
233         }
234         return result;
235     }
236     
237     private String getSeparator(String url)
238     {
239         String sep;
240 
241         if (url.indexOf("?") > -1)
242         {
243             sep = "&";
244         }
245         else
246         {
247             sep = "?";
248         }
249         
250         return sep;
251     }
252     
253     private String updateSeparator(String sep)
254     {
255         if (sep.compareTo("?") == 0 || sep.compareTo("") == 0)
256         {
257             return ("&");
258         }
259         
260         return sep;
261     }
262 
263     //if requestBodyBuffer is null, it means that the request is a GET, otherwise it is a POST and  
264     //requestBodyBuffer must contain the body of the http method at the end of this function call
265     private void setRESTParams(StringBuffer url, UMOMessage msg, Object body, Map args, boolean optional, StringBuffer requestBodyBuffer)
266     {
267         String sep;
268         
269         if (requestBodyBuffer == null)
270         {
271             sep = getSeparator(url.toString());
272         }
273         else
274         {
275             sep = "";
276         }
277 
278         for (Iterator iterator = args.entrySet().iterator(); iterator.hasNext();)
279         {
280             Map.Entry entry = (Map.Entry) iterator.next();
281             String name = (String) entry.getKey();
282             String exp = (String) entry.getValue();
283             Object value = propertyExtractor.getProperty(exp, msg);
284 
285             if (value == null)
286             {
287                 if (!optional)
288                 {
289                     throw new IllegalArgumentException(CoreMessages.propertyIsNotSetOnEvent(exp).toString());
290                 }
291             }
292             else if (requestBodyBuffer != null) //implies this is a POST
293             {
294                 requestBodyBuffer.append(sep);
295                 requestBodyBuffer.append(name).append('=').append(value);
296             }
297             else
298             {
299                 url.append(sep);
300                 url.append(name).append('=').append(value);
301             }
302             
303             sep = updateSeparator(sep);
304         }
305 
306         if (!optional && payloadParameterNames != null)
307         {
308             if (body instanceof Object[])
309             {
310                 Object[] requestArray = (Object[])body;
311                 for(int i=0; i<payloadParameterNames.size(); i++)
312                 {
313                     if (requestBodyBuffer != null)
314                     {
315                         requestBodyBuffer.append(sep).append(payloadParameterNames.get(i)).append('=').append(requestArray[i].toString());
316                     }
317                     else
318                     {
319                         url.append(sep).append(payloadParameterNames.get(i)).append('=').append(requestArray[i].toString());
320                     }
321                     
322                     sep = updateSeparator(sep);
323                 }
324             }
325             else
326             {
327                 if (payloadParameterNames.get(0) != null)
328                 {
329                     if (requestBodyBuffer != null)
330                     {
331                         requestBodyBuffer.append(payloadParameterNames.get(0)).append('=').append(body.toString());
332                     }
333                     else
334                     {
335                         url.append(sep).append(payloadParameterNames.get(0)).append('=').append(body.toString());
336                     }
337                 } 
338             }
339         }
340     }
341 
342     protected boolean isErrorPayload(UMOMessage message)
343     {
344         if (errorFilter != null)
345         {
346             return errorFilter.accept(message);
347         }
348         return false;
349     }
350 
351     protected void handleException(RestServiceException e, UMOMessage result) throws Exception
352     {
353         throw e;
354     }
355 }