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.camel.dataformat.xstream;
018    
019    import java.io.InputStream;
020    import java.io.OutputStream;
021    import java.lang.reflect.Constructor;
022    import java.lang.reflect.Method;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Map.Entry;
026    
027    import javax.xml.stream.XMLStreamException;
028    
029    import com.thoughtworks.xstream.XStream;
030    import com.thoughtworks.xstream.converters.Converter;
031    import com.thoughtworks.xstream.io.HierarchicalStreamReader;
032    import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
033    
034    import org.apache.camel.Exchange;
035    import org.apache.camel.converter.jaxp.StaxConverter;
036    import org.apache.camel.spi.ClassResolver;
037    import org.apache.camel.spi.DataFormat;
038    import org.apache.camel.util.ObjectHelper;
039    
040    /**
041     * An abstract class which implement <a href="http://camel.apache.org/data-format.html">data format</a>
042     * ({@link DataFormat}) interface which leverage the XStream library for XML or JSON's marshaling and unmarshaling
043     *
044     * @version $Revision: 962504 $
045     */
046    public abstract class AbstractXStreamWrapper implements DataFormat {
047        
048        private XStream xstream;
049        private StaxConverter staxConverter;
050        private List<String> converters;
051        private Map<String, String> aliases;
052        private Map<String, String[]> implicitCollections;
053    
054        public AbstractXStreamWrapper() {
055        }
056        
057        public AbstractXStreamWrapper(XStream xstream) {
058            this.xstream = xstream;
059        }
060        
061        public XStream getXStream(ClassResolver resolver) {
062            if (xstream == null) {
063                xstream = createXStream(resolver);
064            }
065            return xstream;
066        }
067    
068        public void setXStream(XStream xstream) {
069            this.xstream = xstream;
070        }
071    
072        protected XStream createXStream(ClassResolver resolver) {
073            xstream = new XStream();
074    
075            try {
076                if (this.implicitCollections != null) {
077                    for (Entry<String, String[]> entry : this.implicitCollections.entrySet()) {
078                        for (String name : entry.getValue()) {
079                            xstream.addImplicitCollection(resolver.resolveMandatoryClass(entry.getKey()), name);
080                        }
081                    }
082                }
083    
084                if (this.aliases != null) {
085                    for (Entry<String, String> entry : this.aliases.entrySet()) {
086                        xstream.alias(entry.getKey(), resolver.resolveMandatoryClass(entry.getValue()));
087                    }
088                }
089    
090                if (this.converters != null) {
091                    for (String name : this.converters) {
092                        Class<Converter> converterClass = resolver.resolveMandatoryClass(name, Converter.class);
093                        Converter converter;
094    
095                        Constructor con = null;
096                        try {
097                            con = converterClass.getDeclaredConstructor(new Class[] {XStream.class});
098                        } catch (Exception e) {
099                             //swallow as we null check in a moment.
100                        }
101                        if (con != null) {
102                            converter = (Converter) con.newInstance(xstream);
103                        } else {
104                            converter = converterClass.newInstance();
105                            try { 
106                                Method method = converterClass.getMethod("setXStream", new Class[] {XStream.class});
107                                if (method != null) {
108                                    ObjectHelper.invokeMethod(method, converter, xstream);
109                                }
110                            } catch (Throwable e) {
111                                // swallow, as it just means the user never add an XStream setter, which is optional
112                            }
113                        }
114    
115                        xstream.registerConverter(converter);
116                    }
117                }
118            } catch (Exception e) {
119                throw new RuntimeException("Unable to build XStream instance", e);
120            }
121    
122            return xstream;
123        }    
124    
125        public StaxConverter getStaxConverter() {
126            if (staxConverter == null) {
127                staxConverter = new StaxConverter();
128            }
129            return staxConverter;
130        }
131    
132        public void setStaxConverter(StaxConverter staxConverter) {
133            this.staxConverter = staxConverter;
134        }
135    
136        public List<String> getConverters() {
137            return converters;
138        }
139    
140        public void setConverters(List<String> converters) {
141            this.converters = converters;
142        }
143    
144        public Map<String, String> getAliases() {
145            return aliases;
146        }
147    
148        public void setAliases(Map<String, String> aliases) {
149            this.aliases = aliases;
150        }
151    
152        public Map<String, String[]> getImplicitCollections() {
153            return implicitCollections;
154        }
155    
156        public void setImplicitCollections(Map<String, String[]> implicitCollections) {
157            this.implicitCollections = implicitCollections;
158        }
159    
160        public XStream getXstream() {
161            return xstream;
162        }
163    
164        public void setXstream(XStream xstream) {
165            this.xstream = xstream;
166        }
167    
168        public void marshal(Exchange exchange, Object body, OutputStream stream) throws Exception {
169            HierarchicalStreamWriter writer = createHierarchicalStreamWriter(exchange, body, stream);
170            try {
171                getXStream(exchange.getContext().getClassResolver()).marshal(body, writer);
172            } finally {
173                writer.close();
174            }
175        }
176    
177        public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
178            HierarchicalStreamReader reader = createHierarchicalStreamReader(exchange, stream);
179            try {
180                return getXStream(exchange.getContext().getClassResolver()).unmarshal(reader);
181            } finally {
182                reader.close();
183            }
184        }
185    
186        protected abstract HierarchicalStreamWriter createHierarchicalStreamWriter(
187                Exchange exchange, Object body, OutputStream stream) throws XMLStreamException;
188    
189        protected abstract HierarchicalStreamReader createHierarchicalStreamReader(
190                Exchange exchange, InputStream stream) throws XMLStreamException;
191    }