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.ArrayList;
020 import java.util.List;
021 import java.util.Map;
022 import java.util.concurrent.ConcurrentHashMap;
023
024 import org.apache.camel.CamelContext;
025 import org.apache.camel.Endpoint;
026 import org.apache.camel.Exchange;
027 import org.apache.camel.ExchangePattern;
028 import org.apache.camel.Message;
029 import org.apache.camel.spi.Synchronization;
030 import org.apache.camel.spi.UnitOfWork;
031 import org.apache.camel.util.ExchangeHelper;
032 import org.apache.camel.util.ObjectHelper;
033
034 /**
035 * A default implementation of {@link Exchange}
036 *
037 * @version
038 */
039 public final class DefaultExchange implements Exchange {
040
041 protected final CamelContext context;
042 private Map<String, Object> properties;
043 private Message in;
044 private Message out;
045 private Exception exception;
046 private String exchangeId;
047 private UnitOfWork unitOfWork;
048 private ExchangePattern pattern;
049 private Endpoint fromEndpoint;
050 private String fromRouteId;
051 private List<Synchronization> onCompletions;
052
053 public DefaultExchange(CamelContext context) {
054 this(context, ExchangePattern.InOnly);
055 }
056
057 public DefaultExchange(CamelContext context, ExchangePattern pattern) {
058 this.context = context;
059 this.pattern = pattern;
060 }
061
062 public DefaultExchange(Exchange parent) {
063 this(parent.getContext(), parent.getPattern());
064 this.fromEndpoint = parent.getFromEndpoint();
065 this.fromRouteId = parent.getFromRouteId();
066 this.unitOfWork = parent.getUnitOfWork();
067 }
068
069 public DefaultExchange(Endpoint fromEndpoint) {
070 this(fromEndpoint, ExchangePattern.InOnly);
071 }
072
073 public DefaultExchange(Endpoint fromEndpoint, ExchangePattern pattern) {
074 this(fromEndpoint.getCamelContext(), pattern);
075 this.fromEndpoint = fromEndpoint;
076 }
077
078 @Override
079 public String toString() {
080 return "Exchange[" + (out == null ? in : out) + "]";
081 }
082
083 public Exchange copy() {
084 DefaultExchange exchange = new DefaultExchange(this);
085
086 if (hasProperties()) {
087 exchange.setProperties(safeCopy(getProperties()));
088 }
089
090 exchange.setIn(getIn().copy());
091 if (hasOut()) {
092 exchange.setOut(getOut().copy());
093 }
094 exchange.setException(getException());
095 return exchange;
096 }
097
098 private static Map<String, Object> safeCopy(Map<String, Object> properties) {
099 if (properties == null) {
100 return null;
101 }
102 return new ConcurrentHashMap<String, Object>(properties);
103 }
104
105 public CamelContext getContext() {
106 return context;
107 }
108
109 public Object getProperty(String name) {
110 if (hasProperties()) {
111 // use intern String for properties which is Camel* properties
112 // this reduces memory allocations needed for those common properties
113 if (name.startsWith("Camel")) {
114 name = name.intern();
115 }
116 return properties.get(name);
117 }
118 return null;
119 }
120
121 public Object getProperty(String name, Object defaultValue) {
122 Object answer = getProperty(name);
123 return answer != null ? answer : defaultValue;
124 }
125
126 @SuppressWarnings("unchecked")
127 public <T> T getProperty(String name, Class<T> type) {
128 Object value = getProperty(name);
129 if (value == null) {
130 // lets avoid NullPointerException when converting to boolean for null values
131 if (boolean.class.isAssignableFrom(type)) {
132 return (T) Boolean.FALSE;
133 }
134 return null;
135 }
136
137 // eager same instance type test to avoid the overhead of invoking the type converter
138 // if already same type
139 if (type.isInstance(value)) {
140 return type.cast(value);
141 }
142
143 return ExchangeHelper.convertToType(this, type, value);
144 }
145
146 @SuppressWarnings("unchecked")
147 public <T> T getProperty(String name, Object defaultValue, Class<T> type) {
148 Object value = getProperty(name, defaultValue);
149 if (value == null) {
150 // lets avoid NullPointerException when converting to boolean for null values
151 if (boolean.class.isAssignableFrom(type)) {
152 return (T) Boolean.FALSE;
153 }
154 return null;
155 }
156
157 // eager same instance type test to avoid the overhead of invoking the type converter
158 // if already same type
159 if (type.isInstance(value)) {
160 return type.cast(value);
161 }
162
163 return ExchangeHelper.convertToType(this, type, value);
164 }
165
166 public void setProperty(String name, Object value) {
167 // use intern String for properties which is Camel* properties
168 // this reduces memory allocations needed for those common properties
169 if (name != null && name.startsWith("Camel")) {
170 name = name.intern();
171 }
172 if (value != null) {
173 // avoid the NullPointException
174 getProperties().put(name, value);
175 } else {
176 // if the value is null, we just remove the key from the map
177 if (name != null) {
178 getProperties().remove(name);
179 }
180 }
181 }
182
183 public Object removeProperty(String name) {
184 if (!hasProperties()) {
185 return null;
186 }
187 return getProperties().remove(name);
188 }
189
190 public Map<String, Object> getProperties() {
191 if (properties == null) {
192 properties = new ConcurrentHashMap<String, Object>();
193 }
194 return properties;
195 }
196
197 public boolean hasProperties() {
198 return properties != null && !properties.isEmpty();
199 }
200
201 public void setProperties(Map<String, Object> properties) {
202 this.properties = properties;
203 }
204
205 public Message getIn() {
206 if (in == null) {
207 in = new DefaultMessage();
208 configureMessage(in);
209 }
210 return in;
211 }
212
213 public <T> T getIn(Class<T> type) {
214 Message in = getIn();
215
216 // eager same instance type test to avoid the overhead of invoking the type converter
217 // if already same type
218 if (type.isInstance(in)) {
219 return type.cast(in);
220 }
221
222 // fallback to use type converter
223 return context.getTypeConverter().convertTo(type, in);
224 }
225
226 public void setIn(Message in) {
227 this.in = in;
228 configureMessage(in);
229 }
230
231 public Message getOut() {
232 // lazy create
233 if (out == null) {
234 out = (in != null && in instanceof MessageSupport)
235 ? ((MessageSupport)in).newInstance() : new DefaultMessage();
236 configureMessage(out);
237 }
238 return out;
239 }
240
241 public <T> T getOut(Class<T> type) {
242 if (!hasOut()) {
243 return null;
244 }
245
246 Message out = getOut();
247
248 // eager same instance type test to avoid the overhead of invoking the type converter
249 // if already same type
250 if (type.isInstance(out)) {
251 return type.cast(out);
252 }
253
254 // fallback to use type converter
255 return context.getTypeConverter().convertTo(type, out);
256 }
257
258 public boolean hasOut() {
259 return out != null;
260 }
261
262 public void setOut(Message out) {
263 this.out = out;
264 configureMessage(out);
265 }
266
267 public Exception getException() {
268 return exception;
269 }
270
271 public <T> T getException(Class<T> type) {
272 return ObjectHelper.getException(type, exception);
273 }
274
275 public void setException(Throwable t) {
276 if (t == null) {
277 this.exception = null;
278 } else if (t instanceof Exception) {
279 this.exception = (Exception) t;
280 } else {
281 // wrap throwable into an exception
282 this.exception = ObjectHelper.wrapCamelExecutionException(this, t);
283 }
284 }
285
286 public ExchangePattern getPattern() {
287 return pattern;
288 }
289
290 public void setPattern(ExchangePattern pattern) {
291 this.pattern = pattern;
292 }
293
294 public Endpoint getFromEndpoint() {
295 return fromEndpoint;
296 }
297
298 public void setFromEndpoint(Endpoint fromEndpoint) {
299 this.fromEndpoint = fromEndpoint;
300 }
301
302 public String getFromRouteId() {
303 return fromRouteId;
304 }
305
306 public void setFromRouteId(String fromRouteId) {
307 this.fromRouteId = fromRouteId;
308 }
309
310 public String getExchangeId() {
311 if (exchangeId == null) {
312 exchangeId = createExchangeId();
313 }
314 return exchangeId;
315 }
316
317 public void setExchangeId(String id) {
318 this.exchangeId = id;
319 }
320
321 public boolean isFailed() {
322 return (hasOut() && getOut().isFault()) || getException() != null;
323 }
324
325 public boolean isTransacted() {
326 UnitOfWork uow = getUnitOfWork();
327 if (uow != null) {
328 return uow.isTransacted();
329 } else {
330 return false;
331 }
332 }
333
334 public Boolean isExternalRedelivered() {
335 Boolean answer = null;
336
337 // check property first, as the implementation details to know if the message
338 // was externally redelivered is message specific, and thus the message implementation
339 // could potentially change during routing, and therefore later we may not know if the
340 // original message was externally redelivered or not, therefore we store this detail
341 // as a exchange property to keep it around for the lifecycle of the exchange
342 if (hasProperties()) {
343 answer = getProperty(Exchange.EXTERNAL_REDELIVERED, null, Boolean.class);
344 }
345
346 if (answer == null) {
347 // lets avoid adding methods to the Message API, so we use the
348 // DefaultMessage to allow component specific messages to extend
349 // and implement the isExternalRedelivered method.
350 DefaultMessage msg = getIn(DefaultMessage.class);
351 if (msg != null) {
352 answer = msg.isTransactedRedelivered();
353 // store as property to keep around
354 setProperty(Exchange.EXTERNAL_REDELIVERED, answer);
355 }
356 }
357
358 return answer;
359 }
360
361 public boolean isRollbackOnly() {
362 return Boolean.TRUE.equals(getProperty(Exchange.ROLLBACK_ONLY)) || Boolean.TRUE.equals(getProperty(Exchange.ROLLBACK_ONLY_LAST));
363 }
364
365 public UnitOfWork getUnitOfWork() {
366 return unitOfWork;
367 }
368
369 public void setUnitOfWork(UnitOfWork unitOfWork) {
370 this.unitOfWork = unitOfWork;
371 if (onCompletions != null) {
372 // now an unit of work has been assigned so add the on completions
373 // we might have registered already
374 for (Synchronization onCompletion : onCompletions) {
375 unitOfWork.addSynchronization(onCompletion);
376 }
377 // cleanup the temporary on completion list as they now have been registered
378 // on the unit of work
379 onCompletions.clear();
380 onCompletions = null;
381 }
382 }
383
384 public void addOnCompletion(Synchronization onCompletion) {
385 if (unitOfWork == null) {
386 // unit of work not yet registered so we store the on completion temporary
387 // until the unit of work is assigned to this exchange by the UnitOfWorkProcessor
388 if (onCompletions == null) {
389 onCompletions = new ArrayList<Synchronization>();
390 }
391 onCompletions.add(onCompletion);
392 } else {
393 getUnitOfWork().addSynchronization(onCompletion);
394 }
395 }
396
397 public boolean containsOnCompletion(Synchronization onCompletion) {
398 if (unitOfWork != null) {
399 // if there is an unit of work then the completions is moved there
400 return unitOfWork.containsSynchronization(onCompletion);
401 } else {
402 // check temporary completions if no unit of work yet
403 return onCompletions != null && onCompletions.contains(onCompletion);
404 }
405 }
406
407 public void handoverCompletions(Exchange target) {
408 if (onCompletions != null) {
409 for (Synchronization onCompletion : onCompletions) {
410 target.addOnCompletion(onCompletion);
411 }
412 // cleanup the temporary on completion list as they have been handed over
413 onCompletions.clear();
414 onCompletions = null;
415 } else if (unitOfWork != null) {
416 // let unit of work handover
417 unitOfWork.handoverSynchronization(target);
418 }
419 }
420
421 public List<Synchronization> handoverCompletions() {
422 List<Synchronization> answer = null;
423 if (onCompletions != null) {
424 answer = new ArrayList<Synchronization>(onCompletions);
425 onCompletions.clear();
426 onCompletions = null;
427 }
428 return answer;
429 }
430
431 /**
432 * Configures the message after it has been set on the exchange
433 */
434 protected void configureMessage(Message message) {
435 if (message instanceof MessageSupport) {
436 MessageSupport messageSupport = (MessageSupport)message;
437 messageSupport.setExchange(this);
438 }
439 }
440
441 @SuppressWarnings("deprecation")
442 protected String createExchangeId() {
443 String answer = null;
444 if (in != null) {
445 answer = in.createExchangeId();
446 }
447 if (answer == null) {
448 answer = context.getUuidGenerator().generateUuid();
449 }
450 return answer;
451 }
452 }