001: /*
002: * $Id: RestServiceWrapper.java 11414 2008-03-18 03:16:19Z dfeist $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.transport.http.components;
012:
013: import org.mule.DefaultMuleMessage;
014: import org.mule.RequestContext;
015: import org.mule.api.MuleEvent;
016: import org.mule.api.MuleException;
017: import org.mule.api.MuleMessage;
018: import org.mule.api.lifecycle.InitialisationException;
019: import org.mule.api.routing.filter.Filter;
020: import org.mule.component.AbstractComponent;
021: import org.mule.config.i18n.CoreMessages;
022: import org.mule.routing.filters.MessagePropertyFilter;
023: import org.mule.routing.filters.RegExFilter;
024: import org.mule.transport.NullPayload;
025: import org.mule.util.expression.ExpressionEvaluator;
026: import org.mule.util.expression.ExpressionEvaluatorManager;
027:
028: import java.net.MalformedURLException;
029: import java.net.URL;
030: import java.util.HashMap;
031: import java.util.Iterator;
032: import java.util.List;
033: import java.util.Map;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037:
038: /**
039: * This service can used to proxy REST style services as local Mule Components. It
040: * can be configured with a service URL plus a number of properties that allow you to
041: * configure the parameters and error conditions on the service.
042: */
043: public class RestServiceWrapper extends AbstractComponent {
044:
045: public static final String REST_SERVICE_URL = "rest.service.url";
046: public static final String GET = "GET";
047: public static final String CONTENT_TYPE = "Content-Type";
048: public static final String CONTENT_TYPE_VALUE = "application/x-www-form-urlencoded";
049: public static final String HTTP_METHOD = "http.method";
050:
051: /**
052: * logger used by this class
053: */
054: protected transient Log logger = LogFactory.getLog(getClass());
055:
056: private String serviceUrl;
057: private boolean urlFromMessage = false;
058: private Map requiredParams = new HashMap();
059: private Map optionalParams = new HashMap();
060: private String httpMethod = "GET";
061: private List payloadParameterNames;
062: private Filter errorFilter;
063: private String errorExpression;
064:
065: public String getServiceUrl() {
066: return serviceUrl;
067: }
068:
069: public void setServiceUrl(String serviceUrl) {
070: this .serviceUrl = serviceUrl;
071: }
072:
073: public boolean isUrlFromMessage() {
074: return urlFromMessage;
075: }
076:
077: public void setUrlFromMessage(boolean urlFromMessage) {
078: this .urlFromMessage = urlFromMessage;
079: }
080:
081: public Map getRequiredParams() {
082: return requiredParams;
083: }
084:
085: /**
086: * Required params that are pulled from the message. If these params don't exist
087: * the call will fail Note that you can use
088: * {@link org.mule.util.expression.ExpressionEvaluator} expressions such as
089: * xpath, header, xquery, etc
090: *
091: * @param requiredParams
092: */
093: public void setRequiredParams(Map requiredParams) {
094: this .requiredParams = requiredParams;
095: }
096:
097: /**
098: * Optional params that are pulled from the message. If these params don't exist
099: * execution will continue. Note that you can use {@link ExpressionEvaluator}
100: * expressions such as xpath, header, xquery, etc
101: *
102: * @param requiredParams
103: */
104: public Map getOptionalParams() {
105: return optionalParams;
106: }
107:
108: public void setOptionalParams(Map optionalParams) {
109: this .optionalParams = optionalParams;
110: }
111:
112: public String getHttpMethod() {
113: return httpMethod;
114: }
115:
116: public void setHttpMethod(String httpMethod) {
117: this .httpMethod = httpMethod;
118: }
119:
120: public List getPayloadParameterNames() {
121: return payloadParameterNames;
122: }
123:
124: public void setPayloadParameterNames(List payloadParameterNames) {
125: this .payloadParameterNames = payloadParameterNames;
126: }
127:
128: public Filter getFilter() {
129: return errorFilter;
130: }
131:
132: public void setFilter(Filter errorFilter) {
133: this .errorFilter = errorFilter;
134: }
135:
136: public String getErrorExpression() {
137: return errorExpression;
138: }
139:
140: public void setErrorExpression(String errorExpression) {
141: this .errorExpression = errorExpression;
142: }
143:
144: protected void doInitialise() throws InitialisationException {
145: if (serviceUrl == null && !urlFromMessage) {
146: throw new InitialisationException(CoreMessages
147: .objectIsNull("serviceUrl"), this );
148: } else if (serviceUrl != null) {
149: try {
150: new URL(serviceUrl);
151: } catch (MalformedURLException e) {
152: throw new InitialisationException(e, this );
153: }
154: }
155:
156: if (errorFilter == null) {
157: if (errorExpression == null) {
158: // We'll set a default filter that checks the return code
159: errorFilter = new MessagePropertyFilter(
160: "http.status!=200");
161: logger
162: .info("Setting default error filter to MessagePropertyFilter('http.status!=200')");
163: } else {
164: errorFilter = new RegExFilter(errorExpression);
165: }
166: }
167: }
168:
169: public MuleMessage doOnCall(MuleEvent event) {
170: String tempUrl;
171: MuleMessage result = null;
172: try {
173:
174: Object request = event.transformMessage();
175: Object requestBody;
176: if (urlFromMessage) {
177: tempUrl = event.getMessage().getStringProperty(
178: REST_SERVICE_URL, null);
179: if (tempUrl == null) {
180: throw new IllegalArgumentException(CoreMessages
181: .propertyIsNotSetOnEvent(REST_SERVICE_URL)
182: .toString());
183: }
184: } else {
185: tempUrl = serviceUrl;
186: }
187: StringBuffer urlBuffer = new StringBuffer(tempUrl);
188:
189: if (GET.equalsIgnoreCase(this .httpMethod)) {
190: requestBody = NullPayload.getInstance();
191:
192: setRESTParams(urlBuffer, event.getMessage(), request,
193: requiredParams, false, null);
194: setRESTParams(urlBuffer, event.getMessage(), request,
195: optionalParams, true, null);
196: } else
197: // if post
198: {
199: StringBuffer requestBodyBuffer = new StringBuffer();
200: event.getMessage().setProperty(CONTENT_TYPE,
201: CONTENT_TYPE_VALUE);
202: setRESTParams(urlBuffer, event.getMessage(), request,
203: requiredParams, false, requestBodyBuffer);
204: setRESTParams(urlBuffer, event.getMessage(), request,
205: optionalParams, true, requestBodyBuffer);
206: requestBody = requestBodyBuffer.toString();
207: }
208:
209: tempUrl = urlBuffer.toString();
210: logger.info("Invoking REST service: " + tempUrl);
211:
212: event.getMessage().setProperty(HTTP_METHOD, httpMethod);
213:
214: result = RequestContext.getEventContext().sendEvent(
215: new DefaultMuleMessage(requestBody, event
216: .getMessage()), tempUrl);
217: if (isErrorPayload(result)) {
218: handleException(new RestServiceException(CoreMessages
219: .failedToInvokeRestService(tempUrl), result),
220: result);
221: }
222:
223: } catch (Exception e) {
224: // TODO Auto-generated catch block
225: e.printStackTrace();
226: }
227:
228: return result;
229: }
230:
231: private String getSeparator(String url) {
232: String sep;
233:
234: if (url.indexOf("?") > -1) {
235: sep = "&";
236: } else {
237: sep = "?";
238: }
239:
240: return sep;
241: }
242:
243: private String updateSeparator(String sep) {
244: if (sep.compareTo("?") == 0 || sep.compareTo("") == 0) {
245: return ("&");
246: }
247:
248: return sep;
249: }
250:
251: // if requestBodyBuffer is null, it means that the request is a GET, otherwise it
252: // is a POST and
253: // requestBodyBuffer must contain the body of the http method at the end of this
254: // function call
255: private void setRESTParams(StringBuffer url, MuleMessage msg,
256: Object body, Map args, boolean optional,
257: StringBuffer requestBodyBuffer) {
258: String sep;
259:
260: if (requestBodyBuffer == null) {
261: sep = getSeparator(url.toString());
262: } else {
263: sep = "";
264: }
265:
266: for (Iterator iterator = args.entrySet().iterator(); iterator
267: .hasNext();) {
268: Map.Entry entry = (Map.Entry) iterator.next();
269: String name = (String) entry.getKey();
270: String exp = (String) entry.getValue();
271: Object value = ExpressionEvaluatorManager
272: .evaluate(exp, msg);
273:
274: if (value == null) {
275: if (!optional) {
276: throw new IllegalArgumentException(CoreMessages
277: .propertyIsNotSetOnEvent(exp).toString());
278: }
279: } else if (requestBodyBuffer != null) // implies this is a POST
280: {
281: requestBodyBuffer.append(sep);
282: requestBodyBuffer.append(name).append('=')
283: .append(value);
284: } else {
285: url.append(sep);
286: url.append(name).append('=').append(value);
287: }
288:
289: sep = updateSeparator(sep);
290: }
291:
292: if (!optional && payloadParameterNames != null) {
293: if (body instanceof Object[]) {
294: Object[] requestArray = (Object[]) body;
295: for (int i = 0; i < payloadParameterNames.size(); i++) {
296: if (requestBodyBuffer != null) {
297: requestBodyBuffer.append(sep).append(
298: payloadParameterNames.get(i)).append(
299: '=').append(requestArray[i].toString());
300: } else {
301: url.append(sep).append(
302: payloadParameterNames.get(i)).append(
303: '=').append(requestArray[i].toString());
304: }
305:
306: sep = updateSeparator(sep);
307: }
308: } else {
309: if (payloadParameterNames.get(0) != null) {
310: if (requestBodyBuffer != null) {
311: requestBodyBuffer.append(
312: payloadParameterNames.get(0)).append(
313: '=').append(body.toString());
314: } else {
315: url.append(sep).append(
316: payloadParameterNames.get(0)).append(
317: '=').append(body.toString());
318: }
319: }
320: }
321: }
322: }
323:
324: protected boolean isErrorPayload(MuleMessage message) {
325: return errorFilter != null && errorFilter.accept(message);
326: }
327:
328: protected void handleException(RestServiceException e,
329: MuleMessage result) throws Exception {
330: throw e;
331: }
332:
333: // @Override
334: protected void doOnEvent(MuleEvent event) {
335: try {
336: onCall(event);
337: } catch (MuleException e) {
338: logger.error(e);
339: }
340: }
341:
342: // @Override
343: protected void doDispose() {
344: // no-op
345: }
346:
347: // @Override
348: protected void doStart() throws MuleException {
349: // no-op
350: }
351:
352: // @Override
353: protected void doStop() throws MuleException {
354: // no-op
355: }
356: }
|