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 */ 017package org.apache.camel.util; 018 019import java.util.HashMap; 020import java.util.LinkedList; 021import java.util.List; 022import java.util.Map; 023import java.util.concurrent.ExecutionException; 024import java.util.concurrent.Future; 025import java.util.concurrent.TimeUnit; 026import java.util.concurrent.TimeoutException; 027import java.util.function.Predicate; 028 029import org.apache.camel.CamelContext; 030import org.apache.camel.CamelExchangeException; 031import org.apache.camel.CamelExecutionException; 032import org.apache.camel.Endpoint; 033import org.apache.camel.Exchange; 034import org.apache.camel.ExchangePattern; 035import org.apache.camel.InvalidPayloadException; 036import org.apache.camel.Message; 037import org.apache.camel.MessageHistory; 038import org.apache.camel.NoSuchBeanException; 039import org.apache.camel.NoSuchEndpointException; 040import org.apache.camel.NoSuchHeaderException; 041import org.apache.camel.NoSuchPropertyException; 042import org.apache.camel.NoTypeConversionAvailableException; 043import org.apache.camel.TypeConversionException; 044import org.apache.camel.TypeConverter; 045import org.apache.camel.impl.DefaultExchange; 046import org.apache.camel.impl.MessageSupport; 047import org.apache.camel.spi.Synchronization; 048import org.apache.camel.spi.UnitOfWork; 049 050/** 051 * Some helper methods for working with {@link Exchange} objects 052 * 053 * @version 054 */ 055public final class ExchangeHelper { 056 057 /** 058 * Utility classes should not have a public constructor. 059 */ 060 private ExchangeHelper() { 061 } 062 063 /** 064 * Extracts the Exchange.BINDING of the given type or null if not present 065 * 066 * @param exchange the message exchange 067 * @param type the expected binding type 068 * @return the binding object of the given type or null if it could not be found or converted 069 */ 070 public static <T> T getBinding(Exchange exchange, Class<T> type) { 071 return exchange != null ? exchange.getProperty(Exchange.BINDING, type) : null; 072 } 073 074 /** 075 * Attempts to resolve the endpoint for the given value 076 * 077 * @param exchange the message exchange being processed 078 * @param value the value which can be an {@link Endpoint} or an object 079 * which provides a String representation of an endpoint via 080 * {@link #toString()} 081 * @return the endpoint 082 * @throws NoSuchEndpointException if the endpoint cannot be resolved 083 */ 084 public static Endpoint resolveEndpoint(Exchange exchange, Object value) throws NoSuchEndpointException { 085 Endpoint endpoint; 086 if (value instanceof Endpoint) { 087 endpoint = (Endpoint) value; 088 } else { 089 String uri = value.toString().trim(); 090 endpoint = CamelContextHelper.getMandatoryEndpoint(exchange.getContext(), uri); 091 } 092 return endpoint; 093 } 094 095 /** 096 * Gets the mandatory property of the exchange of the correct type 097 * 098 * @param exchange the exchange 099 * @param propertyName the property name 100 * @param type the type 101 * @return the property value 102 * @throws TypeConversionException is thrown if error during type conversion 103 * @throws NoSuchPropertyException is thrown if no property exists 104 */ 105 public static <T> T getMandatoryProperty(Exchange exchange, String propertyName, Class<T> type) throws NoSuchPropertyException { 106 T result = exchange.getProperty(propertyName, type); 107 if (result != null) { 108 return result; 109 } 110 throw new NoSuchPropertyException(exchange, propertyName, type); 111 } 112 113 /** 114 * Gets the mandatory inbound header of the correct type 115 * 116 * @param exchange the exchange 117 * @param headerName the header name 118 * @param type the type 119 * @return the header value 120 * @throws TypeConversionException is thrown if error during type conversion 121 * @throws NoSuchHeaderException is thrown if no headers exists 122 */ 123 public static <T> T getMandatoryHeader(Exchange exchange, String headerName, Class<T> type) throws TypeConversionException, NoSuchHeaderException { 124 T answer = exchange.getIn().getHeader(headerName, type); 125 if (answer == null) { 126 throw new NoSuchHeaderException(exchange, headerName, type); 127 } 128 return answer; 129 } 130 131 /** 132 * Gets the mandatory inbound header of the correct type 133 * 134 * @param message the message 135 * @param headerName the header name 136 * @param type the type 137 * @return the header value 138 * @throws TypeConversionException is thrown if error during type conversion 139 * @throws NoSuchHeaderException is thrown if no headers exists 140 */ 141 public static <T> T getMandatoryHeader(Message message, String headerName, Class<T> type) throws TypeConversionException, NoSuchHeaderException { 142 T answer = message.getHeader(headerName, type); 143 if (answer == null) { 144 throw new NoSuchHeaderException(message.getExchange(), headerName, type); 145 } 146 return answer; 147 } 148 149 /** 150 * Gets an header or property of the correct type 151 * 152 * @param exchange the exchange 153 * @param name the name of the header or the property 154 * @param type the type 155 * @return the header or property value 156 * @throws TypeConversionException is thrown if error during type conversion 157 * @throws NoSuchHeaderException is thrown if no headers exists 158 */ 159 public static <T> T getHeaderOrProperty(Exchange exchange, String name, Class<T> type) throws TypeConversionException { 160 T answer = exchange.getIn().getHeader(name, type); 161 if (answer == null) { 162 answer = exchange.getProperty(name, type); 163 } 164 return answer; 165 } 166 167 /** 168 * Returns the mandatory inbound message body of the correct type or throws 169 * an exception if it is not present 170 * 171 * @param exchange the exchange 172 * @return the body, is never <tt>null</tt> 173 * @throws InvalidPayloadException Is thrown if the body being <tt>null</tt> or wrong class type 174 * @deprecated use {@link org.apache.camel.Message#getMandatoryBody()} 175 */ 176 @Deprecated 177 public static Object getMandatoryInBody(Exchange exchange) throws InvalidPayloadException { 178 return exchange.getIn().getMandatoryBody(); 179 } 180 181 /** 182 * Returns the mandatory inbound message body of the correct type or throws 183 * an exception if it is not present 184 * @deprecated use {@link org.apache.camel.Message#getMandatoryBody(Class)} 185 */ 186 @Deprecated 187 public static <T> T getMandatoryInBody(Exchange exchange, Class<T> type) throws InvalidPayloadException { 188 return exchange.getIn().getMandatoryBody(type); 189 } 190 191 /** 192 * Returns the mandatory outbound message body of the correct type or throws 193 * an exception if it is not present 194 * @deprecated use {@link org.apache.camel.Message#getMandatoryBody()} 195 */ 196 @Deprecated 197 public static Object getMandatoryOutBody(Exchange exchange) throws InvalidPayloadException { 198 return exchange.getOut().getMandatoryBody(); 199 } 200 201 /** 202 * Returns the mandatory outbound message body of the correct type or throws 203 * an exception if it is not present 204 * @deprecated use {@link org.apache.camel.Message#getMandatoryBody(Class)} 205 */ 206 @Deprecated 207 public static <T> T getMandatoryOutBody(Exchange exchange, Class<T> type) throws InvalidPayloadException { 208 return exchange.getOut().getMandatoryBody(type); 209 } 210 211 /** 212 * Converts the value to the given expected type or throws an exception 213 * 214 * @return the converted value 215 * @throws TypeConversionException is thrown if error during type conversion 216 * @throws NoTypeConversionAvailableException} if no type converters exists to convert to the given type 217 */ 218 public static <T> T convertToMandatoryType(Exchange exchange, Class<T> type, Object value) 219 throws TypeConversionException, NoTypeConversionAvailableException { 220 CamelContext camelContext = exchange.getContext(); 221 ObjectHelper.notNull(camelContext, "CamelContext of Exchange"); 222 TypeConverter converter = camelContext.getTypeConverter(); 223 if (converter != null) { 224 return converter.mandatoryConvertTo(type, exchange, value); 225 } 226 throw new NoTypeConversionAvailableException(value, type); 227 } 228 229 /** 230 * Converts the value to the given expected type 231 * 232 * @return the converted value 233 * @throws org.apache.camel.TypeConversionException is thrown if error during type conversion 234 */ 235 public static <T> T convertToType(Exchange exchange, Class<T> type, Object value) throws TypeConversionException { 236 CamelContext camelContext = exchange.getContext(); 237 ObjectHelper.notNull(camelContext, "CamelContext of Exchange"); 238 TypeConverter converter = camelContext.getTypeConverter(); 239 if (converter != null) { 240 return converter.convertTo(type, exchange, value); 241 } 242 return null; 243 } 244 245 /** 246 * Creates a new instance and copies from the current message exchange so that it can be 247 * forwarded to another destination as a new instance. Unlike regular copy this operation 248 * will not share the same {@link org.apache.camel.spi.UnitOfWork} so its should be used 249 * for async messaging, where the original and copied exchange are independent. 250 * 251 * @param exchange original copy of the exchange 252 * @param handover whether the on completion callbacks should be handed over to the new copy. 253 */ 254 public static Exchange createCorrelatedCopy(Exchange exchange, boolean handover) { 255 return createCorrelatedCopy(exchange, handover, false); 256 } 257 258 /** 259 * Creates a new instance and copies from the current message exchange so that it can be 260 * forwarded to another destination as a new instance. Unlike regular copy this operation 261 * will not share the same {@link org.apache.camel.spi.UnitOfWork} so its should be used 262 * for async messaging, where the original and copied exchange are independent. 263 * 264 * @param exchange original copy of the exchange 265 * @param handover whether the on completion callbacks should be handed over to the new copy. 266 * @param useSameMessageId whether to use same message id on the copy message. 267 */ 268 public static Exchange createCorrelatedCopy(Exchange exchange, boolean handover, boolean useSameMessageId) { 269 return createCorrelatedCopy(exchange, handover, useSameMessageId, null); 270 } 271 272 /** 273 * Creates a new instance and copies from the current message exchange so that it can be 274 * forwarded to another destination as a new instance. Unlike regular copy this operation 275 * will not share the same {@link org.apache.camel.spi.UnitOfWork} so its should be used 276 * for async messaging, where the original and copied exchange are independent. 277 * 278 * @param exchange original copy of the exchange 279 * @param handover whether the on completion callbacks should be handed over to the new copy. 280 * @param useSameMessageId whether to use same message id on the copy message. 281 * @param filter whether to handover the on completion 282 */ 283 public static Exchange createCorrelatedCopy(Exchange exchange, boolean handover, boolean useSameMessageId, Predicate<Synchronization> filter) { 284 String id = exchange.getExchangeId(); 285 286 // make sure to do a safe copy as the correlated copy can be routed independently of the source. 287 Exchange copy = exchange.copy(true); 288 // do not reuse message id on copy 289 if (!useSameMessageId) { 290 if (copy.hasOut()) { 291 copy.getOut().setMessageId(null); 292 } 293 copy.getIn().setMessageId(null); 294 } 295 // do not share the unit of work 296 copy.setUnitOfWork(null); 297 // do not reuse the message id 298 // hand over on completion to the copy if we got any 299 UnitOfWork uow = exchange.getUnitOfWork(); 300 if (handover && uow != null) { 301 uow.handoverSynchronization(copy, filter); 302 } 303 // set a correlation id so we can track back the original exchange 304 copy.setProperty(Exchange.CORRELATION_ID, id); 305 return copy; 306 } 307 308 /** 309 * Creates a new instance and copies from the current message exchange so that it can be 310 * forwarded to another destination as a new instance. 311 * 312 * @param exchange original copy of the exchange 313 * @param preserveExchangeId whether or not the exchange id should be preserved 314 * @return the copy 315 */ 316 public static Exchange createCopy(Exchange exchange, boolean preserveExchangeId) { 317 Exchange copy = exchange.copy(); 318 if (preserveExchangeId) { 319 // must preserve exchange id 320 copy.setExchangeId(exchange.getExchangeId()); 321 } 322 return copy; 323 } 324 325 /** 326 * Copies the results of a message exchange from the source exchange to the result exchange 327 * which will copy the message contents, exchange properties and the exception. 328 * Notice the {@link ExchangePattern} is <b>not</b> copied/altered. 329 * 330 * @param result the result exchange which will have the output and error state added 331 * @param source the source exchange which is not modified 332 */ 333 public static void copyResults(Exchange result, Exchange source) { 334 335 // -------------------------------------------------------------------- 336 // TODO: merge logic with that of copyResultsPreservePattern() 337 // -------------------------------------------------------------------- 338 339 if (result == source) { 340 // we just need to ensure MEP is as expected (eg copy result to OUT if out capable) 341 // and the result is not failed 342 if (result.getPattern() == ExchangePattern.InOptionalOut) { 343 // keep as is 344 } else if (result.getPattern().isOutCapable() && !result.hasOut() && !result.isFailed()) { 345 // copy IN to OUT as we expect a OUT response 346 result.getOut().copyFrom(source.getIn()); 347 } 348 return; 349 } 350 351 if (result != source) { 352 result.setException(source.getException()); 353 if (source.hasOut()) { 354 result.getOut().copyFrom(source.getOut()); 355 } else if (result.getPattern() == ExchangePattern.InOptionalOut) { 356 // special case where the result is InOptionalOut and with no OUT response 357 // so we should return null to indicate this fact 358 result.setOut(null); 359 } else { 360 // no results so lets copy the last input 361 // as the final processor on a pipeline might not 362 // have created any OUT; such as a mock:endpoint 363 // so lets assume the last IN is the OUT 364 if (result.getPattern().isOutCapable()) { 365 // only set OUT if its OUT capable 366 result.getOut().copyFrom(source.getIn()); 367 } else { 368 // if not replace IN instead to keep the MEP 369 result.getIn().copyFrom(source.getIn()); 370 // clear any existing OUT as the result is on the IN 371 if (result.hasOut()) { 372 result.setOut(null); 373 } 374 } 375 } 376 377 if (source.hasProperties()) { 378 result.getProperties().putAll(source.getProperties()); 379 } 380 } 381 } 382 383 /** 384 * Copies the <code>source</code> exchange to <code>target</code> exchange 385 * preserving the {@link ExchangePattern} of <code>target</code>. 386 * 387 * @param source source exchange. 388 * @param result target exchange. 389 */ 390 public static void copyResultsPreservePattern(Exchange result, Exchange source) { 391 392 // -------------------------------------------------------------------- 393 // TODO: merge logic with that of copyResults() 394 // -------------------------------------------------------------------- 395 396 if (result == source) { 397 // we just need to ensure MEP is as expected (eg copy result to OUT if out capable) 398 // and the result is not failed 399 if (result.getPattern() == ExchangePattern.InOptionalOut) { 400 // keep as is 401 } else if (result.getPattern().isOutCapable() && !result.hasOut() && !result.isFailed()) { 402 // copy IN to OUT as we expect a OUT response 403 result.getOut().copyFrom(source.getIn()); 404 } 405 return; 406 } 407 408 // copy in message 409 result.getIn().copyFrom(source.getIn()); 410 411 // copy out message 412 if (source.hasOut()) { 413 // exchange pattern sensitive 414 Message resultMessage = source.getOut().isFault() ? result.getOut() : getResultMessage(result); 415 resultMessage.copyFrom(source.getOut()); 416 } 417 418 // copy exception 419 result.setException(source.getException()); 420 421 // copy properties 422 if (source.hasProperties()) { 423 result.getProperties().putAll(source.getProperties()); 424 } 425 } 426 427 /** 428 * Returns the message where to write results in an 429 * exchange-pattern-sensitive way. 430 * 431 * @param exchange message exchange. 432 * @return result message. 433 */ 434 public static Message getResultMessage(Exchange exchange) { 435 if (exchange.getPattern().isOutCapable()) { 436 return exchange.getOut(); 437 } else { 438 return exchange.getIn(); 439 } 440 } 441 442 /** 443 * Returns true if the given exchange pattern (if defined) can support OUT messages 444 * 445 * @param exchange the exchange to interrogate 446 * @return true if the exchange is defined as an {@link ExchangePattern} which supports 447 * OUT messages 448 */ 449 public static boolean isOutCapable(Exchange exchange) { 450 ExchangePattern pattern = exchange.getPattern(); 451 return pattern != null && pattern.isOutCapable(); 452 } 453 454 /** 455 * Creates a new instance of the given type from the injector 456 * 457 * @param exchange the exchange 458 * @param type the given type 459 * @return the created instance of the given type 460 */ 461 public static <T> T newInstance(Exchange exchange, Class<T> type) { 462 return exchange.getContext().getInjector().newInstance(type); 463 } 464 465 /** 466 * Creates a Map of the variables which are made available to a script or template 467 * 468 * @param exchange the exchange to make available 469 * @return a Map populated with the require variables 470 */ 471 public static Map<String, Object> createVariableMap(Exchange exchange) { 472 Map<String, Object> answer = new HashMap<String, Object>(); 473 populateVariableMap(exchange, answer); 474 return answer; 475 } 476 477 /** 478 * Populates the Map with the variables which are made available to a script or template 479 * 480 * @param exchange the exchange to make available 481 * @param map the map to populate 482 */ 483 public static void populateVariableMap(Exchange exchange, Map<String, Object> map) { 484 map.put("exchange", exchange); 485 Message in = exchange.getIn(); 486 map.put("in", in); 487 map.put("request", in); 488 map.put("headers", in.getHeaders()); 489 map.put("body", in.getBody()); 490 if (isOutCapable(exchange)) { 491 // if we are out capable then set out and response as well 492 // however only grab OUT if it exists, otherwise reuse IN 493 // this prevents side effects to alter the Exchange if we force creating an OUT message 494 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 495 map.put("out", msg); 496 map.put("response", msg); 497 } 498 map.put("camelContext", exchange.getContext()); 499 } 500 501 /** 502 * Returns the MIME content type on the input message or null if one is not defined 503 * 504 * @param exchange the exchange 505 * @return the MIME content type 506 */ 507 public static String getContentType(Exchange exchange) { 508 return MessageHelper.getContentType(exchange.getIn()); 509 } 510 511 /** 512 * Returns the MIME content encoding on the input message or null if one is not defined 513 * 514 * @param exchange the exchange 515 * @return the MIME content encoding 516 */ 517 public static String getContentEncoding(Exchange exchange) { 518 return MessageHelper.getContentEncoding(exchange.getIn()); 519 } 520 521 /** 522 * Performs a lookup in the registry of the mandatory bean name and throws an exception if it could not be found 523 * 524 * @param exchange the exchange 525 * @param name the bean name 526 * @return the bean 527 * @throws NoSuchBeanException if no bean could be found in the registry 528 */ 529 public static Object lookupMandatoryBean(Exchange exchange, String name) throws NoSuchBeanException { 530 Object value = lookupBean(exchange, name); 531 if (value == null) { 532 throw new NoSuchBeanException(name); 533 } 534 return value; 535 } 536 537 /** 538 * Performs a lookup in the registry of the mandatory bean name and throws an exception if it could not be found 539 * 540 * @param exchange the exchange 541 * @param name the bean name 542 * @param type the expected bean type 543 * @return the bean 544 * @throws NoSuchBeanException if no bean could be found in the registry 545 */ 546 public static <T> T lookupMandatoryBean(Exchange exchange, String name, Class<T> type) { 547 T value = lookupBean(exchange, name, type); 548 if (value == null) { 549 throw new NoSuchBeanException(name); 550 } 551 return value; 552 } 553 554 /** 555 * Performs a lookup in the registry of the bean name 556 * 557 * @param exchange the exchange 558 * @param name the bean name 559 * @return the bean, or <tt>null</tt> if no bean could be found 560 */ 561 public static Object lookupBean(Exchange exchange, String name) { 562 return exchange.getContext().getRegistry().lookupByName(name); 563 } 564 565 /** 566 * Performs a lookup in the registry of the bean name and type 567 * 568 * @param exchange the exchange 569 * @param name the bean name 570 * @param type the expected bean type 571 * @return the bean, or <tt>null</tt> if no bean could be found 572 */ 573 public static <T> T lookupBean(Exchange exchange, String name, Class<T> type) { 574 return exchange.getContext().getRegistry().lookupByNameAndType(name, type); 575 } 576 577 /** 578 * Returns the first exchange in the given collection of exchanges which has the same exchange ID as the one given 579 * or null if none could be found 580 * 581 * @param exchanges the exchanges 582 * @param exchangeId the exchangeId to find 583 * @return matching exchange, or <tt>null</tt> if none found 584 */ 585 public static Exchange getExchangeById(Iterable<Exchange> exchanges, String exchangeId) { 586 for (Exchange exchange : exchanges) { 587 String id = exchange.getExchangeId(); 588 if (id != null && id.equals(exchangeId)) { 589 return exchange; 590 } 591 } 592 return null; 593 } 594 595 /** 596 * Prepares the exchanges for aggregation. 597 * <p/> 598 * This implementation will copy the OUT body to the IN body so when you do 599 * aggregation the body is <b>only</b> in the IN body to avoid confusing end users. 600 * 601 * @param oldExchange the old exchange 602 * @param newExchange the new exchange 603 */ 604 public static void prepareAggregation(Exchange oldExchange, Exchange newExchange) { 605 // move body/header from OUT to IN 606 if (oldExchange != null) { 607 if (oldExchange.hasOut()) { 608 oldExchange.setIn(oldExchange.getOut()); 609 oldExchange.setOut(null); 610 } 611 } 612 613 if (newExchange != null) { 614 if (newExchange.hasOut()) { 615 newExchange.setIn(newExchange.getOut()); 616 newExchange.setOut(null); 617 } 618 } 619 } 620 621 /** 622 * Checks whether the exchange has been failure handed 623 * 624 * @param exchange the exchange 625 * @return <tt>true</tt> if failure handled, <tt>false</tt> otherwise 626 */ 627 public static boolean isFailureHandled(Exchange exchange) { 628 return exchange.getProperty(Exchange.FAILURE_HANDLED, false, Boolean.class); 629 } 630 631 /** 632 * Checks whether the exchange {@link UnitOfWork} is exhausted 633 * 634 * @param exchange the exchange 635 * @return <tt>true</tt> if exhausted, <tt>false</tt> otherwise 636 */ 637 public static boolean isUnitOfWorkExhausted(Exchange exchange) { 638 return exchange.getProperty(Exchange.UNIT_OF_WORK_EXHAUSTED, false, Boolean.class); 639 } 640 641 /** 642 * Sets the exchange to be failure handled. 643 * 644 * @param exchange the exchange 645 */ 646 public static void setFailureHandled(Exchange exchange) { 647 exchange.setProperty(Exchange.FAILURE_HANDLED, Boolean.TRUE); 648 // clear exception since its failure handled 649 exchange.setException(null); 650 } 651 652 /** 653 * Checks whether the exchange is redelivery exhausted 654 * 655 * @param exchange the exchange 656 * @return <tt>true</tt> if exhausted, <tt>false</tt> otherwise 657 */ 658 public static boolean isRedeliveryExhausted(Exchange exchange) { 659 return exchange.getProperty(Exchange.REDELIVERY_EXHAUSTED, false, Boolean.class); 660 } 661 662 /** 663 * Checks whether the exchange {@link UnitOfWork} is redelivered 664 * 665 * @param exchange the exchange 666 * @return <tt>true</tt> if redelivered, <tt>false</tt> otherwise 667 */ 668 public static boolean isRedelivered(Exchange exchange) { 669 return exchange.getIn().hasHeaders() && exchange.getIn().getHeader(Exchange.REDELIVERED, false, Boolean.class); 670 } 671 672 /** 673 * Checks whether the exchange {@link UnitOfWork} has been interrupted during processing 674 * 675 * @param exchange the exchange 676 * @return <tt>true</tt> if interrupted, <tt>false</tt> otherwise 677 */ 678 public static boolean isInterrupted(Exchange exchange) { 679 Object value = exchange.getProperty(Exchange.INTERRUPTED); 680 return value != null && Boolean.TRUE == value; 681 } 682 683 /** 684 * Check whether or not stream caching is enabled for the given route or globally. 685 * 686 * @param exchange the exchange 687 * @return <tt>true</tt> if enabled, <tt>false</tt> otherwise 688 */ 689 public static boolean isStreamCachingEnabled(final Exchange exchange) { 690 if (exchange.getFromRouteId() == null) { 691 return exchange.getContext().getStreamCachingStrategy().isEnabled(); 692 } else { 693 return exchange.getContext().getRoute(exchange.getFromRouteId()).getRouteContext().isStreamCaching(); 694 } 695 } 696 697 /** 698 * Extracts the body from the given exchange. 699 * <p/> 700 * If the exchange pattern is provided it will try to honor it and retrieve the body 701 * from either IN or OUT according to the pattern. 702 * 703 * @param exchange the exchange 704 * @param pattern exchange pattern if given, can be <tt>null</tt> 705 * @return the result body, can be <tt>null</tt>. 706 * @throws CamelExecutionException is thrown if the processing of the exchange failed 707 */ 708 public static Object extractResultBody(Exchange exchange, ExchangePattern pattern) { 709 Object answer = null; 710 if (exchange != null) { 711 // rethrow if there was an exception during execution 712 if (exchange.getException() != null) { 713 throw ObjectHelper.wrapCamelExecutionException(exchange, exchange.getException()); 714 } 715 716 // result could have a fault message 717 if (hasFaultMessage(exchange)) { 718 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 719 answer = msg.getBody(); 720 return answer; 721 } 722 723 // okay no fault then return the response according to the pattern 724 // try to honor pattern if provided 725 boolean notOut = pattern != null && !pattern.isOutCapable(); 726 boolean hasOut = exchange.hasOut(); 727 if (hasOut && !notOut) { 728 // we have a response in out and the pattern is out capable 729 answer = exchange.getOut().getBody(); 730 } else if (!hasOut && exchange.getPattern() == ExchangePattern.InOptionalOut) { 731 // special case where the result is InOptionalOut and with no OUT response 732 // so we should return null to indicate this fact 733 answer = null; 734 } else { 735 // use IN as the response 736 answer = exchange.getIn().getBody(); 737 } 738 } 739 return answer; 740 } 741 742 /** 743 * Tests whether the exchange has a fault message set and that its not null. 744 * 745 * @param exchange the exchange 746 * @return <tt>true</tt> if fault message exists 747 */ 748 public static boolean hasFaultMessage(Exchange exchange) { 749 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 750 return msg.isFault() && msg.getBody() != null; 751 } 752 753 /** 754 * Tests whether the exchange has already been handled by the error handler 755 * 756 * @param exchange the exchange 757 * @return <tt>true</tt> if handled already by error handler, <tt>false</tt> otherwise 758 */ 759 public static boolean hasExceptionBeenHandledByErrorHandler(Exchange exchange) { 760 return Boolean.TRUE.equals(exchange.getProperty(Exchange.ERRORHANDLER_HANDLED)); 761 } 762 763 /** 764 * Extracts the body from the given future, that represents a handle to an asynchronous exchange. 765 * <p/> 766 * Will wait until the future task is complete. 767 * 768 * @param context the camel context 769 * @param future the future handle 770 * @param type the expected body response type 771 * @return the result body, can be <tt>null</tt>. 772 * @throws CamelExecutionException is thrown if the processing of the exchange failed 773 */ 774 public static <T> T extractFutureBody(CamelContext context, Future<?> future, Class<T> type) { 775 try { 776 return doExtractFutureBody(context, future.get(), type); 777 } catch (InterruptedException e) { 778 throw ObjectHelper.wrapRuntimeCamelException(e); 779 } catch (ExecutionException e) { 780 // execution failed due to an exception so rethrow the cause 781 throw ObjectHelper.wrapCamelExecutionException(null, e.getCause()); 782 } finally { 783 // its harmless to cancel if task is already completed 784 // and in any case we do not want to get hold of the task a 2nd time 785 // and its recommended to cancel according to Brian Goetz in his Java Concurrency in Practice book 786 future.cancel(true); 787 } 788 } 789 790 /** 791 * Extracts the body from the given future, that represents a handle to an asynchronous exchange. 792 * <p/> 793 * Will wait for the future task to complete, but waiting at most the timeout value. 794 * 795 * @param context the camel context 796 * @param future the future handle 797 * @param timeout timeout value 798 * @param unit timeout unit 799 * @param type the expected body response type 800 * @return the result body, can be <tt>null</tt>. 801 * @throws CamelExecutionException is thrown if the processing of the exchange failed 802 * @throws java.util.concurrent.TimeoutException is thrown if a timeout triggered 803 */ 804 public static <T> T extractFutureBody(CamelContext context, Future<?> future, long timeout, TimeUnit unit, Class<T> type) throws TimeoutException { 805 try { 806 if (timeout > 0) { 807 return doExtractFutureBody(context, future.get(timeout, unit), type); 808 } else { 809 return doExtractFutureBody(context, future.get(), type); 810 } 811 } catch (InterruptedException e) { 812 // execution failed due interruption so rethrow the cause 813 throw ObjectHelper.wrapCamelExecutionException(null, e); 814 } catch (ExecutionException e) { 815 // execution failed due to an exception so rethrow the cause 816 throw ObjectHelper.wrapCamelExecutionException(null, e.getCause()); 817 } finally { 818 // its harmless to cancel if task is already completed 819 // and in any case we do not want to get hold of the task a 2nd time 820 // and its recommended to cancel according to Brian Goetz in his Java Concurrency in Practice book 821 future.cancel(true); 822 } 823 } 824 825 private static <T> T doExtractFutureBody(CamelContext context, Object result, Class<T> type) { 826 if (result == null) { 827 return null; 828 } 829 if (type.isAssignableFrom(result.getClass())) { 830 return type.cast(result); 831 } 832 if (result instanceof Exchange) { 833 Exchange exchange = (Exchange) result; 834 Object answer = ExchangeHelper.extractResultBody(exchange, exchange.getPattern()); 835 return context.getTypeConverter().convertTo(type, exchange, answer); 836 } 837 return context.getTypeConverter().convertTo(type, result); 838 } 839 840 /** 841 * @deprecated use org.apache.camel.CamelExchangeException.createExceptionMessage instead 842 */ 843 @Deprecated 844 public static String createExceptionMessage(String message, Exchange exchange, Throwable cause) { 845 return CamelExchangeException.createExceptionMessage(message, exchange, cause); 846 } 847 848 /** 849 * Strategy to prepare results before next iterator or when we are complete, 850 * which is done by copying OUT to IN, so there is only an IN as input 851 * for the next iteration. 852 * 853 * @param exchange the exchange to prepare 854 */ 855 public static void prepareOutToIn(Exchange exchange) { 856 // we are routing using pipes and filters so we need to manually copy OUT to IN 857 if (exchange.hasOut()) { 858 exchange.setIn(exchange.getOut()); 859 exchange.setOut(null); 860 } 861 } 862 863 /** 864 * Gets both the messageId and exchangeId to be used for logging purposes. 865 * <p/> 866 * Logging both ids, can help to correlate exchanges which may be redelivered messages 867 * from for example a JMS broker. 868 * 869 * @param exchange the exchange 870 * @return a log message with both the messageId and exchangeId 871 */ 872 public static String logIds(Exchange exchange) { 873 String msgId = exchange.hasOut() ? exchange.getOut().getMessageId() : exchange.getIn().getMessageId(); 874 return "(MessageId: " + msgId + " on ExchangeId: " + exchange.getExchangeId() + ")"; 875 } 876 877 /** 878 * Copies the exchange but the copy will be tied to the given context 879 * 880 * @param exchange the source exchange 881 * @param context the camel context 882 * @return a copy with the given camel context 883 */ 884 public static Exchange copyExchangeAndSetCamelContext(Exchange exchange, CamelContext context) { 885 return copyExchangeAndSetCamelContext(exchange, context, true); 886 } 887 888 /** 889 * Copies the exchange but the copy will be tied to the given context 890 * 891 * @param exchange the source exchange 892 * @param context the camel context 893 * @param handover whether to handover on completions from the source to the copy 894 * @return a copy with the given camel context 895 */ 896 public static Exchange copyExchangeAndSetCamelContext(Exchange exchange, CamelContext context, boolean handover) { 897 DefaultExchange answer = new DefaultExchange(context, exchange.getPattern()); 898 if (exchange.hasProperties()) { 899 answer.setProperties(safeCopyProperties(exchange.getProperties())); 900 } 901 if (handover) { 902 // Need to hand over the completion for async invocation 903 exchange.handoverCompletions(answer); 904 } 905 answer.setIn(exchange.getIn().copy()); 906 if (exchange.hasOut()) { 907 answer.setOut(exchange.getOut().copy()); 908 } 909 answer.setException(exchange.getException()); 910 return answer; 911 } 912 913 /** 914 * Replaces the existing message with the new message 915 * 916 * @param exchange the exchange 917 * @param newMessage the new message 918 * @param outOnly whether to replace the message as OUT message 919 */ 920 public static void replaceMessage(Exchange exchange, Message newMessage, boolean outOnly) { 921 Message old = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 922 if (outOnly || exchange.hasOut()) { 923 exchange.setOut(newMessage); 924 } else { 925 exchange.setIn(newMessage); 926 } 927 928 // need to de-reference old from the exchange so it can be GC 929 if (old instanceof MessageSupport) { 930 ((MessageSupport) old).setExchange(null); 931 } 932 } 933 934 /** 935 * Gets the original IN {@link Message} this Unit of Work was started with. 936 * <p/> 937 * The original message is only returned if the option {@link org.apache.camel.RuntimeConfiguration#isAllowUseOriginalMessage()} 938 * is enabled. If its disabled, then <tt>null</tt> is returned. 939 * 940 * @return the original IN {@link Message}, or <tt>null</tt> if using original message is disabled. 941 */ 942 public static Message getOriginalInMessage(Exchange exchange) { 943 Message answer = null; 944 945 // try parent first 946 UnitOfWork uow = exchange.getProperty(Exchange.PARENT_UNIT_OF_WORK, UnitOfWork.class); 947 if (uow != null) { 948 answer = uow.getOriginalInMessage(); 949 } 950 // fallback to the current exchange 951 if (answer == null) { 952 uow = exchange.getUnitOfWork(); 953 if (uow != null) { 954 answer = uow.getOriginalInMessage(); 955 } 956 } 957 return answer; 958 } 959 960 @SuppressWarnings("unchecked") 961 private static Map<String, Object> safeCopyProperties(Map<String, Object> properties) { 962 if (properties == null) { 963 return null; 964 } 965 966 Map<String, Object> answer = new HashMap<>(properties); 967 968 // safe copy message history using a defensive copy 969 List<MessageHistory> history = (List<MessageHistory>) answer.remove(Exchange.MESSAGE_HISTORY); 970 if (history != null) { 971 answer.put(Exchange.MESSAGE_HISTORY, new LinkedList<>(history)); 972 } 973 974 return answer; 975 } 976}