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 }