View Javadoc

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