001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.http.endpoints;
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    import java.io.ObjectInputStream;
022    import java.io.ObjectOutputStream;
023    import java.io.OutputStream;
024    import java.io.StringWriter;
025    import java.io.Writer;
026    
027    import javax.jbi.component.ComponentContext;
028    import javax.jbi.messaging.MessageExchange;
029    import javax.jbi.messaging.NormalizedMessage;
030    import javax.servlet.http.HttpServletRequest;
031    import javax.servlet.http.HttpServletResponse;
032    import javax.xml.transform.Source;
033    import javax.xml.transform.TransformerException;
034    
035    import com.thoughtworks.xstream.XStream;
036    import com.thoughtworks.xstream.io.xml.DomDriver;
037    
038    import org.apache.commons.logging.Log;
039    import org.apache.commons.logging.LogFactory;
040    import org.apache.servicemix.jbi.jaxp.SourceTransformer;
041    import org.apache.servicemix.jbi.jaxp.StringSource;
042    
043    /**
044     * A marshaler that handles Java serialized content from the InputStream of the HttpServletRequest object and to the
045     * OutputStream of the HttpServletResponse object. This class is intended to handle requests initiated by the Spring <a
046     * href="http://www.springframework.org/docs/api/org/springframework/remoting/httpinvoker/package-summary.html">httpinvoker
047     * package</a> so the marshaled/unmarshaled XML invocation will be Spring <a
048     * href="http://www.springframework.org/docs/api/org/springframework/remoting/support/RemoteInvocation.html">RemoteInvocation</a>/
049     * <a href="http://www.springframework.org/docs/api/org/springframework/remoting/support/RemoteInvocationResult.html">
050     * RemoteInvocationResult</a> objects respectively.
051     * 
052     * <p>
053     * This class makes no assumptions about how XML should be marshaled/unmarshaled. I.e., there is currently no way to
054     * customize the marshaled XML invocation. So this marshaler will need to pass the XML to a component that can transform
055     * it into some custom XML. The servicemix-saxon component can handle this very easily via XLST.
056     * 
057     * @author bsnyder, aco
058     * @org.apache.xbean.XBean element="serializedMarshaler" description="a consumer-side marshaler for handling Java serialized content"
059     */
060    public class SerializedMarshaler extends DefaultHttpConsumerMarshaler {
061    
062        private static Log log = LogFactory.getLog(SerializedMarshaler.class);
063    
064        public MessageExchange createExchange(HttpServletRequest request, ComponentContext context) throws Exception {
065            MessageExchange me = context.getDeliveryChannel().createExchangeFactory().createExchange(getDefaultMep());
066            NormalizedMessage in = me.createMessage();
067            in.setContent(marshal(request.getInputStream()));
068            me.setMessage(in, "in");
069            return me;
070        }
071    
072        public void sendOut(MessageExchange exchange, NormalizedMessage outMsg, HttpServletRequest request,
073                        HttpServletResponse response) throws Exception {
074            if (outMsg.getContent() != null) {
075                unmarshal(response.getOutputStream(), outMsg.getContent());
076            }
077        }
078    
079        /**
080         * Marshal the byte content of the input stream to an XML source. This method is marshaling the contents of the
081         * Spring <a
082         * href="http://www.springframework.org/docs/api/org/springframework/remoting/support/RemoteInvocation.html">RemoteInvocation</a>
083         * object. Below is an example of what this method emits:
084         * 
085         * <pre>
086         * &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;org.springframework.remoting.support.RemoteInvocation&gt;
087         *   &lt;methodName&gt;login&lt;/methodName&gt;
088         *     &lt;parameterTypes&gt;
089         *       &lt;java-class&gt;java.lang.String&lt;/java-class&gt;
090         *       &lt;java-class&gt;java.lang.String&lt;/java-class&gt;
091         *     &lt;/parameterTypes&gt;
092         *     &lt;arguments&gt;
093         *       &lt;string&gt;foo&lt;/string&gt;
094         *       &lt;string&gt;bar&lt;/string&gt;
095         *     &lt;/arguments&gt;
096         *   &lt;/org.springframework.remoting.support.RemoteInvocation&gt;
097         * </pre>
098         * 
099         * @param is -
100         *            input stream to read the object from
101         * @return xml source
102         * @throws IOException
103         * @throws ClassNotFoundException
104         */
105        private Source marshal(InputStream is) throws IOException, ClassNotFoundException {
106            Object obj = new ObjectInputStream(is).readObject();
107            Writer w = new StringWriter();
108            XStream xstream = new XStream(new DomDriver());
109            xstream.toXML(obj, w);
110            String request = w.toString();
111    
112            if (log.isDebugEnabled()) {
113                log.debug("Remote invocation request: " + request);
114            }
115    
116            return new StringSource(request);
117        }
118    
119        /**
120         * Unmarshal the XML content to the specified output stream. This method is unmarshaling XML into the Spring 
121         * <a href="http://www.springframework.org/docs/api/org/springframework/remoting/support/RemoteInvocationResult.html">
122         * RemoteInvocationResult</a> object. Below is an example of the XML expected by this method:
123         * 
124         * <pre>
125         * &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
126         * &lt;org.springframework.remoting.support.RemoteInvocationResult&gt;
127         *   &lt;value class=&quot;com.example.foo.bar.Baz&quot;&gt;
128         *     &lt;firstName&gt;myfirstname&lt;/firstName&gt;
129         *     &lt;lastName&gt;mylastname&lt;/lastName&gt;
130         *     &lt;phone&gt;12312312&lt;/phone&gt;
131         *   &lt;/value&gt;
132         * &lt;/org.springframework.remoting.support.RemoteInvocationResult&gt;
133         * </pre>
134         * 
135         * @param os -
136         *            output stream to unmarshal to
137         * @param content -
138         *            XML source
139         * @throws TransformerException
140         * @throws IOException
141         */
142        private void unmarshal(OutputStream os, Source content) throws TransformerException, IOException {
143            SourceTransformer transform = new SourceTransformer();
144            XStream xstream = new XStream(new DomDriver());
145            String result = transform.toString(content);
146    
147            if (log.isDebugEnabled()) {
148                log.debug("Remote invocation result: " + result);
149            }
150    
151            Object obj = xstream.fromXML(result);
152            ObjectOutputStream oos = new ObjectOutputStream(os);
153            oos.writeObject(obj);
154        }
155    }