001/*
002 * Copyright 2002-2016 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;
020import javax.xml.transform.Result;
021import javax.xml.transform.Source;
022
023import org.springframework.beans.TypeMismatchException;
024import org.springframework.http.HttpHeaders;
025import org.springframework.http.MediaType;
026import org.springframework.http.converter.HttpMessageNotReadableException;
027import org.springframework.http.converter.HttpMessageNotWritableException;
028import org.springframework.oxm.Marshaller;
029import org.springframework.oxm.MarshallingFailureException;
030import org.springframework.oxm.Unmarshaller;
031import org.springframework.oxm.UnmarshallingFailureException;
032import org.springframework.util.Assert;
033
034/**
035 * Implementation of {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverter}
036 * that can read and write XML using Spring's {@link Marshaller} and {@link Unmarshaller} abstractions.
037 *
038 * <p>This converter requires a {@code Marshaller} and {@code Unmarshaller} before it can be used.
039 * These can be injected by the {@linkplain #MarshallingHttpMessageConverter(Marshaller) constructor}
040 * or {@linkplain #setMarshaller(Marshaller) bean properties}.
041 *
042 * <p>By default, this converter supports {@code text/xml} and {@code application/xml}. This can be
043 * overridden by setting the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
044 *
045 * @author Arjen Poutsma
046 * @since 3.0
047 */
048public class MarshallingHttpMessageConverter extends AbstractXmlHttpMessageConverter<Object> {
049
050        private Marshaller marshaller;
051
052        private Unmarshaller unmarshaller;
053
054
055        /**
056         * Construct a new {@code MarshallingHttpMessageConverter} with no {@link Marshaller} or
057         * {@link Unmarshaller} set. The Marshaller and Unmarshaller must be set after construction
058         * by invoking {@link #setMarshaller(Marshaller)} and {@link #setUnmarshaller(Unmarshaller)}.
059         */
060        public MarshallingHttpMessageConverter() {
061        }
062
063        /**
064         * Construct a new {@code MarshallingMessageConverter} with the given {@link Marshaller} set.
065         * <p>If the given {@link Marshaller} also implements the {@link Unmarshaller} interface,
066         * it is used for both marshalling and unmarshalling. Otherwise, an exception is thrown.
067         * <p>Note that all {@code Marshaller} implementations in Spring also implement the
068         * {@code Unmarshaller} interface, so that you can safely use this constructor.
069         * @param marshaller object used as marshaller and unmarshaller
070         */
071        public MarshallingHttpMessageConverter(Marshaller marshaller) {
072                Assert.notNull(marshaller, "Marshaller must not be null");
073                this.marshaller = marshaller;
074                if (marshaller instanceof Unmarshaller) {
075                        this.unmarshaller = (Unmarshaller) marshaller;
076                }
077        }
078
079        /**
080         * Construct a new {@code MarshallingMessageConverter} with the given
081         * {@code Marshaller} and {@code Unmarshaller}.
082         * @param marshaller the Marshaller to use
083         * @param unmarshaller the Unmarshaller to use
084         */
085        public MarshallingHttpMessageConverter(Marshaller marshaller, Unmarshaller unmarshaller) {
086                Assert.notNull(marshaller, "Marshaller must not be null");
087                Assert.notNull(unmarshaller, "Unmarshaller must not be null");
088                this.marshaller = marshaller;
089                this.unmarshaller = unmarshaller;
090        }
091
092
093        /**
094         * Set the {@link Marshaller} to be used by this message converter.
095         */
096        public void setMarshaller(Marshaller marshaller) {
097                this.marshaller = marshaller;
098        }
099
100        /**
101         * Set the {@link Unmarshaller} to be used by this message converter.
102         */
103        public void setUnmarshaller(Unmarshaller unmarshaller) {
104                this.unmarshaller = unmarshaller;
105        }
106
107
108        @Override
109        public boolean canRead(Class<?> clazz, MediaType mediaType) {
110                return (canRead(mediaType) && this.unmarshaller != null && this.unmarshaller.supports(clazz));
111        }
112
113        @Override
114        public boolean canWrite(Class<?> clazz, MediaType mediaType) {
115                return (canWrite(mediaType) && this.marshaller != null && this.marshaller.supports(clazz));
116        }
117
118        @Override
119        protected boolean supports(Class<?> clazz) {
120                // should not be called, since we override canRead()/canWrite()
121                throw new UnsupportedOperationException();
122        }
123
124        @Override
125        protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws IOException {
126                Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required");
127                try {
128                        Object result = this.unmarshaller.unmarshal(source);
129                        if (!clazz.isInstance(result)) {
130                                throw new TypeMismatchException(result, clazz);
131                        }
132                        return result;
133                }
134                catch (UnmarshallingFailureException ex) {
135                        throw new HttpMessageNotReadableException("Could not read [" + clazz + "]", ex);
136                }
137        }
138
139        @Override
140        protected void writeToResult(Object o, HttpHeaders headers, Result result) throws IOException {
141                Assert.notNull(this.marshaller, "Property 'marshaller' is required");
142                try {
143                        this.marshaller.marshal(o, result);
144                }
145                catch (MarshallingFailureException ex) {
146                        throw new HttpMessageNotWritableException("Could not write [" + o + "]", ex);
147                }
148        }
149}