提交 ca01cb4d 编写于 作者: A Arjen Poutsma

SPR-5539: Add XML HttpMessageConverters

上级 2a790ad2
......@@ -59,6 +59,8 @@
conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration"
conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.oxm" rev="latest.integration"
conf="compile->compile"/>
<!-- test dependencies -->
<dependency org="org.apache.taglibs" name="com.springsource.org.apache.taglibs.standard" rev="1.1.2"
conf="test->runtime"/>
......@@ -67,6 +69,8 @@
<dependency org="org.easymock" name="com.springsource.org.easymock" rev="2.3.0" conf="test->compile"/>
<dependency org="org.mortbay.jetty" name="com.springsource.org.mortbay.jetty.server" rev="6.1.9"
conf="test->compile"/>
<dependency org="org.custommonkey.xmlunit" name="com.springsource.org.custommonkey.xmlunit" rev="1.2.0"
conf="test->compile"/>
</dependencies>
</ivy-module>
/*
* 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);
}
}
}
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);
}
}
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);
}
}
<html>
<body>
Provides a HttpMessageConverter implementations for handling XML.
</body>
</html>
package org.springframework.http.converter.xml;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.http.MockHttpInputMessage;
import org.springframework.http.MockHttpOutputMessage;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
/** @author Arjen Poutsma */
public class MarshallingHttpMessageConverterTest {
private MarshallingHttpMessageConverter converter;
private Marshaller marshaller;
private Unmarshaller unmarshaller;
@Before
public void setUp() {
marshaller = createMock(Marshaller.class);
unmarshaller = createMock(Unmarshaller.class);
converter = new MarshallingHttpMessageConverter(marshaller, unmarshaller);
}
@Test
public void read() throws Exception {
String body = "<root>Hello World</root>";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
expect(unmarshaller.unmarshal(isA(StreamSource.class))).andReturn(body);
replay(marshaller, unmarshaller);
String result = (String) converter.read(Object.class, inputMessage);
assertEquals("Invalid result", body, result);
verify(marshaller, unmarshaller);
}
@Test
public void write() throws Exception {
String body = "<root>Hello World</root>";
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
marshaller.marshal(eq(body), isA(StreamResult.class));
replay(marshaller, unmarshaller);
converter.write(body, outputMessage);
assertEquals("Invalid content-type", new MediaType("application", "xml"),
outputMessage.getHeaders().getContentType());
verify(marshaller, unmarshaller);
}
}
package org.springframework.http.converter.xml;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import static org.custommonkey.xmlunit.XMLAssert.*;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.springframework.http.MediaType;
import org.springframework.http.MockHttpInputMessage;
import org.springframework.http.MockHttpOutputMessage;
import org.springframework.util.FileCopyUtils;
/** @author Arjen Poutsma */
public class SourceHttpMessageConverterTest {
@Test
public void readDOMSource() throws Exception {
SourceHttpMessageConverter<DOMSource> converter = new SourceHttpMessageConverter<DOMSource>();
String body = "<root>Hello World</root>";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
inputMessage.getHeaders().setContentType(new MediaType("application", "xml"));
DOMSource result = converter.read(DOMSource.class, inputMessage);
Document document = (Document) result.getNode();
assertEquals("Invalid result", "root", document.getDocumentElement().getLocalName());
}
@Test
public void readSAXSource() throws Exception {
SourceHttpMessageConverter<SAXSource> converter = new SourceHttpMessageConverter<SAXSource>();
String body = "<root>Hello World</root>";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
inputMessage.getHeaders().setContentType(new MediaType("application", "xml"));
SAXSource result = converter.read(SAXSource.class, inputMessage);
InputSource inputSource = result.getInputSource();
String s = FileCopyUtils.copyToString(new InputStreamReader(inputSource.getByteStream()));
assertXMLEqual("Invalid result", body, s);
}
@Test
public void readStreamSource() throws Exception {
SourceHttpMessageConverter<StreamSource> converter = new SourceHttpMessageConverter<StreamSource>();
String body = "<root>Hello World</root>";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
inputMessage.getHeaders().setContentType(new MediaType("application", "xml"));
StreamSource result = converter.read(StreamSource.class, inputMessage);
String s = FileCopyUtils.copyToString(new InputStreamReader(result.getInputStream()));
assertXMLEqual("Invalid result", body, s);
}
@Test
public void readSource() throws Exception {
SourceHttpMessageConverter<Source> converter = new SourceHttpMessageConverter<Source>();
String body = "<root>Hello World</root>";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
inputMessage.getHeaders().setContentType(new MediaType("application", "xml"));
converter.read(Source.class, inputMessage);
}
@Test
public void write() throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
Document document = documentBuilderFactory.newDocumentBuilder().newDocument();
Element rootElement = document.createElement("root");
document.appendChild(rootElement);
rootElement.setTextContent("Hello World");
DOMSource domSource = new DOMSource(document);
SourceHttpMessageConverter<Source> converter = new SourceHttpMessageConverter<Source>();
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
converter.write(domSource, outputMessage);
assertXMLEqual("Invalid result", "<root>Hello World</root>",
outputMessage.getBodyAsString(Charset.forName("UTF-8")));
assertEquals("Invalid content-type", new MediaType("application", "xml"),
outputMessage.getHeaders().getContentType());
}
}
......@@ -15,6 +15,7 @@
<orderEntry type="module" module-name="beans" />
<orderEntry type="module" module-name="context" />
<orderEntry type="module" module-name="core" />
<orderEntry type="module" module-name="oxm" />
<orderEntry type="module-library">
<library>
<CLASSES>
......@@ -222,6 +223,17 @@
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-1.2.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-sources-1.2.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
</component>
<component name="copyright">
<Base>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册