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