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.messaging.support;
018
019import java.io.Serializable;
020import java.util.Map;
021
022import org.springframework.lang.Nullable;
023import org.springframework.messaging.Message;
024import org.springframework.messaging.MessageHeaders;
025import org.springframework.util.Assert;
026import org.springframework.util.ObjectUtils;
027
028/**
029 * An implementation of {@link Message} with a generic payload.
030 * Once created, a GenericMessage is immutable.
031 *
032 * @author Mark Fisher
033 * @since 4.0
034 * @param <T> the payload type
035 * @see MessageBuilder
036 */
037public class GenericMessage<T> implements Message<T>, Serializable {
038
039        private static final long serialVersionUID = 4268801052358035098L;
040
041
042        private final T payload;
043
044        private final MessageHeaders headers;
045
046
047        /**
048         * Create a new message with the given payload.
049         * @param payload the message payload (never {@code null})
050         */
051        public GenericMessage(T payload) {
052                this(payload, new MessageHeaders(null));
053        }
054
055        /**
056         * Create a new message with the given payload and headers.
057         * The content of the given header map is copied.
058         * @param payload the message payload (never {@code null})
059         * @param headers message headers to use for initialization
060         */
061        public GenericMessage(T payload, Map<String, Object> headers) {
062                this(payload, new MessageHeaders(headers));
063        }
064
065        /**
066         * A constructor with the {@link MessageHeaders} instance to use.
067         * <p><strong>Note:</strong> the given {@code MessageHeaders} instance is used
068         * directly in the new message, i.e. it is not copied.
069         * @param payload the message payload (never {@code null})
070         * @param headers message headers
071         */
072        public GenericMessage(T payload, MessageHeaders headers) {
073                Assert.notNull(payload, "Payload must not be null");
074                Assert.notNull(headers, "MessageHeaders must not be null");
075                this.payload = payload;
076                this.headers = headers;
077        }
078
079
080        @Override
081        public T getPayload() {
082                return this.payload;
083        }
084
085        @Override
086        public MessageHeaders getHeaders() {
087                return this.headers;
088        }
089
090
091        @Override
092        public boolean equals(@Nullable Object other) {
093                if (this == other) {
094                        return true;
095                }
096                if (!(other instanceof GenericMessage)) {
097                        return false;
098                }
099                GenericMessage<?> otherMsg = (GenericMessage<?>) other;
100                // Using nullSafeEquals for proper array equals comparisons
101                return (ObjectUtils.nullSafeEquals(this.payload, otherMsg.payload) && this.headers.equals(otherMsg.headers));
102        }
103
104        @Override
105        public int hashCode() {
106                // Using nullSafeHashCode for proper array hashCode handling
107                return (ObjectUtils.nullSafeHashCode(this.payload) * 23 + this.headers.hashCode());
108        }
109
110        @Override
111        public String toString() {
112                StringBuilder sb = new StringBuilder(getClass().getSimpleName());
113                sb.append(" [payload=");
114                if (this.payload instanceof byte[]) {
115                        sb.append("byte[").append(((byte[]) this.payload).length).append("]");
116                }
117                else {
118                        sb.append(this.payload);
119                }
120                sb.append(", headers=").append(this.headers).append("]");
121                return sb.toString();
122        }
123
124}