001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.jms;
018    
019    import java.io.File;
020    import java.util.Map;
021    import javax.jms.Destination;
022    import javax.jms.JMSException;
023    import javax.jms.Message;
024    import javax.jms.Queue;
025    import javax.jms.Topic;
026    
027    import org.apache.camel.RuntimeExchangeException;
028    import org.apache.camel.impl.DefaultMessage;
029    import org.apache.camel.util.ExchangeHelper;
030    import org.apache.commons.logging.Log;
031    import org.apache.commons.logging.LogFactory;
032    
033    /**
034     * Represents a {@link org.apache.camel.Message} for working with JMS
035     *
036     * @version $Revision:520964 $
037     */
038    public class JmsMessage extends DefaultMessage {
039        private static final transient Log LOG = LogFactory.getLog(JmsMessage.class);
040        private Message jmsMessage;
041        private JmsBinding binding;
042    
043        public JmsMessage(Message jmsMessage, JmsBinding binding) {
044            setJmsMessage(jmsMessage);
045            setBinding(binding);
046        }
047    
048        @Override
049        public String toString() {
050            if (jmsMessage != null) {
051                return "JmsMessage: " + jmsMessage;
052            } else {
053                return "JmsMessage: " + getBody();
054            }
055        }
056    
057        @Override
058        public void copyFrom(org.apache.camel.Message that) {
059            // must initialize headers before we set the JmsMessage to avoid Camel
060            // populating it before we do the copy
061            getHeaders().clear();
062    
063            boolean copyMessageId = true;
064            if (that instanceof JmsMessage) {
065                JmsMessage thatMessage = (JmsMessage) that;
066                this.jmsMessage = thatMessage.jmsMessage;
067                if (this.jmsMessage != null) {
068                    // for performance lets not copy the messageID if we are a JMS message
069                    copyMessageId = false;
070                }
071            }
072    
073            if (copyMessageId) {
074                setMessageId(that.getMessageId());
075            }
076            setBody(that.getBody());
077            getHeaders().putAll(that.getHeaders());
078            getAttachments().putAll(that.getAttachments());
079        }
080    
081        /**
082         * Returns the underlying JMS message
083         */
084        public Message getJmsMessage() {
085            return jmsMessage;
086        }
087    
088        public JmsBinding getBinding() {
089            if (binding == null) {
090                binding = ExchangeHelper.getBinding(getExchange(), JmsBinding.class);
091            }
092            return binding;
093        }
094    
095        public void setBinding(JmsBinding binding) {
096            this.binding = binding;
097        }
098    
099        public void setJmsMessage(Message jmsMessage) {
100            if (jmsMessage != null) {
101                try {
102                    setMessageId(jmsMessage.getJMSMessageID());
103                } catch (JMSException e) {
104                    LOG.warn("Unable to retrieve JMSMessageID from JMS Message", e);
105                }
106            }
107            this.jmsMessage = jmsMessage;
108        }
109    
110        public Object getHeader(String name) {
111            Object answer = null;
112    
113            // we will exclude using JMS-prefixed headers here to avoid strangeness with some JMS providers
114            // e.g. ActiveMQ returns the String not the Destination type for "JMSReplyTo"!
115            // only look in jms message directly if we have not populated headers
116            if (jmsMessage != null && !hasPopulatedHeaders() && !name.startsWith("JMS")) {
117                try {
118                    // use binding to do the lookup as it has to consider using encoded keys
119                    answer = getBinding().getObjectProperty(jmsMessage, name);
120                } catch (JMSException e) {
121                    throw new RuntimeExchangeException("Unable to retrieve header from JMS Message: " + name, getExchange(), e);
122                }
123            }
124            // only look if we have populated headers otherwise there are no headers at all
125            // if we do lookup a header starting with JMS then force a lookup
126            if (answer == null && (hasPopulatedHeaders() || name.startsWith("JMS"))) {
127                answer = super.getHeader(name);
128            }
129            return answer;
130        }
131    
132        @Override
133        public Map<String, Object> getHeaders() {
134            ensureInitialHeaders();
135            return super.getHeaders();
136        }
137    
138        @Override
139        public Object removeHeader(String name) {
140            ensureInitialHeaders();
141            return super.removeHeader(name);
142        }
143    
144        @Override
145        public void setHeaders(Map<String, Object> headers) {
146            ensureInitialHeaders();
147            super.setHeaders(headers);
148        }
149    
150        @Override
151        public void setHeader(String name, Object value) {
152            ensureInitialHeaders();
153            super.setHeader(name, value);
154        }
155    
156        @Override
157        public JmsMessage newInstance() {
158            return new JmsMessage(null, binding);
159        }
160    
161        /**
162         * Returns true if a new JMS message instance should be created to send to the next component
163         */
164        public boolean shouldCreateNewMessage() {
165            return super.hasPopulatedHeaders();
166        }
167    
168        /**
169         * Ensure that the headers have been populated from the underlying JMS message
170         * before we start mutating the headers
171         */
172        protected void ensureInitialHeaders() {
173            if (jmsMessage != null && !hasPopulatedHeaders()) {
174                // we have not populated headers so force this by creating
175                // new headers and set it on super
176                super.setHeaders(createHeaders());
177            }
178        }
179    
180        @Override
181        protected Object createBody() {
182            if (jmsMessage != null) {
183                return getBinding().extractBodyFromJms(getExchange(), jmsMessage);
184            }
185            return null;
186        }
187    
188        @Override
189        protected void populateInitialHeaders(Map<String, Object> map) {
190            if (jmsMessage != null && map != null) {
191                map.putAll(getBinding().extractHeadersFromJms(jmsMessage, getExchange()));
192            }
193        }
194    
195        @Override
196        protected String createMessageId() {
197            if (jmsMessage == null) {
198                if (LOG.isTraceEnabled()) {
199                    LOG.trace("No javax.jms.Message set so generating a new message id");
200                }
201                return super.createMessageId();
202            }
203            try {
204                String id = getDestinationAsString(jmsMessage.getJMSDestination()) + jmsMessage.getJMSMessageID();
205                return getSanitizedString(id);
206            } catch (JMSException e) {
207                throw new RuntimeExchangeException("Unable to retrieve JMSMessageID from JMS Message", getExchange(), e);
208            }
209        }
210    
211        private String getDestinationAsString(Destination destination) throws JMSException {
212            String result;
213            if (destination == null) {
214                result = "null destination!" + File.separator;
215            } else if (destination instanceof Topic) {
216                result = "topic" + File.separator + ((Topic) destination).getTopicName() + File.separator;
217            } else {
218                result = "queue" + File.separator + ((Queue) destination).getQueueName() + File.separator;
219            }
220            return result;
221        }
222    
223        private String getSanitizedString(Object value) {
224            return value != null ? value.toString().replaceAll("[^a-zA-Z0-9\\.\\_\\-]", "_") : "";
225        }
226    
227        @Override
228        public String createExchangeId() {
229            if (jmsMessage != null) {
230                try {
231                    return jmsMessage.getJMSMessageID();
232                } catch (JMSException e) {
233                    throw new RuntimeExchangeException("Unable to retrieve JMSMessageID from JMS Message", getExchange(), e);
234                }
235            }
236            return super.createExchangeId();
237        }
238    
239    }