Home » spring-ws-1.5.9-with-dependencies » org.springframework » oxm » castor » [javadoc | source]

    1   /*
    2    * Copyright 2002-2009 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   package org.springframework.oxm.castor;
   17   
   18   import java.io.IOException;
   19   import java.io.InputStream;
   20   import java.io.OutputStream;
   21   import java.io.OutputStreamWriter;
   22   import java.io.Reader;
   23   import java.io.Writer;
   24   import java.util.Iterator;
   25   import java.util.Properties;
   26   import javax.xml.stream.XMLEventReader;
   27   import javax.xml.stream.XMLEventWriter;
   28   import javax.xml.stream.XMLStreamReader;
   29   import javax.xml.stream.XMLStreamWriter;
   30   
   31   import org.exolab.castor.mapping.Mapping;
   32   import org.exolab.castor.mapping.MappingException;
   33   import org.exolab.castor.xml.MarshalException;
   34   import org.exolab.castor.xml.Marshaller;
   35   import org.exolab.castor.xml.ResolverException;
   36   import org.exolab.castor.xml.UnmarshalHandler;
   37   import org.exolab.castor.xml.Unmarshaller;
   38   import org.exolab.castor.xml.XMLContext;
   39   import org.exolab.castor.xml.XMLException;
   40   import org.w3c.dom.Node;
   41   import org.xml.sax.ContentHandler;
   42   import org.xml.sax.InputSource;
   43   import org.xml.sax.SAXException;
   44   import org.xml.sax.XMLReader;
   45   import org.xml.sax.ext.LexicalHandler;
   46   
   47   import org.springframework.beans.factory.InitializingBean;
   48   import org.springframework.core.io.Resource;
   49   import org.springframework.oxm.AbstractMarshaller;
   50   import org.springframework.oxm.XmlMappingException;
   51   import org.springframework.util.ObjectUtils;
   52   import org.springframework.util.StringUtils;
   53   import org.springframework.xml.dom.DomContentHandler;
   54   import org.springframework.xml.sax.SaxUtils;
   55   import org.springframework.xml.stream.StaxEventContentHandler;
   56   import org.springframework.xml.stream.StaxEventXmlReader;
   57   import org.springframework.xml.stream.StaxStreamContentHandler;
   58   import org.springframework.xml.stream.StaxStreamXmlReader;
   59   
   60   /**
   61    * Implementation of the <code>Marshaller</code> interface for Castor. By default, Castor does not require any further
   62    * configuration, though setting a target class or providing a mapping file can be used to have more control over the
   63    * behavior of Castor.
   64    * <p/>
   65    * If a target class is specified using <code>setTargetClass</code>, the <code>CastorMarshaller</code> can only be used
   66    * to unmarshall XML that represents that specific class. If you want to unmarshall multiple classes, you have to
   67    * provide a mapping file using <code>setMappingLocations</code>.
   68    * <p/>
   69    * Due to Castor's API, it is required to set the encoding used for writing to output streams. It defaults to
   70    * <code>UTF-8</code>.
   71    *
   72    * @author Arjen Poutsma
   73    * @see #setEncoding(String)
   74    * @see #setTargetClass(Class)
   75    * @see #setMappingLocation(org.springframework.core.io.Resource)
   76    * @see #setMappingLocations(org.springframework.core.io.Resource[])
   77    * @since 1.0.0
   78    */
   79   public class CastorMarshaller extends AbstractMarshaller implements InitializingBean {
   80   
   81       /** The default encoding used for stream access. */
   82       public static final String DEFAULT_ENCODING = "UTF-8";
   83   
   84       private Resource[] mappingLocations;
   85   
   86       private String encoding = DEFAULT_ENCODING;
   87   
   88       private Class targetClass;
   89   
   90       private XMLContext xmlContext;
   91   
   92       private boolean validating = false;
   93   
   94       private boolean whitespacePreserve = false;
   95   
   96       private boolean ignoreExtraAttributes = true;
   97   
   98       private boolean ignoreExtraElements = false;
   99   
  100       private Properties namespaceMappings;
  101   
  102       private boolean suppressNamespaces = false;
  103   
  104       private boolean suppressXsiType = false;
  105   
  106       /** Returns whether the Castor  {@link Unmarshaller} should ignore attributes that do not match a specific field. */
  107       public boolean getIgnoreExtraAttributes() {
  108           return ignoreExtraAttributes;
  109       }
  110   
  111       /**
  112        * Sets whether the Castor  {@link Unmarshaller} should ignore attributes that do not match a specific field.
  113        * Default is <code>true</code>: extra attributes are ignored.
  114        *
  115        * @see org.exolab.castor.xml.Unmarshaller#setIgnoreExtraAttributes(boolean)
  116        */
  117       public void setIgnoreExtraAttributes(boolean ignoreExtraAttributes) {
  118           this.ignoreExtraAttributes = ignoreExtraAttributes;
  119       }
  120   
  121       /** Returns whether the Castor  {@link Unmarshaller} should ignore elements that do not match a specific field. */
  122       public boolean getIgnoreExtraElements() {
  123           return ignoreExtraElements;
  124       }
  125   
  126       /**
  127        * Sets whether the Castor  {@link Unmarshaller} should ignore elements that do not match a specific field. Default
  128        * is <code>false</code>, extra attributes are flagged as an error.
  129        *
  130        * @see org.exolab.castor.xml.Unmarshaller#setIgnoreExtraElements(boolean)
  131        */
  132       public void setIgnoreExtraElements(boolean ignoreExtraElements) {
  133           this.ignoreExtraElements = ignoreExtraElements;
  134       }
  135   
  136       /** Returns whether the Castor {@link Unmarshaller} should preserve "ignorable" whitespace. */
  137       public boolean getWhitespacePreserve() {
  138           return whitespacePreserve;
  139       }
  140   
  141       /**
  142        * Sets whether the Castor {@link Unmarshaller} should preserve "ignorable" whitespace. Default is
  143        * <code>false</code>.
  144        *
  145        * @see org.exolab.castor.xml.Unmarshaller#setWhitespacePreserve(boolean)
  146        */
  147       public void setWhitespacePreserve(boolean whitespacePreserve) {
  148           this.whitespacePreserve = whitespacePreserve;
  149       }
  150   
  151       /** Returns whether this marshaller should validate in- and outgoing documents. */
  152       public boolean isValidating() {
  153           return validating;
  154       }
  155   
  156       /**
  157        * Sets whether this marshaller should validate in- and outgoing documents. Default is <code>false</code>.
  158        *
  159        * @see Marshaller#setValidation(boolean)
  160        */
  161       public void setValidating(boolean validating) {
  162           this.validating = validating;
  163       }
  164   
  165       /** Returns the namespace mappings. Property names are interpreted as namespace prefixes; values are namespace URIs. */
  166       public Properties getNamespaceMappings() {
  167           return namespaceMappings;
  168       }
  169   
  170       /**
  171        * Sets the namespace mappings. Property names are interpreted as namespace prefixes; values are namespace URIs.
  172        *
  173        * @see org.exolab.castor.xml.Marshaller#setNamespaceMapping(String, String)
  174        */
  175       public void setNamespaceMappings(Properties namespaceMappings) {
  176           this.namespaceMappings = namespaceMappings;
  177       }
  178   
  179       /**
  180        * Sets the encoding to be used for stream access. If this property is not set, the default encoding is used.
  181        *
  182        * @see #DEFAULT_ENCODING
  183        */
  184       public void setEncoding(String encoding) {
  185           this.encoding = encoding;
  186       }
  187   
  188       /** Sets the locations of the Castor XML Mapping files. */
  189       public void setMappingLocation(Resource mappingLocation) {
  190           mappingLocations = new Resource[]{mappingLocation};
  191       }
  192   
  193       /** Sets the locations of the Castor XML Mapping files. */
  194       public void setMappingLocations(Resource[] mappingLocations) {
  195           this.mappingLocations = mappingLocations;
  196       }
  197   
  198       /** Returns whether this marshaller should output namespaces. */
  199       public boolean isSuppressNamespaces() {
  200           return suppressNamespaces;
  201       }
  202   
  203       /**
  204        * Sets whether this marshaller should output namespaces. The default is {@code false}, i.e. namespaces are
  205        * written.
  206        *
  207        * @see org.exolab.castor.xml.Marshaller#setSuppressNamespaces(boolean)
  208        */
  209       public void setSuppressNamespaces(boolean suppressNamespaces) {
  210           this.suppressNamespaces = suppressNamespaces;
  211       }
  212   
  213       /** Sets whether this marshaller should output the xsi:type attribute. */
  214       public boolean isSuppressXsiType() {
  215           return suppressXsiType;
  216       }
  217   
  218       /**
  219        * Sets whether this marshaller should output the {@code xsi:type} attribute. The default is {@code false}, i.e. the
  220        * {@code xsi:type} is written.
  221        *
  222        * @see org.exolab.castor.xml.Marshaller#setSuppressXSIType(boolean)
  223        */
  224       public void setSuppressXsiType(boolean suppressXsiType) {
  225           this.suppressXsiType = suppressXsiType;
  226       }
  227   
  228       /**
  229        * Sets the Castor target class. If this property is set, this <code>CastorMarshaller</code> is tied to this one
  230        * specific class. Use a mapping file for unmarshalling multiple classes.
  231        */
  232       public void setTargetClass(Class targetClass) {
  233           this.targetClass = targetClass;
  234       }
  235   
  236       public final void afterPropertiesSet() throws IOException {
  237           if (mappingLocations != null && targetClass != null) {
  238               throw new IllegalArgumentException("Cannot set both the 'mappingLocations' and 'targetClass' property. " +
  239                       "Set targetClass for unmarshalling a single class, and 'mappingLocations' for multiple classes'");
  240           }
  241           if (logger.isInfoEnabled()) {
  242               if (mappingLocations != null) {
  243                   logger.info("Configured using " + StringUtils.arrayToCommaDelimitedString(mappingLocations));
  244               }
  245               else if (targetClass != null) {
  246                   logger.info("Configured for target class [" + targetClass.getName() + "]");
  247               }
  248               else {
  249                   logger.info("Using default configuration");
  250               }
  251           }
  252           try {
  253               xmlContext = createXMLContext(mappingLocations, targetClass);
  254           }
  255           catch (MappingException ex) {
  256               throw new CastorSystemException("Could not load Castor mapping: " + ex.getMessage(), ex);
  257           }
  258           catch (ResolverException rex) {
  259               throw new CastorSystemException("Could not load Castor mapping: " + rex.getMessage(), rex);
  260           }
  261       }
  262   
  263       /** Returns <code>true</code> for all classes, i.e. Castor supports arbitrary classes. */
  264       public boolean supports(Class clazz) {
  265           return true;
  266       }
  267   
  268       /**
  269        * Creates the Castor <code>XMLContext</code>. Subclasses can override this to create a custom context.
  270        * <p/>
  271        * The default implementation loads mapping files if defined, and the target class if not defined.
  272        *
  273        * @return the created resolver
  274        * @throws MappingException when the mapping file cannot be loaded
  275        * @throws IOException      in case of I/O errors
  276        * @see XMLContext#addMapping(org.exolab.castor.mapping.Mapping)
  277        * @see XMLContext#addClass(Class)
  278        */
  279       protected XMLContext createXMLContext(Resource[] mappingLocations, Class targetClass)
  280               throws MappingException, IOException, ResolverException {
  281           XMLContext context = new XMLContext();
  282           if (!ObjectUtils.isEmpty(mappingLocations)) {
  283               Mapping mapping = new Mapping();
  284               for (int i = 0; i < mappingLocations.length; i++) {
  285                   mapping.loadMapping(SaxUtils.createInputSource(mappingLocations[i]));
  286               }
  287               context.addMapping(mapping);
  288           }
  289           if (targetClass != null) {
  290               context.addClass(targetClass);
  291           }
  292           return context;
  293       }
  294   
  295       //
  296       // Marshalling
  297       //
  298   
  299       protected final void marshalDomNode(Object graph, Node node) throws XmlMappingException {
  300           marshalSaxHandlers(graph, new DomContentHandler(node), null);
  301       }
  302   
  303       protected final void marshalSaxHandlers(Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler)
  304               throws XmlMappingException {
  305           Marshaller marshaller = xmlContext.createMarshaller();
  306           marshaller.setContentHandler(contentHandler);
  307           marshal(graph, marshaller);
  308       }
  309   
  310       protected final void marshalOutputStream(Object graph, OutputStream outputStream)
  311               throws XmlMappingException, IOException {
  312           marshalWriter(graph, new OutputStreamWriter(outputStream, encoding));
  313       }
  314   
  315       protected final void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
  316           Marshaller marshaller = xmlContext.createMarshaller();
  317           marshaller.setWriter(writer);
  318           marshal(graph, marshaller);
  319       }
  320   
  321       protected final void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException {
  322           marshalSaxHandlers(graph, new StaxEventContentHandler(eventWriter), null);
  323       }
  324   
  325       protected final void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException {
  326           marshalSaxHandlers(graph, new StaxStreamContentHandler(streamWriter), null);
  327       }
  328   
  329       private void marshal(Object graph, Marshaller marshaller) {
  330           try {
  331               customizeMarshaller(marshaller);
  332               marshaller.marshal(graph);
  333           }
  334           catch (XMLException ex) {
  335               throw convertCastorException(ex, true);
  336           }
  337       }
  338   
  339       /**
  340        * Template method that allows for customizing of the given Castor {@link Marshaller}.
  341        * <p/>
  342        * Default implementation invokes {@link Marshaller#setValidation(boolean)} with the property set on this
  343        * marshaller, and calls {@link Marshaller#setNamespaceMapping(String, String)} with the {@linkplain
  344        * #setNamespaceMappings(java.util.Properties) namespace mappings}.
  345        */
  346       protected void customizeMarshaller(Marshaller marshaller) {
  347           marshaller.setValidation(isValidating());
  348           marshaller.setSuppressNamespaces(isSuppressNamespaces());
  349           marshaller.setSuppressXSIType(isSuppressXsiType());
  350           Properties namespaceMappings = getNamespaceMappings();
  351           if (namespaceMappings != null) {
  352               for (Iterator iterator = namespaceMappings.keySet().iterator(); iterator.hasNext();) {
  353                   String prefix = (String) iterator.next();
  354                   String uri = namespaceMappings.getProperty(prefix);
  355                   marshaller.setNamespaceMapping(prefix, uri);
  356               }
  357           }
  358       }
  359   
  360       //
  361       // Unmarshalling
  362       //
  363   
  364       protected final Object unmarshalDomNode(Node node) throws XmlMappingException {
  365           try {
  366               return createUnmarshaller().unmarshal(node);
  367           }
  368           catch (XMLException ex) {
  369               throw convertCastorException(ex, false);
  370           }
  371       }
  372   
  373       protected final Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException {
  374           try {
  375               return createUnmarshaller().unmarshal(new InputSource(inputStream));
  376           }
  377           catch (XMLException ex) {
  378               throw convertCastorException(ex, false);
  379           }
  380       }
  381   
  382       protected final Object unmarshalReader(Reader reader) throws XmlMappingException, IOException {
  383           try {
  384               return createUnmarshaller().unmarshal(new InputSource(reader));
  385           }
  386           catch (XMLException ex) {
  387               throw convertCastorException(ex, false);
  388           }
  389       }
  390   
  391       protected final Object unmarshalXmlEventReader(XMLEventReader eventReader) {
  392           XMLReader reader = new StaxEventXmlReader(eventReader);
  393           try {
  394               return unmarshalSaxReader(reader, new InputSource());
  395           }
  396           catch (IOException ex) {
  397               throw new CastorUnmarshallingFailureException(new MarshalException(ex));
  398           }
  399       }
  400   
  401       protected final Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
  402               throws XmlMappingException, IOException {
  403           UnmarshalHandler unmarshalHandler = createUnmarshaller().createHandler();
  404           try {
  405               ContentHandler contentHandler = Unmarshaller.getContentHandler(unmarshalHandler);
  406               xmlReader.setContentHandler(contentHandler);
  407               xmlReader.parse(inputSource);
  408               return unmarshalHandler.getObject();
  409           }
  410           catch (SAXException ex) {
  411               throw new CastorUnmarshallingFailureException(ex);
  412           }
  413       }
  414   
  415       protected final Object unmarshalXmlStreamReader(XMLStreamReader streamReader) {
  416           XMLReader reader = new StaxStreamXmlReader(streamReader);
  417           try {
  418               return unmarshalSaxReader(reader, new InputSource());
  419           }
  420           catch (IOException ex) {
  421               throw new CastorUnmarshallingFailureException(new MarshalException(ex));
  422           }
  423       }
  424   
  425       private Unmarshaller createUnmarshaller() {
  426           Unmarshaller unmarshaller = xmlContext.createUnmarshaller();
  427           if (targetClass != null) {
  428               unmarshaller.setClass(targetClass);
  429               unmarshaller.setClassLoader(targetClass.getClassLoader());
  430           }
  431           customizeUnmarshaller(unmarshaller);
  432           return unmarshaller;
  433       }
  434   
  435       /**
  436        * Template method that allows for customizing of the given Castor {@link Unmarshaller}.
  437        * <p/>
  438        * Default implementation invokes {@link Unmarshaller#setValidation(boolean)}, {@link
  439        * Unmarshaller#setWhitespacePreserve(boolean)}, {@link Unmarshaller#setIgnoreExtraAttributes(boolean)}, and {@link
  440        * Unmarshaller#setIgnoreExtraElements(boolean)} with the properties set on this marshaller.
  441        */
  442       protected void customizeUnmarshaller(Unmarshaller unmarshaller) {
  443           unmarshaller.setValidation(isValidating());
  444           unmarshaller.setWhitespacePreserve(getWhitespacePreserve());
  445           unmarshaller.setIgnoreExtraAttributes(getIgnoreExtraAttributes());
  446           unmarshaller.setIgnoreExtraElements(getIgnoreExtraElements());
  447       }
  448   
  449       /**
  450        * Converts the given <code>CastorException</code> to an appropriate exception from the
  451        * <code>org.springframework.oxm</code> hierarchy.
  452        * <p/>
  453        * The default implementation delegates to <code>CastorUtils</code>. Can be overridden in subclasses.
  454        * <p/>
  455        * A boolean flag is used to indicate whether this exception occurs during marshalling or unmarshalling, since
  456        * Castor itself does not make this distinction in its exception hierarchy.
  457        *
  458        * @param ex          Castor <code>XMLException</code> that occured
  459        * @param marshalling indicates whether the exception occurs during marshalling (<code>true</code>), or
  460        *                    unmarshalling (<code>false</code>)
  461        * @return the corresponding <code>XmlMappingException</code>
  462        * @see CastorUtils#convertXmlException
  463        */
  464       public XmlMappingException convertCastorException(XMLException ex, boolean marshalling) {
  465           return CastorUtils.convertXmlException(ex, marshalling);
  466       }
  467   }

Home » spring-ws-1.5.9-with-dependencies » org.springframework » oxm » castor » [javadoc | source]