/*
* Copyright 2002-2016 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
*
* http://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.integration.jms;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.jms.Destination;
import javax.jms.JMSException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.jms.support.JmsHeaders;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.StringUtils;
/**
* Default implementation of {@link JmsHeaderMapper}.
* <p>
* This implementation copies JMS API headers (e.g. JMSReplyTo) to and from
* Spring Integration Messages. Any user-defined properties will also be copied
* from a JMS Message to a Spring Integration Message, and any other headers
* on a Spring Integration Message (beyond the JMS API headers) will likewise
* be copied to a JMS Message. Those other headers will be copied to the
* general properties of a JMS Message whereas the JMS API headers are passed
* to the appropriate setter methods (e.g. setJMSReplyTo).
* <p>
* Constants for the JMS API headers are defined in {@link JmsHeaders}.
* Note that the JMSMessageID and JMSRedelivered flag are only copied
* <em>from</em> a JMS Message. Those values will <em>not</em> be passed
* along from a Spring Integration Message to an outbound JMS Message.
*
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
*/
public class DefaultJmsHeaderMapper extends JmsHeaderMapper {
private static List<Class<?>> SUPPORTED_PROPERTY_TYPES = Arrays.asList(new Class<?>[] {
Boolean.class, Byte.class, Double.class, Float.class, Integer.class, Long.class, Short.class, String.class });
private final Log logger = LogFactory.getLog(this.getClass());
private volatile String inboundPrefix = "";
private volatile String outboundPrefix = "";
private volatile boolean mapInboundPriority = true;
/**
* Suppress the mapping of inbound priority by using this setter with 'false'.
*
* @param mapInboundPriority 'false' to suppress mapping the inbound priority.
*/
public void setMapInboundPriority(boolean mapInboundPriority) {
this.mapInboundPriority = mapInboundPriority;
}
/**
* Specify a prefix to be appended to the integration message header name
* for any JMS property that is being mapped into the MessageHeaders.
* The Default is an empty string (no prefix).
* <p>
* This does not affect the JMS properties covered by the specification/API,
* such as JMSCorrelationID, etc. The header names used for mapping such
* properties are all defined in our {@link JmsHeaders}.
*
* @param inboundPrefix The inbound prefix.
*/
public void setInboundPrefix(String inboundPrefix) {
this.inboundPrefix = (inboundPrefix != null) ? inboundPrefix : "";
}
/**
* Specify a prefix to be appended to the JMS property name for any
* integration message header that is being mapped into the JMS Message.
* The Default is an empty string (no prefix).
* <p>
* This does not affect the JMS properties covered by the specification/API,
* such as JMSCorrelationID, etc. The header names used for mapping such
* properties are all defined in our {@link JmsHeaders}.
*
* @param outboundPrefix The outbound prefix.
*/
public void setOutboundPrefix(String outboundPrefix) {
this.outboundPrefix = (outboundPrefix != null) ? outboundPrefix : "";
}
@Override
public void fromHeaders(MessageHeaders headers, javax.jms.Message jmsMessage) {
try {
Object jmsCorrelationId = headers.get(JmsHeaders.CORRELATION_ID);
if (jmsCorrelationId instanceof Number) {
jmsCorrelationId = jmsCorrelationId.toString();
}
if (jmsCorrelationId instanceof String) {
try {
jmsMessage.setJMSCorrelationID((String) jmsCorrelationId);
}
catch (Exception e) {
this.logger.info("failed to set JMSCorrelationID, skipping", e);
}
}
Object jmsReplyTo = headers.get(JmsHeaders.REPLY_TO);
if (jmsReplyTo instanceof Destination) {
try {
jmsMessage.setJMSReplyTo((Destination) jmsReplyTo);
}
catch (Exception e) {
this.logger.info("failed to set JMSReplyTo, skipping", e);
}
}
Object jmsType = headers.get(JmsHeaders.TYPE);
if (jmsType instanceof String) {
try {
jmsMessage.setJMSType((String) jmsType);
}
catch (Exception e) {
this.logger.info("failed to set JMSType, skipping", e);
}
}
for (Entry<String, Object> entry : headers.entrySet()) {
String headerName = entry.getKey();
if (StringUtils.hasText(headerName) && !headerName.startsWith(JmsHeaders.PREFIX)
&& jmsMessage.getObjectProperty(headerName) == null) {
Object value = entry.getValue();
if (value != null) {
if (SUPPORTED_PROPERTY_TYPES.contains(value.getClass())) {
try {
String propertyName = this.fromHeaderName(headerName);
jmsMessage.setObjectProperty(propertyName, value);
}
catch (Exception e) {
if (headerName.startsWith("JMSX")
|| headerName.equals(IntegrationMessageHeaderAccessor.PRIORITY)) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("skipping reserved header, it cannot be set by client: "
+ headerName);
}
}
else if (this.logger.isWarnEnabled()) {
this.logger.warn("failed to map Message header '" + headerName + "' to JMS property", e);
}
}
}
else if (IntegrationMessageHeaderAccessor.CORRELATION_ID.equals(headerName)) {
String propertyName = fromHeaderName(headerName);
jmsMessage.setObjectProperty(propertyName, value.toString());
}
}
}
}
}
catch (Exception e) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("error occurred while mapping from MessageHeaders to JMS properties", e);
}
}
}
@Override
public Map<String, Object> toHeaders(javax.jms.Message jmsMessage) {
Map<String, Object> headers = new HashMap<String, Object>();
try {
try {
String messageId = jmsMessage.getJMSMessageID();
if (messageId != null) {
headers.put(JmsHeaders.MESSAGE_ID, messageId);
}
}
catch (Exception e) {
this.logger.info("failed to read JMSMessageID property, skipping", e);
}
try {
Destination destination = jmsMessage.getJMSDestination();
if (destination != null) {
headers.put(JmsHeaders.DESTINATION, destination);
}
}
catch (Exception ex) {
this.logger.info("failed to read JMSDestination property, skipping", ex);
}
try {
String correlationId = jmsMessage.getJMSCorrelationID();
if (correlationId != null) {
headers.put(JmsHeaders.CORRELATION_ID, correlationId);
}
}
catch (Exception e) {
this.logger.info("failed to read JMSCorrelationID property, skipping", e);
}
try {
Destination replyTo = jmsMessage.getJMSReplyTo();
if (replyTo != null) {
headers.put(JmsHeaders.REPLY_TO, replyTo);
}
}
catch (Exception e) {
this.logger.info("failed to read JMSReplyTo property, skipping", e);
}
try {
headers.put(JmsHeaders.REDELIVERED, jmsMessage.getJMSRedelivered());
}
catch (Exception e) {
this.logger.info("failed to read JMSRedelivered property, skipping", e);
}
try {
String type = jmsMessage.getJMSType();
if (type != null) {
headers.put(JmsHeaders.TYPE, type);
}
}
catch (Exception e) {
this.logger.info("failed to read JMSType property, skipping", e);
}
try {
headers.put(JmsHeaders.TIMESTAMP, jmsMessage.getJMSTimestamp());
}
catch (Exception e) {
this.logger.info("failed to read JMSTimestamp property, skipping", e);
}
if (this.mapInboundPriority) {
try {
headers.put(IntegrationMessageHeaderAccessor.PRIORITY, jmsMessage.getJMSPriority());
}
catch (Exception e) {
this.logger.info("failed to read JMSPriority property, skipping", e);
}
}
Enumeration<?> jmsPropertyNames = jmsMessage.getPropertyNames();
if (jmsPropertyNames != null) {
while (jmsPropertyNames.hasMoreElements()) {
String propertyName = jmsPropertyNames.nextElement().toString();
try {
String headerName = this.toHeaderName(propertyName);
headers.put(headerName, jmsMessage.getObjectProperty(propertyName));
}
catch (Exception e) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("error occurred while mapping JMS property '"
+ propertyName + "' to Message header", e);
}
}
}
}
}
catch (JMSException e) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("error occurred while mapping from JMS properties to MessageHeaders", e);
}
}
return headers;
}
/**
* Adds the outbound prefix if necessary.
* Converts {@link MessageHeaders#CONTENT_TYPE} to content_type for JMS compliance.
*/
private String fromHeaderName(String headerName) {
String propertyName = headerName;
if (StringUtils.hasText(this.outboundPrefix) && !propertyName.startsWith(this.outboundPrefix)) {
propertyName = this.outboundPrefix + headerName;
}
else if (MessageHeaders.CONTENT_TYPE.equals(headerName)) {
propertyName = CONTENT_TYPE_PROPERTY;
}
return propertyName;
}
/**
* Adds the inbound prefix if necessary.
* Converts content_type to {@link MessageHeaders#CONTENT_TYPE}.
*/
private String toHeaderName(String propertyName) {
String headerName = propertyName;
if (StringUtils.hasText(this.inboundPrefix) && !headerName.startsWith(this.inboundPrefix)) {
headerName = this.inboundPrefix + propertyName;
}
else if (CONTENT_TYPE_PROPERTY.equals(propertyName)) {
headerName = MessageHeaders.CONTENT_TYPE;
}
return headerName;
}
}