You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicemix.apache.org by gn...@apache.org on 2008/05/07 14:58:28 UTC

svn commit: r654087 - in /servicemix/smx3/trunk: core/servicemix-core/src/main/java/org/apache/servicemix/expression/ deployables/bindingcomponents/servicemix-http/src/main/java/org/apache/servicemix/http/endpoints/

Author: gnodet
Date: Wed May  7 05:58:27 2008
New Revision: 654087

URL: http://svn.apache.org/viewvc?rev=654087&view=rev
Log:
SM-1307: Rest provider marshaler

Added:
    servicemix/smx3/trunk/deployables/bindingcomponents/servicemix-http/src/main/java/org/apache/servicemix/http/endpoints/RestProviderMarshaler.java
Modified:
    servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/expression/JAXPXPathExpression.java

Modified: servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/expression/JAXPXPathExpression.java
URL: http://svn.apache.org/viewvc/servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/expression/JAXPXPathExpression.java?rev=654087&r1=654086&r2=654087&view=diff
==============================================================================
--- servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/expression/JAXPXPathExpression.java (original)
+++ servicemix/smx3/trunk/core/servicemix-core/src/main/java/org/apache/servicemix/expression/JAXPXPathExpression.java Wed May  7 05:58:27 2008
@@ -22,9 +22,11 @@
 import javax.jbi.messaging.MessagingException;
 import javax.jbi.messaging.NormalizedMessage;
 import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.TransformerException;
 import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpression;
 import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFactory;
@@ -96,17 +98,45 @@
     }
 
     /**
+     * Evaluates the XPath expression and returns the string values for the XML items described
+     * by that expression.
+     *
      * Before evaluating the xpath expression, it will be compiled by calling
      * the {@link #afterPropertiesSet()} method.
+     *
+     * @param exchange MessageExchange to use on MessageVariableResolver
+     * @param message  NormalizedMessage to use on MessageVariableResolver
+     *
+     * @return Object  Contains the string values for the XML items described by the provided XPath
+     *                 expression
      */
     public Object evaluate(MessageExchange exchange, NormalizedMessage message) throws MessagingException {
+        return evaluate(exchange, message, XPathConstants.STRING);
+    }
+
+    /**
+     * Evaluates the XPath expression and the XML items described by that expression. The type is
+     * determined by the returnType parameter.  
+     *
+     * Before evaluating the xpath expression, it will be compiled by calling
+     * the {@link #afterPropertiesSet()} method.
+     *
+     * @param exchange    MessageExchange to use on MessageVariableResolver
+     * @param message     NormalizedMessage to use on MessageVariableResolver
+     * @param returnType  QName as defined by javax.xml.xpath.XPathConstants that describes the
+     *                    desired type of the object to be retuned
+     *
+     * @return Object    Contains the XML items described by the provided XPath expression. The type is
+     *                   determined by the returnType parameter.
+     */
+    public Object evaluate(MessageExchange exchange, NormalizedMessage message, QName returnType) throws MessagingException {
         try {
             afterPropertiesSet();
             Object object = getXMLNode(exchange, message);
             synchronized (this) {
                 variableResolver.setExchange(exchange);
                 variableResolver.setMessage(message);
-                return evaluateXPath(object);
+                return evaluateXPath(object, returnType);
             }
         } catch (TransformerException e) {
             throw new MessagingException(e);
@@ -196,6 +226,10 @@
         return xPathExpression.evaluate(object);
     }
 
+    protected Object evaluateXPath(Object object, QName returnType) throws XPathExpressionException {
+        return xPathExpression.evaluate(object, returnType);
+    }
+
     protected XPathExpression getXPathExpression() {
         return xPathExpression;
     }

Added: servicemix/smx3/trunk/deployables/bindingcomponents/servicemix-http/src/main/java/org/apache/servicemix/http/endpoints/RestProviderMarshaler.java
URL: http://svn.apache.org/viewvc/servicemix/smx3/trunk/deployables/bindingcomponents/servicemix-http/src/main/java/org/apache/servicemix/http/endpoints/RestProviderMarshaler.java?rev=654087&view=auto
==============================================================================
--- servicemix/smx3/trunk/deployables/bindingcomponents/servicemix-http/src/main/java/org/apache/servicemix/http/endpoints/RestProviderMarshaler.java (added)
+++ servicemix/smx3/trunk/deployables/bindingcomponents/servicemix-http/src/main/java/org/apache/servicemix/http/endpoints/RestProviderMarshaler.java Wed May  7 05:58:27 2008
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.http.endpoints;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jbi.messaging.ExchangeStatus;
+import javax.jbi.messaging.Fault;
+import javax.jbi.messaging.InOnly;
+import javax.jbi.messaging.MessageExchange;
+import javax.jbi.messaging.NormalizedMessage;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Node;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.servicemix.expression.JAXPStringXPathExpression;
+import org.apache.servicemix.http.jetty.SmxHttpExchange;
+import org.apache.servicemix.jbi.jaxp.SourceTransformer;
+import org.mortbay.io.ByteArrayBuffer;
+import org.mortbay.jetty.HttpFields;
+import org.mortbay.jetty.HttpHeaders;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: tpurcell
+ * Date: Mar 12, 2008
+ * Time: 3:37:47 PM
+ *
+ * Provides basic support for calling RESTful services using the servicemix-http JBI component
+ */
+public class RestProviderMarshaler extends DefaultHttpProviderMarshaler {
+    private SourceTransformer transformer = new SourceTransformer();
+    private JAXPStringXPathExpression contentExpression;
+
+    /**
+     * Returns the XPath expression that decribes the content to send.
+     *
+     * @return JAXPStringXPathExpression  XPath expression that decribes the content to send
+     */
+    public JAXPStringXPathExpression getContentExpression() {
+        return contentExpression;
+    }
+
+    /**
+     * Accepts the XPath expression that decribes the content to send.
+     *
+     * @param contentExpression JAXPStringXPathExpression that decribes the content to send
+     */
+    public void setContentExpression(JAXPStringXPathExpression contentExpression) {
+        this.contentExpression = contentExpression;
+    }
+
+    public void createRequest(MessageExchange exchange, NormalizedMessage inMsg, SmxHttpExchange httpExchange) throws Exception {
+        httpExchange.setURL(getLocationUri(exchange, inMsg));
+
+        // Temporary fix for bug in jetty-client 6.1.5
+        // http://fisheye.codehaus.org/browse/jetty-contrib/jetty/trunk/contrib/client/src/main/
+        //                  java/org/mortbay/jetty/client/HttpConnection.java?r1=374&r2=378
+        httpExchange.addRequestHeader(HttpHeaders.HOST_BUFFER, new ByteArrayBuffer(new URI(getLocationUri(exchange, inMsg)).getHost()));
+
+        httpExchange.setMethod(getMethod(exchange, inMsg));
+        httpExchange.setRequestHeader(HttpHeaders.CONTENT_TYPE, getContentType(exchange, inMsg));
+
+        if (getHeaders() != null) {
+            for (Map.Entry<String, String> e : getHeaders().entrySet()) {
+                httpExchange.setRequestHeader(e.getKey(), e.getValue());
+            }
+        }
+
+        if (contentExpression != null) {
+            String contentToSend = applyContentExpression(exchange, inMsg);
+            if (contentToSend != null) {
+                httpExchange.setRequestContent(new ByteArrayBuffer(contentToSend.getBytes()));
+            }
+        } else if (inMsg.getContent() != null) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            transformer.toResult(inMsg.getContent(), new StreamResult(baos));
+            httpExchange.setRequestContent(new ByteArrayBuffer(baos.toByteArray()));
+        }
+    }
+
+    /**
+     * Calls evaluate on the XPath expression requesting a XPathConstants.NODE as the return type. This will make it
+     * possible for a SourceTransformer to construct a full XML representation of the selected nodes.
+     *
+     * @param exchange  MessageExchange to use on MessageVariableResolver
+     * @param inMsg     NormalizedMessage to use on MessageVariableResolver
+     * @return String   Contains the XML items described by the provided XPath expression.
+     */
+    protected String applyContentExpression(MessageExchange exchange, NormalizedMessage inMsg) throws Exception {
+        String content = null;
+
+        if (contentExpression != null) {
+            Node node = (Node) contentExpression.evaluate(exchange, inMsg, XPathConstants.NODE);
+            content = transformer.toString(new DOMSource(node));
+        }
+
+        if (content == null) {
+            throw new IllegalStateException("XPath expression failed. Unable to find Content for exchange");
+        }
+        return content;
+    }
+
+    /**
+     * Accept the response from the RESTful service and pass it on to the NMR
+     * @param exchange      MessageExchange to use on MessageVariableResolver
+     * @param httpExchange  SmxHttpExchange which holds the response from the RESTful service
+     * @throws Exception
+     */
+    public void handleResponse(MessageExchange exchange, SmxHttpExchange httpExchange) throws Exception {
+        int response = httpExchange.getResponseStatus();
+
+        if (exchange instanceof InOnly) {
+            processInOnly(exchange, response);
+        } else if (isSuccessful(response)) {
+            NormalizedMessage msg = exchange.createMessage();
+            msg.setContent(packageHttpResponse(httpExchange, response));
+            exchange.setMessage(msg, "out");
+        } else {
+            Fault fault = exchange.createFault();
+            fault.setContent(packageHttpResponse(httpExchange, response));
+            exchange.setFault(fault);
+        }
+
+    }
+
+    /**
+     * Handles InOnly MEP. If the call was not successful there's noone to tell so throw an exception
+     *
+     * @param exchange      MessageExchange to use on MessageVariableResolver
+     * @param response      HTTP Status Code returned by the RESTful service
+     */
+    private void processInOnly(MessageExchange exchange, int response) throws Exception {
+        if (isSuccessful(response)) {
+            exchange.setStatus(ExchangeStatus.DONE);
+        } else {
+            throw new Exception("Invalid status response: " + response);
+        }
+    }
+
+    /**
+     * Returns true for the following HTTP staus codes as successful:
+     * OK                       200 OK
+     * CREATED                  201 Created
+     * ACCEPTED                 202 Accepted
+     * NO_CONTENT               204 No Content
+     *
+     * False for all others.
+     *
+     * @param   httpStatusCode Code to evaluate.
+     * @return  True for the above codes. False for all others.
+     */
+    public boolean isSuccessful(int httpStatusCode) {
+        if (httpStatusCode == HttpStatus.SC_OK
+                || httpStatusCode == HttpStatus.SC_CREATED
+                || httpStatusCode == HttpStatus.SC_ACCEPTED
+                || httpStatusCode == HttpStatus.SC_NO_CONTENT) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Provides basic packaging for the response from the RESTful service. The XML returned has the following structure:
+     *
+     * <response>
+     *    <http-status>
+     *       <code>nnn</code>
+     *    </http-status>
+     *    <http-headers>
+     *       <"header field name 1">header field value 1"</"header field name 1">
+     *       <"header field name 2">header field value 2"</"header field name 2">
+     *       ...
+     *       <"header field name n">header field value n"</"header field name n">
+     *    </http-headers>
+     *    <content>
+     *      "Actual HTTP content(if any)"
+     *    </content>
+     * </response>
+     *
+     * @param httpExchange  SmxHttpExchange which holds the response from the RESTful service
+     * @param response      HTTP Status Code returned by the RESTful service
+     * @return StreamSource
+     */
+    public StreamSource packageHttpResponse(SmxHttpExchange httpExchange, int response) throws UnsupportedEncodingException {
+        StringBuffer responseBuffer = new StringBuffer();
+        responseBuffer.append("<response>");
+
+        responseBuffer.append("<http-status>");
+        responseBuffer.append("<code>" + response + "</code>");
+        responseBuffer.append("</http-status>");
+
+        responseBuffer.append("<http-headers>");
+        Iterator responseFields = httpExchange.getResponseFields().getFields();
+        while (responseFields.hasNext()) {
+            HttpFields.Field httpField = (HttpFields.Field) responseFields.next();
+            responseBuffer.append("<");
+            responseBuffer.append(httpField.getName());
+            responseBuffer.append(">");
+            responseBuffer.append(httpField.getValue());
+            responseBuffer.append("</");
+            responseBuffer.append(httpField.getName());
+            responseBuffer.append(">");
+        }
+        responseBuffer.append("</http-headers>");
+
+        if (httpExchange.getResponseData() != null) {
+            responseBuffer.append("<content>");
+            responseBuffer.append(httpExchange.getResponseContent());
+            responseBuffer.append("</content>");
+        } else {
+            responseBuffer.append("<content/>");
+        }
+
+        responseBuffer.append("</response>");
+
+        return new StreamSource(new InputStreamReader(new ByteArrayInputStream(responseBuffer.toString().getBytes()), "utf-8"));
+    }
+}