001/*
002 * Copyright 2002-2019 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.jms.support;
018
019import java.util.Arrays;
020import java.util.Enumeration;
021import java.util.HashMap;
022import java.util.HashSet;
023import java.util.Map;
024import java.util.Set;
025
026import javax.jms.Destination;
027import javax.jms.JMSException;
028import javax.jms.Message;
029
030import org.springframework.messaging.MessageHeaders;
031import org.springframework.messaging.support.AbstractHeaderMapper;
032import org.springframework.util.StringUtils;
033
034/**
035 * Simple implementation of {@link JmsHeaderMapper}.
036 *
037 * <p>This implementation copies JMS API headers (e.g. JMSReplyTo) to and from
038 * {@link org.springframework.messaging.Message Messages}. Any user-defined
039 * properties will also be copied from a JMS Message to a Message, and any
040 * other headers on a Message (beyond the JMS API headers) will likewise
041 * be copied to a JMS Message. Those other headers will be copied to the
042 * general properties of a JMS Message whereas the JMS API headers are passed
043 * to the appropriate setter methods (e.g. setJMSReplyTo).
044 *
045 * <p>Constants for the JMS API headers are defined in {@link JmsHeaders}.
046 * Note that most of the JMS headers are read-only: the JMSDestination,
047 * JMSDeliveryMode, JMSExpiration, JMSMessageID, JMSPriority, JMSRedelivered
048 * and JMSTimestamp flags are only copied <em>from</em> a JMS Message. Those
049 * values will <em>not</em> be passed along from a Message to an outbound
050 * JMS Message.
051 *
052 * @author Mark Fisher
053 * @author Gary Russell
054 * @author Stephane Nicoll
055 * @since 4.1
056 */
057public class SimpleJmsHeaderMapper extends AbstractHeaderMapper<Message> implements JmsHeaderMapper {
058
059        private static final Set<Class<?>> SUPPORTED_PROPERTY_TYPES = new HashSet<>(Arrays.asList(
060                        Boolean.class, Byte.class, Double.class, Float.class, Integer.class, Long.class, Short.class, String.class));
061
062
063        @Override
064        public void fromHeaders(MessageHeaders headers, javax.jms.Message jmsMessage) {
065                try {
066                        Object jmsCorrelationId = headers.get(JmsHeaders.CORRELATION_ID);
067                        if (jmsCorrelationId instanceof Number) {
068                                jmsCorrelationId = jmsCorrelationId.toString();
069                        }
070                        if (jmsCorrelationId instanceof String) {
071                                try {
072                                        jmsMessage.setJMSCorrelationID((String) jmsCorrelationId);
073                                }
074                                catch (Exception ex) {
075                                        logger.debug("Failed to set JMSCorrelationID - skipping", ex);
076                                }
077                        }
078                        Destination jmsReplyTo = getHeaderIfAvailable(headers, JmsHeaders.REPLY_TO, Destination.class);
079                        if (jmsReplyTo != null) {
080                                try {
081                                        jmsMessage.setJMSReplyTo(jmsReplyTo);
082                                }
083                                catch (Exception ex) {
084                                        logger.debug("Failed to set JMSReplyTo - skipping", ex);
085                                }
086                        }
087                        String jmsType = getHeaderIfAvailable(headers, JmsHeaders.TYPE, String.class);
088                        if (jmsType != null) {
089                                try {
090                                        jmsMessage.setJMSType(jmsType);
091                                }
092                                catch (Exception ex) {
093                                        logger.debug("Failed to set JMSType - skipping", ex);
094                                }
095                        }
096                        Set<Map.Entry<String, Object>> entries = headers.entrySet();
097                        for (Map.Entry<String, Object> entry : entries) {
098                                String headerName = entry.getKey();
099                                if (StringUtils.hasText(headerName) && !headerName.startsWith(JmsHeaders.PREFIX)) {
100                                        Object value = entry.getValue();
101                                        if (value != null && SUPPORTED_PROPERTY_TYPES.contains(value.getClass())) {
102                                                try {
103                                                        String propertyName = this.fromHeaderName(headerName);
104                                                        jmsMessage.setObjectProperty(propertyName, value);
105                                                }
106                                                catch (Exception ex) {
107                                                        if (headerName.startsWith("JMSX")) {
108                                                                if (logger.isTraceEnabled()) {
109                                                                        logger.trace("Skipping reserved header '" + headerName +
110                                                                                        "' since it cannot be set by client");
111                                                                }
112                                                        }
113                                                        else if (logger.isDebugEnabled()) {
114                                                                logger.debug("Failed to map message header '" + headerName + "' to JMS property", ex);
115                                                        }
116                                                }
117                                        }
118                                }
119                        }
120                }
121                catch (Exception ex) {
122                        if (logger.isDebugEnabled()) {
123                                logger.debug("Error occurred while mapping from MessageHeaders to JMS properties", ex);
124                        }
125                }
126        }
127
128        @Override
129        public MessageHeaders toHeaders(javax.jms.Message jmsMessage) {
130                Map<String, Object> headers = new HashMap<>();
131                try {
132                        try {
133                                String correlationId = jmsMessage.getJMSCorrelationID();
134                                if (correlationId != null) {
135                                        headers.put(JmsHeaders.CORRELATION_ID, correlationId);
136                                }
137                        }
138                        catch (Exception ex) {
139                                logger.debug("Failed to read JMSCorrelationID property - skipping", ex);
140                        }
141                        try {
142                                Destination destination = jmsMessage.getJMSDestination();
143                                if (destination != null) {
144                                        headers.put(JmsHeaders.DESTINATION, destination);
145                                }
146                        }
147                        catch (Exception ex) {
148                                logger.debug("Failed to read JMSDestination property - skipping", ex);
149                        }
150                        try {
151                                int deliveryMode = jmsMessage.getJMSDeliveryMode();
152                                headers.put(JmsHeaders.DELIVERY_MODE, deliveryMode);
153                        }
154                        catch (Exception ex) {
155                                logger.debug("Failed to read JMSDeliveryMode property - skipping", ex);
156                        }
157                        try {
158                                long expiration = jmsMessage.getJMSExpiration();
159                                headers.put(JmsHeaders.EXPIRATION, expiration);
160                        }
161                        catch (Exception ex) {
162                                logger.debug("Failed to read JMSExpiration property - skipping", ex);
163                        }
164                        try {
165                                String messageId = jmsMessage.getJMSMessageID();
166                                if (messageId != null) {
167                                        headers.put(JmsHeaders.MESSAGE_ID, messageId);
168                                }
169                        }
170                        catch (Exception ex) {
171                                logger.debug("Failed to read JMSMessageID property - skipping", ex);
172                        }
173                        try {
174                                headers.put(JmsHeaders.PRIORITY, jmsMessage.getJMSPriority());
175                        }
176                        catch (Exception ex) {
177                                logger.debug("Failed to read JMSPriority property - skipping", ex);
178                        }
179                        try {
180                                Destination replyTo = jmsMessage.getJMSReplyTo();
181                                if (replyTo != null) {
182                                        headers.put(JmsHeaders.REPLY_TO, replyTo);
183                                }
184                        }
185                        catch (Exception ex) {
186                                logger.debug("Failed to read JMSReplyTo property - skipping", ex);
187                        }
188                        try {
189                                headers.put(JmsHeaders.REDELIVERED, jmsMessage.getJMSRedelivered());
190                        }
191                        catch (Exception ex) {
192                                logger.debug("Failed to read JMSRedelivered property - skipping", ex);
193                        }
194                        try {
195                                String type = jmsMessage.getJMSType();
196                                if (type != null) {
197                                        headers.put(JmsHeaders.TYPE, type);
198                                }
199                        }
200                        catch (Exception ex) {
201                                logger.debug("Failed to read JMSType property - skipping", ex);
202                        }
203                        try {
204                                headers.put(JmsHeaders.TIMESTAMP, jmsMessage.getJMSTimestamp());
205                        }
206                        catch (Exception ex) {
207                                logger.debug("Failed to read JMSTimestamp property - skipping", ex);
208                        }
209
210                        Enumeration<?> jmsPropertyNames = jmsMessage.getPropertyNames();
211                        if (jmsPropertyNames != null) {
212                                while (jmsPropertyNames.hasMoreElements()) {
213                                        String propertyName = jmsPropertyNames.nextElement().toString();
214                                        try {
215                                                String headerName = this.toHeaderName(propertyName);
216                                                headers.put(headerName, jmsMessage.getObjectProperty(propertyName));
217                                        }
218                                        catch (Exception ex) {
219                                                if (logger.isDebugEnabled()) {
220                                                        logger.debug("Error occurred while mapping JMS property '" + propertyName +
221                                                                        "' to Message header", ex);
222                                                }
223                                        }
224                                }
225                        }
226                }
227                catch (JMSException ex) {
228                        if (logger.isDebugEnabled()) {
229                                logger.debug("Error occurred while mapping from JMS properties to MessageHeaders", ex);
230                        }
231                }
232                return new MessageHeaders(headers);
233        }
234
235        /**
236         * Add the outbound prefix if necessary.
237         * <p>Convert {@link MessageHeaders#CONTENT_TYPE} to {@code content_type} for JMS compliance.
238         * @see #CONTENT_TYPE_PROPERTY
239         */
240        @Override
241        protected String fromHeaderName(String headerName) {
242                if (MessageHeaders.CONTENT_TYPE.equals(headerName)) {
243                        return CONTENT_TYPE_PROPERTY;
244                }
245                return super.fromHeaderName(headerName);
246        }
247
248        /**
249         * Add the inbound prefix if necessary.
250         * <p>Convert the JMS-compliant {@code content_type} to {@link MessageHeaders#CONTENT_TYPE}.
251         * @see #CONTENT_TYPE_PROPERTY
252         */
253        @Override
254        protected String toHeaderName(String propertyName) {
255                if (CONTENT_TYPE_PROPERTY.equals(propertyName)) {
256                        return MessageHeaders.CONTENT_TYPE;
257                }
258                return super.toHeaderName(propertyName);
259        }
260
261}