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, this, 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, this, 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 }