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.impl;
018
019 import java.util.HashMap;
020 import java.util.Map;
021 import java.util.concurrent.ConcurrentHashMap;
022
023 import org.apache.camel.CamelContext;
024 import org.apache.camel.Exchange;
025 import org.apache.camel.ExchangePattern;
026 import org.apache.camel.ExchangeProperty;
027 import org.apache.camel.Message;
028 import org.apache.camel.RuntimeCamelException;
029 import org.apache.camel.spi.UnitOfWork;
030 import org.apache.camel.util.UuidGenerator;
031
032 /**
033 * A default implementation of {@link Exchange}
034 *
035 * @version $Revision: 46968 $
036 */
037 public class DefaultExchange implements Exchange {
038
039 private static final UuidGenerator DEFAULT_ID_GENERATOR = new UuidGenerator();
040 protected final CamelContext context;
041 private Map<String, Object> properties;
042 private Message in;
043 private Message out;
044 private Message fault;
045 private Throwable exception;
046 private String exchangeId;
047 private UnitOfWork unitOfWork;
048 private ExchangePattern pattern;
049
050 public DefaultExchange(CamelContext context) {
051 this(context, ExchangePattern.InOnly);
052 }
053
054 public DefaultExchange(CamelContext context, ExchangePattern pattern) {
055 this.context = context;
056 this.pattern = pattern;
057 }
058
059 public DefaultExchange(DefaultExchange parent) {
060 this(parent.getContext(), parent.getPattern());
061 this.unitOfWork = parent.getUnitOfWork();
062 }
063
064 @Override
065 public String toString() {
066 return "Exchange[" + in + "]";
067 }
068
069 public Exchange copy() {
070 Exchange exchange = newInstance();
071 exchange.copyFrom(this);
072 return exchange;
073 }
074
075 public void copyFrom(Exchange exchange) {
076 if (exchange == this) {
077 return;
078 }
079 setProperties(safeCopy(exchange.getProperties()));
080
081 // this can cause strangeness if we copy, say, a FileMessage onto an FtpExchange with overloaded getExchange() methods etc.
082 safeCopy(getIn(), exchange, exchange.getIn());
083 Message copyOut = exchange.getOut(false);
084 if (copyOut != null) {
085 safeCopy(getOut(true), exchange, copyOut);
086 }
087 Message copyFault = exchange.getFault(false);
088 if (copyFault != null) {
089 safeCopy(getFault(true), exchange, copyFault);
090 }
091 setException(exchange.getException());
092
093 unitOfWork = exchange.getUnitOfWork();
094 pattern = exchange.getPattern();
095 }
096
097 private static void safeCopy(Message message, Exchange exchange, Message that) {
098 if (message != null) {
099 message.copyFrom(that);
100 }
101 }
102
103 private static Map<String, Object> safeCopy(Map<String, Object> properties) {
104 if (properties == null) {
105 return null;
106 }
107 return new ConcurrentHashMap<String, Object>(properties);
108 }
109
110 private static Message safeCopy(Exchange exchange, Message message) {
111 if (message == null) {
112 return null;
113 }
114 Message answer = message.copy();
115 if (answer instanceof MessageSupport) {
116 MessageSupport messageSupport = (MessageSupport) answer;
117 messageSupport.setExchange(exchange);
118 }
119 return answer;
120 }
121
122 public Exchange newInstance() {
123 return new DefaultExchange(this);
124 }
125
126 public CamelContext getContext() {
127 return context;
128 }
129
130 public Object getProperty(String name) {
131 if (properties != null) {
132 return properties.get(name);
133 }
134 return null;
135 }
136
137 public <T> T getProperty(String name, Class<T> type) {
138 Object value = getProperty(name);
139
140 // if the property is also a well known property in ExchangeProperty then validate that the
141 // value is of the same type
142 ExchangeProperty<?> property = ExchangeProperty.getByName(name);
143 if (property != null) {
144 validateExchangePropertyIsExpectedType(property, type, value);
145 }
146
147 return getContext().getTypeConverter().convertTo(type, this, value);
148 }
149
150 public void setProperty(String name, Object value) {
151 ExchangeProperty<?> property = ExchangeProperty.getByName(name);
152
153 // if the property is also a well known property in ExchangeProperty then validate that the
154 // value is of the same type
155 if (property != null) {
156 Class type = value.getClass();
157 validateExchangePropertyIsExpectedType(property, type, value);
158 }
159 if (value != null) {
160 // avoid the NULLPointException
161 getProperties().put(name, value);
162 } else { // if the value is null , we just remove the key from the map
163 if (name != null) {
164 getProperties().remove(name);
165 }
166 }
167 }
168
169 private <T> void validateExchangePropertyIsExpectedType(ExchangeProperty<?> property, Class<T> type, Object value) {
170 if (value != null && property != null && !property.type().isAssignableFrom(type)) {
171 throw new RuntimeCamelException("Type cast exception while getting an "
172 + "Exchange Property value '" + value.toString()
173 + "' on Exchange " + this
174 + " for a well known Exchange Property with these traits: " + property);
175 }
176 }
177
178 public Object removeProperty(String name) {
179 return getProperties().remove(name);
180 }
181
182 public Map<String, Object> getProperties() {
183 if (properties == null) {
184 properties = new ConcurrentHashMap<String, Object>();
185 }
186 return properties;
187 }
188
189 public void setProperties(Map<String, Object> properties) {
190 this.properties = properties;
191 }
192
193 public Message getIn() {
194 if (in == null) {
195 in = createInMessage();
196 configureMessage(in);
197 }
198 return in;
199 }
200
201 public void setIn(Message in) {
202 this.in = in;
203 configureMessage(in);
204 }
205
206 public Message getOut() {
207 return getOut(true);
208 }
209
210 public Message getOut(boolean lazyCreate) {
211 if (out == null && lazyCreate) {
212 out = createOutMessage();
213 configureMessage(out);
214 }
215 return out;
216 }
217
218 public void setOut(Message out) {
219 this.out = out;
220 configureMessage(out);
221 }
222
223 public Throwable getException() {
224 return exception;
225 }
226
227 public void setException(Throwable exception) {
228 this.exception = exception;
229 }
230
231 public ExchangePattern getPattern() {
232 return pattern;
233 }
234
235 public void setPattern(ExchangePattern pattern) {
236 this.pattern = pattern;
237 }
238
239 public void throwException() throws Exception {
240 if (exception == null) {
241 return;
242 }
243 if (exception instanceof RuntimeException) {
244 throw (RuntimeException)exception;
245 }
246 if (exception instanceof Exception) {
247 throw (Exception)exception;
248 }
249 throw new RuntimeCamelException(exception);
250 }
251
252 public Message getFault() {
253 return getFault(true);
254 }
255
256 public Message getFault(boolean lazyCreate) {
257 if (fault == null && lazyCreate) {
258 fault = createFaultMessage();
259 configureMessage(fault);
260 }
261 return fault;
262 }
263
264 public void setFault(Message fault) {
265 this.fault = fault;
266 configureMessage(fault);
267 }
268
269 public String getExchangeId() {
270 if (exchangeId == null) {
271 exchangeId = DefaultExchange.DEFAULT_ID_GENERATOR.generateId();
272 }
273 return exchangeId;
274 }
275
276 public void setExchangeId(String id) {
277 this.exchangeId = id;
278 }
279
280 public boolean isFailed() {
281 Message faultMessage = getFault(false);
282 if (faultMessage != null) {
283 Object faultBody = faultMessage.getBody();
284 if (faultBody != null) {
285 return true;
286 }
287 }
288 return getException() != null;
289 }
290
291 public boolean isTransacted() {
292 ExchangeProperty<?> property = ExchangeProperty.get("transacted");
293 return property != null && property.get(this) == Boolean.TRUE;
294 }
295
296 public UnitOfWork getUnitOfWork() {
297 return unitOfWork;
298 }
299
300 public void setUnitOfWork(UnitOfWork unitOfWork) {
301 this.unitOfWork = unitOfWork;
302 }
303
304 /**
305 * Factory method used to lazily create the IN message
306 */
307 protected Message createInMessage() {
308 return new DefaultMessage();
309 }
310
311 /**
312 * Factory method to lazily create the OUT message
313 */
314 protected Message createOutMessage() {
315 return new DefaultMessage();
316 }
317
318 /**
319 * Factory method to lazily create the FAULT message
320 */
321 protected Message createFaultMessage() {
322 return new DefaultMessage();
323 }
324
325 /**
326 * Configures the message after it has been set on the exchange
327 */
328 protected void configureMessage(Message message) {
329 if (message instanceof MessageSupport) {
330 MessageSupport messageSupport = (MessageSupport)message;
331 messageSupport.setExchange(this);
332 }
333 }
334
335 }