| /** |
| * 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.camel.dataformat.xstream; |
| |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Method; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import javax.xml.stream.XMLStreamException; |
| |
| import com.thoughtworks.xstream.XStream; |
| import com.thoughtworks.xstream.converters.Converter; |
| import com.thoughtworks.xstream.core.util.CompositeClassLoader; |
| import com.thoughtworks.xstream.io.HierarchicalStreamDriver; |
| import com.thoughtworks.xstream.io.HierarchicalStreamReader; |
| import com.thoughtworks.xstream.io.HierarchicalStreamWriter; |
| import com.thoughtworks.xstream.security.AnyTypePermission; |
| import com.thoughtworks.xstream.security.ExplicitTypePermission; |
| import com.thoughtworks.xstream.security.TypePermission; |
| import com.thoughtworks.xstream.security.WildcardTypePermission; |
| import org.apache.camel.CamelContext; |
| import org.apache.camel.Exchange; |
| import org.apache.camel.converter.jaxp.StaxConverter; |
| import org.apache.camel.spi.ClassResolver; |
| import org.apache.camel.spi.DataFormat; |
| import org.apache.camel.spi.DataFormatName; |
| import org.apache.camel.support.ServiceSupport; |
| import org.apache.camel.util.ObjectHelper; |
| |
| /** |
| * An abstract class which implement <a href="http://camel.apache.org/data-format.html">data format</a> |
| * ({@link DataFormat}) interface which leverage the XStream library for XML or JSON's marshaling and unmarshaling |
| */ |
| public abstract class AbstractXStreamWrapper extends ServiceSupport implements DataFormat, DataFormatName { |
| private static final String PERMISSIONS_PROPERTY_KEY = "org.apache.camel.xstream.permissions"; |
| private static final String PERMISSIONS_PROPERTY_DEFAULT = "-*,java.lang.*,java.util.*"; |
| |
| private XStream xstream; |
| private HierarchicalStreamDriver xstreamDriver; |
| private StaxConverter staxConverter; |
| private List<String> converters; |
| private Map<String, String> aliases; |
| private Map<String, String[]> omitFields; |
| private Map<String, String[]> implicitCollections; |
| private String permissions; |
| private String mode; |
| |
| public AbstractXStreamWrapper() { |
| } |
| |
| public AbstractXStreamWrapper(XStream xstream) { |
| this.xstream = xstream; |
| } |
| |
| /** |
| * Resolves the XStream instance to be used by this data format. If XStream is not explicitly set, new instance will |
| * be created and cached. |
| * |
| * @param resolver class resolver to be used during a configuration of the XStream instance. |
| * @return XStream instance used by this data format. |
| */ |
| public XStream getXStream(ClassResolver resolver) { |
| if (xstream == null) { |
| xstream = createXStream(resolver); |
| } |
| return xstream; |
| } |
| |
| /** |
| * Resolves the XStream instance to be used by this data format. If XStream is not explicitly set, new instance will |
| * be created and cached. |
| * |
| * @param context to be used during a configuration of the XStream instance |
| * @return XStream instance used by this data format. |
| */ |
| public XStream getXStream(CamelContext context) { |
| if (xstream == null) { |
| xstream = createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); |
| } |
| return xstream; |
| } |
| |
| public void setXStream(XStream xstream) { |
| this.xstream = xstream; |
| } |
| |
| /** |
| * @deprecated Use {@link #createXStream(ClassResolver, ClassLoader)} |
| */ |
| @Deprecated |
| protected XStream createXStream(ClassResolver resolver) { |
| return createXStream(resolver, null); |
| } |
| |
| protected XStream createXStream(ClassResolver resolver, ClassLoader classLoader) { |
| if (xstreamDriver != null) { |
| xstream = new XStream(xstreamDriver); |
| } else { |
| xstream = new XStream(); |
| } |
| |
| if (mode != null) { |
| xstream.setMode(getModeFromString(mode)); |
| } |
| |
| ClassLoader xstreamLoader = xstream.getClassLoader(); |
| if (classLoader != null && xstreamLoader instanceof CompositeClassLoader) { |
| ((CompositeClassLoader) xstreamLoader).add(classLoader); |
| } |
| |
| try { |
| if (this.implicitCollections != null) { |
| for (Entry<String, String[]> entry : this.implicitCollections.entrySet()) { |
| for (String name : entry.getValue()) { |
| xstream.addImplicitCollection(resolver.resolveMandatoryClass(entry.getKey()), name); |
| } |
| } |
| } |
| |
| if (this.aliases != null) { |
| for (Entry<String, String> entry : this.aliases.entrySet()) { |
| xstream.alias(entry.getKey(), resolver.resolveMandatoryClass(entry.getValue())); |
| // It can turn the auto-detection mode off |
| xstream.processAnnotations(resolver.resolveMandatoryClass(entry.getValue())); |
| } |
| } |
| |
| if (this.omitFields != null) { |
| for (Entry<String, String[]> entry : this.omitFields.entrySet()) { |
| for (String name : entry.getValue()) { |
| xstream.omitField(resolver.resolveMandatoryClass(entry.getKey()), name); |
| } |
| } |
| } |
| |
| if (this.converters != null) { |
| for (String name : this.converters) { |
| Class<Converter> converterClass = resolver.resolveMandatoryClass(name, Converter.class); |
| Converter converter; |
| |
| Constructor<Converter> con = null; |
| try { |
| con = converterClass.getDeclaredConstructor(new Class[]{XStream.class}); |
| } catch (Exception e) { |
| //swallow as we null check in a moment. |
| } |
| if (con != null) { |
| converter = con.newInstance(xstream); |
| } else { |
| converter = converterClass.newInstance(); |
| try { |
| Method method = converterClass.getMethod("setXStream", new Class[]{XStream.class}); |
| if (method != null) { |
| ObjectHelper.invokeMethod(method, converter, xstream); |
| } |
| } catch (Throwable e) { |
| // swallow, as it just means the user never add an XStream setter, which is optional |
| } |
| } |
| |
| xstream.registerConverter(converter); |
| } |
| } |
| |
| addDefaultPermissions(xstream); |
| if (this.permissions != null) { |
| // permissions ::= pterm (',' pterm)* # consits of one or more terms |
| // pterm ::= aod? wterm # each term preceded by an optional sign |
| // aod ::= '+' | '-' # indicates allow or deny where allow if omitted |
| // wterm ::= a class name with optional wildcard characters |
| addPermissions(xstream, permissions); |
| } |
| } catch (Exception e) { |
| throw new RuntimeException("Unable to build XStream instance", e); |
| } |
| |
| return xstream; |
| } |
| |
| private static void addPermissions(XStream xstream, String permissions) { |
| for (String pterm : permissions.split(",")) { |
| boolean aod; |
| pterm = pterm.trim(); |
| if (pterm.startsWith("-")) { |
| aod = false; |
| pterm = pterm.substring(1); |
| } else { |
| aod = true; |
| if (pterm.startsWith("+")) { |
| pterm = pterm.substring(1); |
| } |
| } |
| TypePermission typePermission = null; |
| if ("*".equals(pterm)) { |
| // accept or deny any |
| typePermission = AnyTypePermission.ANY; |
| } else if (pterm.indexOf('*') < 0) { |
| // exact type |
| typePermission = new ExplicitTypePermission(new String[]{pterm}); |
| } else if (pterm.length() > 0) { |
| // wildcard type |
| typePermission = new WildcardTypePermission(new String[]{pterm}); |
| } |
| if (typePermission != null) { |
| if (aod) { |
| xstream.addPermission(typePermission); |
| } else { |
| xstream.denyPermission(typePermission); |
| } |
| } |
| } |
| } |
| |
| private static void addDefaultPermissions(XStream xstream) { |
| addPermissions(xstream, System.getProperty(PERMISSIONS_PROPERTY_KEY, PERMISSIONS_PROPERTY_DEFAULT)); |
| } |
| |
| protected int getModeFromString(String modeString) { |
| int result; |
| if ("NO_REFERENCES".equalsIgnoreCase(modeString)) { |
| result = XStream.NO_REFERENCES; |
| } else if ("ID_REFERENCES".equalsIgnoreCase(modeString)) { |
| result = XStream.ID_REFERENCES; |
| } else if ("XPATH_RELATIVE_REFERENCES".equalsIgnoreCase(modeString)) { |
| result = XStream.XPATH_RELATIVE_REFERENCES; |
| } else if ("XPATH_ABSOLUTE_REFERENCES".equalsIgnoreCase(modeString)) { |
| result = XStream.XPATH_ABSOLUTE_REFERENCES; |
| } else if ("SINGLE_NODE_XPATH_RELATIVE_REFERENCES".equalsIgnoreCase(modeString)) { |
| result = XStream.SINGLE_NODE_XPATH_RELATIVE_REFERENCES; |
| } else if ("SINGLE_NODE_XPATH_ABSOLUTE_REFERENCES".equalsIgnoreCase(modeString)) { |
| result = XStream.SINGLE_NODE_XPATH_ABSOLUTE_REFERENCES; |
| } else { |
| throw new IllegalArgumentException("Unknown mode : " + modeString); |
| } |
| return result; |
| } |
| |
| public StaxConverter getStaxConverter() { |
| if (staxConverter == null) { |
| staxConverter = new StaxConverter(); |
| } |
| return staxConverter; |
| } |
| |
| public void setStaxConverter(StaxConverter staxConverter) { |
| this.staxConverter = staxConverter; |
| } |
| |
| public List<String> getConverters() { |
| return converters; |
| } |
| |
| public void setConverters(List<String> converters) { |
| this.converters = converters; |
| } |
| |
| public Map<String, String> getAliases() { |
| return aliases; |
| } |
| |
| public void setAliases(Map<String, String> aliases) { |
| this.aliases = aliases; |
| } |
| |
| public Map<String, String[]> getImplicitCollections() { |
| return implicitCollections; |
| } |
| |
| public void setImplicitCollections(Map<String, String[]> implicitCollections) { |
| this.implicitCollections = implicitCollections; |
| } |
| |
| public Map<String, String[]> getOmitFields() { |
| return omitFields; |
| } |
| |
| public void setOmitFields(Map<String, String[]> omitFields) { |
| this.omitFields = omitFields; |
| } |
| |
| public HierarchicalStreamDriver getXstreamDriver() { |
| return xstreamDriver; |
| } |
| |
| public void setXstreamDriver(HierarchicalStreamDriver xstreamDriver) { |
| this.xstreamDriver = xstreamDriver; |
| } |
| |
| public String getPermissions() { |
| return permissions; |
| } |
| |
| public void setPermissions(String permissions) { |
| this.permissions = permissions; |
| } |
| |
| public String getMode() { |
| return mode; |
| } |
| |
| public void setMode(String mode) { |
| this.mode = mode; |
| } |
| |
| public XStream getXstream() { |
| return xstream; |
| } |
| |
| public void setXstream(XStream xstream) { |
| this.xstream = xstream; |
| } |
| |
| public void marshal(Exchange exchange, Object body, OutputStream stream) throws Exception { |
| HierarchicalStreamWriter writer = createHierarchicalStreamWriter(exchange, body, stream); |
| try { |
| getXStream(exchange.getContext()).marshal(body, writer); |
| } finally { |
| writer.close(); |
| } |
| } |
| |
| public Object unmarshal(Exchange exchange, InputStream stream) throws Exception { |
| HierarchicalStreamReader reader = createHierarchicalStreamReader(exchange, stream); |
| try { |
| return getXStream(exchange.getContext()).unmarshal(reader); |
| } finally { |
| reader.close(); |
| } |
| } |
| |
| protected abstract HierarchicalStreamWriter createHierarchicalStreamWriter( |
| Exchange exchange, Object body, OutputStream stream) throws XMLStreamException; |
| |
| protected abstract HierarchicalStreamReader createHierarchicalStreamReader( |
| Exchange exchange, InputStream stream) throws XMLStreamException; |
| |
| @Override |
| protected void doStart() throws Exception { |
| // noop |
| } |
| |
| @Override |
| protected void doStop() throws Exception { |
| // noop |
| } |
| } |