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.util.concurrent.ConcurrentHashMap;
020import java.util.concurrent.ConcurrentMap;
021import javax.xml.bind.JAXBContext;
022import javax.xml.bind.JAXBException;
023import javax.xml.bind.Marshaller;
024import javax.xml.bind.Unmarshaller;
025
026import org.springframework.http.converter.HttpMessageConversionException;
027import org.springframework.util.Assert;
028
029/**
030 * Abstract base class for {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverters}
031 * that use JAXB2. Creates {@link JAXBContext} object lazily.
032 *
033 * @author Arjen Poutsma
034 * @author Rossen Stoyanchev
035 * @since 3.0
036 */
037public abstract class AbstractJaxb2HttpMessageConverter<T> extends AbstractXmlHttpMessageConverter<T> {
038
039        private final ConcurrentMap<Class<?>, JAXBContext> jaxbContexts = new ConcurrentHashMap<Class<?>, JAXBContext>(64);
040
041
042        /**
043         * Create a new {@link Marshaller} for the given class.
044         * @param clazz the class to create the marshaller for
045         * @return the {@code Marshaller}
046         * @throws HttpMessageConversionException in case of JAXB errors
047         */
048        protected final Marshaller createMarshaller(Class<?> clazz) {
049                try {
050                        JAXBContext jaxbContext = getJaxbContext(clazz);
051                        Marshaller marshaller = jaxbContext.createMarshaller();
052                        customizeMarshaller(marshaller);
053                        return marshaller;
054                }
055                catch (JAXBException ex) {
056                        throw new HttpMessageConversionException(
057                                        "Could not create Marshaller for class [" + clazz + "]: " + ex.getMessage(), ex);
058                }
059        }
060
061        /**
062         * Customize the {@link Marshaller} created by this
063         * message converter before using it to write the object to the output.
064         * @param marshaller the marshaller to customize
065         * @since 4.0.3
066         * @see #createMarshaller(Class)
067         */
068        protected void customizeMarshaller(Marshaller marshaller) {
069        }
070
071        /**
072         * Create a new {@link Unmarshaller} for the given class.
073         * @param clazz the class to create the unmarshaller for
074         * @return the {@code Unmarshaller}
075         * @throws HttpMessageConversionException in case of JAXB errors
076         */
077        protected final Unmarshaller createUnmarshaller(Class<?> clazz) {
078                try {
079                        JAXBContext jaxbContext = getJaxbContext(clazz);
080                        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
081                        customizeUnmarshaller(unmarshaller);
082                        return unmarshaller;
083                }
084                catch (JAXBException ex) {
085                        throw new HttpMessageConversionException(
086                                        "Could not create Unmarshaller for class [" + clazz + "]: " + ex.getMessage(), ex);
087                }
088        }
089
090        /**
091         * Customize the {@link Unmarshaller} created by this
092         * message converter before using it to read the object from the input.
093         * @param unmarshaller the unmarshaller to customize
094         * @since 4.0.3
095         * @see #createUnmarshaller(Class)
096         */
097        protected void customizeUnmarshaller(Unmarshaller unmarshaller) {
098        }
099
100        /**
101         * Return a {@link JAXBContext} for the given class.
102         * @param clazz the class to return the context for
103         * @return the {@code JAXBContext}
104         * @throws HttpMessageConversionException in case of JAXB errors
105         */
106        protected final JAXBContext getJaxbContext(Class<?> clazz) {
107                Assert.notNull(clazz, "Class must not be null");
108                JAXBContext jaxbContext = this.jaxbContexts.get(clazz);
109                if (jaxbContext == null) {
110                        try {
111                                jaxbContext = JAXBContext.newInstance(clazz);
112                                this.jaxbContexts.putIfAbsent(clazz, jaxbContext);
113                        }
114                        catch (JAXBException ex) {
115                                throw new HttpMessageConversionException(
116                                                "Could not instantiate JAXBContext for class [" + clazz + "]: " + ex.getMessage(), ex);
117                        }
118                }
119                return jaxbContext;
120        }
121
122}