001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.http.converter.xml; 018 019import java.io.IOException; 020 021import javax.xml.transform.Result; 022import javax.xml.transform.Source; 023import javax.xml.transform.TransformerException; 024import javax.xml.transform.TransformerFactory; 025import javax.xml.transform.stream.StreamResult; 026import javax.xml.transform.stream.StreamSource; 027 028import org.springframework.http.HttpHeaders; 029import org.springframework.http.HttpInputMessage; 030import org.springframework.http.HttpOutputMessage; 031import org.springframework.http.MediaType; 032import org.springframework.http.converter.AbstractHttpMessageConverter; 033import org.springframework.http.converter.HttpMessageConversionException; 034import org.springframework.http.converter.HttpMessageNotReadableException; 035import org.springframework.http.converter.HttpMessageNotWritableException; 036 037/** 038 * Abstract base class for {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverters} 039 * that convert from/to XML. 040 * 041 * <p>By default, subclasses of this converter support {@code text/xml}, {@code application/xml}, and {@code 042 * application/*-xml}. This can be overridden by setting the {@link #setSupportedMediaTypes(java.util.List) 043 * supportedMediaTypes} property. 044 * 045 * @author Arjen Poutsma 046 * @author Juergen Hoeller 047 * @since 3.0 048 * @param <T> the converted object type 049 */ 050public abstract class AbstractXmlHttpMessageConverter<T> extends AbstractHttpMessageConverter<T> { 051 052 private final TransformerFactory transformerFactory = TransformerFactory.newInstance(); 053 054 055 /** 056 * Protected constructor that sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} 057 * to {@code text/xml} and {@code application/xml}, and {@code application/*-xml}. 058 */ 059 protected AbstractXmlHttpMessageConverter() { 060 super(MediaType.APPLICATION_XML, MediaType.TEXT_XML, new MediaType("application", "*+xml")); 061 } 062 063 064 @Override 065 public final T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage) 066 throws IOException, HttpMessageNotReadableException { 067 068 try { 069 return readFromSource(clazz, inputMessage.getHeaders(), new StreamSource(inputMessage.getBody())); 070 } 071 catch (IOException | HttpMessageConversionException ex) { 072 throw ex; 073 } 074 catch (Exception ex) { 075 throw new HttpMessageNotReadableException("Could not unmarshal to [" + clazz + "]: " + ex.getMessage(), 076 ex, inputMessage); 077 } 078 } 079 080 @Override 081 protected final void writeInternal(T t, HttpOutputMessage outputMessage) 082 throws IOException, HttpMessageNotWritableException { 083 084 try { 085 writeToResult(t, outputMessage.getHeaders(), new StreamResult(outputMessage.getBody())); 086 } 087 catch (IOException | HttpMessageConversionException ex) { 088 throw ex; 089 } 090 catch (Exception ex) { 091 throw new HttpMessageNotWritableException("Could not marshal [" + t + "]: " + ex.getMessage(), ex); 092 } 093 } 094 095 /** 096 * Transforms the given {@code Source} to the {@code Result}. 097 * @param source the source to transform from 098 * @param result the result to transform to 099 * @throws TransformerException in case of transformation errors 100 */ 101 protected void transform(Source source, Result result) throws TransformerException { 102 this.transformerFactory.newTransformer().transform(source, result); 103 } 104 105 106 /** 107 * Abstract template method called from {@link #read(Class, HttpInputMessage)}. 108 * @param clazz the type of object to return 109 * @param headers the HTTP input headers 110 * @param source the HTTP input body 111 * @return the converted object 112 * @throws Exception in case of I/O or conversion errors 113 */ 114 protected abstract T readFromSource(Class<? extends T> clazz, HttpHeaders headers, Source source) throws Exception; 115 116 /** 117 * Abstract template method called from {@link #writeInternal(Object, HttpOutputMessage)}. 118 * @param t the object to write to the output message 119 * @param headers the HTTP output headers 120 * @param result the HTTP output body 121 * @throws Exception in case of I/O or conversion errors 122 */ 123 protected abstract void writeToResult(T t, HttpHeaders headers, Result result) throws Exception; 124 125}