SPR-5539: Add XML HttpMessageConverters
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2008 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.http.converter.xml;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.xml.transform.Result;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.AbstractHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConversionException;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverters} that
|
||||
* convert from/to XML.
|
||||
*
|
||||
* <p>By default, subclasses of this converter support {@code text/xml} and {@code application/xml}. This can be
|
||||
* overridden by setting the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractXmlHttpMessageConverter<T> extends AbstractHttpMessageConverter<T> {
|
||||
|
||||
private final TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
|
||||
/**
|
||||
* Protected constructor that sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} to {@code
|
||||
* text/xml} and {@code application/xml}.
|
||||
*/
|
||||
protected AbstractXmlHttpMessageConverter() {
|
||||
super(new MediaType("application", "xml"), new MediaType("text", "xml"));
|
||||
}
|
||||
|
||||
/** Invokes {@link #readFromSource(Class, HttpHeaders, Source)}. */
|
||||
public final T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException {
|
||||
return readFromSource(clazz, inputMessage.getHeaders(), new StreamSource(inputMessage.getBody()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method called from {@link #read(Class, HttpInputMessage)}.
|
||||
*
|
||||
* @param clazz the type of object to return
|
||||
* @param headers the HTTP input headers
|
||||
* @param source the HTTP input body
|
||||
* @return the converted object
|
||||
* @throws IOException in case of I/O errors
|
||||
* @throws org.springframework.http.converter.HttpMessageConversionException in case of conversion errors
|
||||
*/
|
||||
protected abstract T readFromSource(Class<T> clazz, HttpHeaders headers, Source source) throws IOException;
|
||||
|
||||
@Override
|
||||
protected final void writeToInternal(T t, HttpOutputMessage outputMessage) throws IOException {
|
||||
writeToResult(t, outputMessage.getHeaders(), new StreamResult(outputMessage.getBody()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method called from {@link #writeToInternal(Object, HttpOutputMessage)}.
|
||||
*
|
||||
* @param t the object to write to the output message
|
||||
* @param headers the HTTP output headers
|
||||
* @param result the HTTP output body
|
||||
* @throws IOException in case of I/O errors
|
||||
* @throws HttpMessageConversionException in case of conversion errors
|
||||
*/
|
||||
protected abstract void writeToResult(T t, HttpHeaders headers, Result result) throws IOException;
|
||||
|
||||
/**
|
||||
* Transforms the given {@code Source} to the {@code Result}.
|
||||
*
|
||||
* @param source the source to transform from
|
||||
* @param result the result to transform to
|
||||
* @throws HttpMessageConversionException in case of transformation errors
|
||||
*/
|
||||
protected void transform(Source source, Result result) {
|
||||
try {
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
transformer.transform(source, result);
|
||||
}
|
||||
catch (TransformerException ex) {
|
||||
throw new HttpMessageConversionException("Could not transform XML", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.springframework.http.converter.xml;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.xml.transform.Result;
|
||||
import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.oxm.Marshaller;
|
||||
import org.springframework.oxm.Unmarshaller;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverter} that can read
|
||||
* and write XML using Spring's {@link Marshaller} and {@link Unmarshaller} abstractions.
|
||||
*
|
||||
* <p>This converter requires a {@code Marshaller} and {@code Unmarshaller} before it can be used. These can be injected
|
||||
* by the {@linkplain #MarshallingHttpMessageConverter(Marshaller) constructor} or {@linkplain
|
||||
* #setMarshaller(Marshaller) bean properties}.
|
||||
*
|
||||
* <p>By default, this converter supports {@code text/xml} and {@code application/xml}. This can be overridden by
|
||||
* setting the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
*/
|
||||
|
||||
public class MarshallingHttpMessageConverter extends AbstractXmlHttpMessageConverter<Object>
|
||||
implements InitializingBean {
|
||||
|
||||
private Marshaller marshaller;
|
||||
|
||||
private Unmarshaller unmarshaller;
|
||||
|
||||
/**
|
||||
* Construct a new {@code MarshallingHttpMessageConverter} with no {@link Marshaller} or {@link Unmarshaller} set. The
|
||||
* marshaller and unmarshaller must be set after construction by invoking {@link #setMarshaller(Marshaller)} and {@link
|
||||
* #setUnmarshaller(Unmarshaller)} .
|
||||
*/
|
||||
public MarshallingHttpMessageConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code MarshallingMessageConverter} with the given {@link Marshaller} set. <p>If the given {@link
|
||||
* Marshaller} also implements the {@link Unmarshaller} interface, it is used for both marshalling and unmarshalling.
|
||||
* Otherwise, an exception is thrown. <p>Note that all {@code Marshaller} implementations in Spring also implement the
|
||||
* {@code Unmarshaller} interface, so that you can safely use this constructor.
|
||||
*
|
||||
* @param marshaller object used as marshaller and unmarshaller
|
||||
* @throws IllegalArgumentException when <code>marshaller</code> does not implement the {@link Unmarshaller} interface
|
||||
* as well
|
||||
*/
|
||||
public MarshallingHttpMessageConverter(Marshaller marshaller) {
|
||||
Assert.notNull(marshaller, "marshaller must not be null");
|
||||
if (!(marshaller instanceof Unmarshaller)) {
|
||||
throw new IllegalArgumentException("Marshaller [" + marshaller + "] does not implement the Unmarshaller " +
|
||||
"interface. Please set an Unmarshaller explicitely by using the " +
|
||||
"MarshallingHttpMessageConverter(Marshaller, Unmarshaller) constructor.");
|
||||
}
|
||||
else {
|
||||
this.marshaller = marshaller;
|
||||
this.unmarshaller = (Unmarshaller) marshaller;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new <code>MarshallingMessageConverter</code> with the given {@code Marshaller} and {@code
|
||||
* Unmarshaller}.
|
||||
*
|
||||
* @param marshaller the Marshaller to use
|
||||
* @param unmarshaller the Unmarshaller to use
|
||||
*/
|
||||
public MarshallingHttpMessageConverter(Marshaller marshaller, Unmarshaller unmarshaller) {
|
||||
Assert.notNull(marshaller, "marshaller must not be null");
|
||||
Assert.notNull(unmarshaller, "unmarshaller must not be null");
|
||||
this.marshaller = marshaller;
|
||||
this.unmarshaller = unmarshaller;
|
||||
}
|
||||
|
||||
/** Set the {@link Marshaller} to be used by this message converter. */
|
||||
public void setMarshaller(Marshaller marshaller) {
|
||||
this.marshaller = marshaller;
|
||||
}
|
||||
|
||||
/** Set the {@link Unmarshaller} to be used by this message converter. */
|
||||
public void setUnmarshaller(Unmarshaller unmarshaller) {
|
||||
this.unmarshaller = unmarshaller;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.marshaller, "Property 'marshaller' is required");
|
||||
Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required");
|
||||
}
|
||||
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return unmarshaller.supports(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object readFromSource(Class<Object> clazz, HttpHeaders headers, Source source) throws IOException {
|
||||
return unmarshaller.unmarshal(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeToResult(Object o, HttpHeaders headers, Result result) throws IOException {
|
||||
marshaller.marshal(o, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.springframework.http.converter.xml;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import javax.xml.transform.Result;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.dom.DOMResult;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.sax.SAXSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.converter.HttpMessageConversionException;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
public class SourceHttpMessageConverter<T extends Source> extends AbstractXmlHttpMessageConverter<T> {
|
||||
|
||||
public boolean supports(Class<? extends T> clazz) {
|
||||
return Source.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T readFromSource(Class<T> clazz, HttpHeaders headers, Source source) throws IOException {
|
||||
if (DOMSource.class.equals(clazz)) {
|
||||
DOMResult domResult = new DOMResult();
|
||||
transform(source, domResult);
|
||||
return (T) new DOMSource(domResult.getNode());
|
||||
}
|
||||
else if (SAXSource.class.equals(clazz)) {
|
||||
ByteArrayInputStream bis = transformToByteArray(source);
|
||||
return (T) new SAXSource(new InputSource(bis));
|
||||
}
|
||||
else if (StreamSource.class.equals(clazz) || Source.class.equals(clazz)) {
|
||||
ByteArrayInputStream bis = transformToByteArray(source);
|
||||
return (T) new StreamSource(bis);
|
||||
}
|
||||
else {
|
||||
throw new HttpMessageConversionException(
|
||||
"Could not read class [" + clazz + "]. Only DOMSource, SAXSource, and StreamSource are supported.");
|
||||
}
|
||||
}
|
||||
|
||||
private ByteArrayInputStream transformToByteArray(Source source) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
transform(source, new StreamResult(bos));
|
||||
return new ByteArrayInputStream(bos.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeToResult(T t, HttpHeaders headers, Result result) throws IOException {
|
||||
transform(t, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Provides a HttpMessageConverter implementations for handling XML.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user