org.springframework.amqp.support.converter.SimpleMessageConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.amqp.support.converter.SimpleMessageConverter.java

Source

/*
 * Copyright 2002-2019 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
 *
 *      https://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.amqp.support.converter;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.utils.SerializationUtils;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.remoting.rmi.CodebaseAwareObjectInputStream;
import org.springframework.util.ClassUtils;

/**
 * Implementation of {@link MessageConverter} that can work with Strings, Serializable
 * instances, or byte arrays. The {@link #toMessage(Object, MessageProperties)} method
 * simply checks the type of the provided instance while the {@link #fromMessage(Message)}
 * method relies upon the {@link MessageProperties#getContentType() content-type} of the
 * provided Message.
 *
 * @author Mark Fisher
 * @author Oleg Zhurakousky
 * @author Gary Russell
 */
public class SimpleMessageConverter extends WhiteListDeserializingMessageConverter implements BeanClassLoaderAware {

    public static final String DEFAULT_CHARSET = "UTF-8";

    private volatile String defaultCharset = DEFAULT_CHARSET;

    private String codebaseUrl;

    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

    @Override
    public void setBeanClassLoader(ClassLoader beanClassLoader) {
        this.beanClassLoader = beanClassLoader;
    }

    /**
     * Set the codebase URL to download classes from if not found locally. Can consists of multiple URLs, separated by
     * spaces.
     * <p>
     * Follows RMI's codebase conventions for dynamic class download.
     *
     * @param codebaseUrl The codebase URL.
     *
     * @see org.springframework.remoting.rmi.CodebaseAwareObjectInputStream
     * @see java.rmi.server.RMIClassLoader
     */
    public void setCodebaseUrl(String codebaseUrl) {
        this.codebaseUrl = codebaseUrl;
    }

    /**
     * Specify the default charset to use when converting to or from text-based
     * Message body content. If not specified, the charset will be "UTF-8".
     *
     * @param defaultCharset The default charset.
     */
    public void setDefaultCharset(String defaultCharset) {
        this.defaultCharset = (defaultCharset != null) ? defaultCharset : DEFAULT_CHARSET;
    }

    /**
     * Converts from a AMQP Message to an Object.
     */
    @Override
    public Object fromMessage(Message message) throws MessageConversionException {
        Object content = null;
        MessageProperties properties = message.getMessageProperties();
        if (properties != null) {
            String contentType = properties.getContentType();
            if (contentType != null && contentType.startsWith("text")) {
                String encoding = properties.getContentEncoding();
                if (encoding == null) {
                    encoding = this.defaultCharset;
                }
                try {
                    content = new String(message.getBody(), encoding);
                } catch (UnsupportedEncodingException e) {
                    throw new MessageConversionException("failed to convert text-based Message content", e);
                }
            } else if (contentType != null
                    && contentType.equals(MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT)) {
                try {
                    content = SerializationUtils.deserialize(
                            createObjectInputStream(new ByteArrayInputStream(message.getBody()), this.codebaseUrl));
                } catch (IOException | IllegalArgumentException | IllegalStateException e) {
                    throw new MessageConversionException("failed to convert serialized Message content", e);
                }
            }
        }
        if (content == null) {
            content = message.getBody();
        }
        return content;
    }

    /**
     * Creates an AMQP Message from the provided Object.
     */
    @Override
    protected Message createMessage(Object object, MessageProperties messageProperties)
            throws MessageConversionException {
        byte[] bytes = null;
        if (object instanceof byte[]) {
            bytes = (byte[]) object;
            messageProperties.setContentType(MessageProperties.CONTENT_TYPE_BYTES);
        } else if (object instanceof String) {
            try {
                bytes = ((String) object).getBytes(this.defaultCharset);
            } catch (UnsupportedEncodingException e) {
                throw new MessageConversionException("failed to convert to Message content", e);
            }
            messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
            messageProperties.setContentEncoding(this.defaultCharset);
        } else if (object instanceof Serializable) {
            try {
                bytes = SerializationUtils.serialize(object);
            } catch (IllegalArgumentException e) {
                throw new MessageConversionException("failed to convert to serialized Message content", e);
            }
            messageProperties.setContentType(MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT);
        }
        if (bytes != null) {
            messageProperties.setContentLength(bytes.length);
            return new Message(bytes, messageProperties);
        }
        throw new IllegalArgumentException(
                getClass().getSimpleName() + " only supports String, byte[] and Serializable payloads, received: "
                        + object.getClass().getName());
    }

    /**
     * Create an ObjectInputStream for the given InputStream and codebase. The default implementation creates a
     * CodebaseAwareObjectInputStream.
     * @param is the InputStream to read from
     * @param codebaseUrl the codebase URL to load classes from if not found locally (can be <code>null</code>)
     * @return the new ObjectInputStream instance to use
     * @throws IOException if creation of the ObjectInputStream failed
     * @see org.springframework.remoting.rmi.CodebaseAwareObjectInputStream
     */
    protected ObjectInputStream createObjectInputStream(InputStream is, String codebaseUrl) throws IOException {
        return new CodebaseAwareObjectInputStream(is, this.beanClassLoader, codebaseUrl) {

            @Override
            protected Class<?> resolveClass(ObjectStreamClass classDesc)
                    throws IOException, ClassNotFoundException {
                Class<?> clazz = super.resolveClass(classDesc);
                checkWhiteList(clazz);
                return clazz;
            }

        };
    }

}