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