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.builder; 018 019import java.io.PrintWriter; 020import java.io.StringWriter; 021import java.text.SimpleDateFormat; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Comparator; 025import java.util.Date; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029import java.util.Random; 030import java.util.Scanner; 031import java.util.Set; 032import java.util.TimeZone; 033import java.util.concurrent.atomic.AtomicReference; 034import java.util.function.BiFunction; 035import java.util.function.Function; 036import java.util.regex.Matcher; 037import java.util.regex.Pattern; 038import org.apache.camel.CamelContext; 039import org.apache.camel.Component; 040import org.apache.camel.Endpoint; 041import org.apache.camel.Exchange; 042import org.apache.camel.Expression; 043import org.apache.camel.InvalidPayloadException; 044import org.apache.camel.Message; 045import org.apache.camel.NoSuchEndpointException; 046import org.apache.camel.NoSuchLanguageException; 047import org.apache.camel.NoTypeConversionAvailableException; 048import org.apache.camel.Producer; 049import org.apache.camel.component.bean.BeanInvocation; 050import org.apache.camel.component.properties.PropertiesComponent; 051import org.apache.camel.language.bean.BeanLanguage; 052import org.apache.camel.language.simple.SimpleLanguage; 053import org.apache.camel.model.language.MethodCallExpression; 054import org.apache.camel.processor.DefaultExchangeFormatter; 055import org.apache.camel.spi.ExchangeFormatter; 056import org.apache.camel.spi.Language; 057import org.apache.camel.spi.RouteContext; 058import org.apache.camel.spi.UnitOfWork; 059import org.apache.camel.support.ExpressionAdapter; 060import org.apache.camel.support.TokenPairExpressionIterator; 061import org.apache.camel.support.TokenXMLExpressionIterator; 062import org.apache.camel.support.XMLTokenExpressionIterator; 063import org.apache.camel.util.CamelContextHelper; 064import org.apache.camel.util.ExchangeHelper; 065import org.apache.camel.util.FileUtil; 066import org.apache.camel.util.GroupIterator; 067import org.apache.camel.util.GroupTokenIterator; 068import org.apache.camel.util.IOHelper; 069import org.apache.camel.util.MessageHelper; 070import org.apache.camel.util.ObjectHelper; 071import org.apache.camel.util.OgnlHelper; 072import org.apache.camel.util.SkipIterator; 073import org.apache.camel.util.StringHelper; 074 075/** 076 * A helper class for working with <a href="http://camel.apache.org/expression.html">expressions</a>. 077 * 078 * @version 079 */ 080public final class ExpressionBuilder { 081 082 private static final Pattern OFFSET_PATTERN = Pattern.compile("([+-])([^+-]+)"); 083 084 /** 085 * Utility classes should not have a public constructor. 086 */ 087 private ExpressionBuilder() { 088 } 089 090 /** 091 * Returns an expression for the inbound message attachments 092 * 093 * @return an expression object which will return the inbound message attachments 094 */ 095 public static Expression attachmentObjectsExpression() { 096 return new ExpressionAdapter() { 097 public Object evaluate(Exchange exchange) { 098 return exchange.getIn().getAttachmentObjects(); 099 } 100 101 @Override 102 public String toString() { 103 return "attachmentObjects"; 104 } 105 }; 106 } 107 108 /** 109 * Returns an expression for the inbound message attachments 110 * 111 * @return an expression object which will return the inbound message attachments 112 */ 113 public static Expression attachmentObjectValuesExpression() { 114 return new ExpressionAdapter() { 115 public Object evaluate(Exchange exchange) { 116 return exchange.getIn().getAttachmentObjects().values(); 117 } 118 119 @Override 120 public String toString() { 121 return "attachmentObjects"; 122 } 123 }; 124 } 125 126 /** 127 * Returns an expression for the inbound message attachments 128 * 129 * @return an expression object which will return the inbound message attachments 130 */ 131 public static Expression attachmentsExpression() { 132 return new ExpressionAdapter() { 133 public Object evaluate(Exchange exchange) { 134 return exchange.getIn().getAttachments(); 135 } 136 137 @Override 138 public String toString() { 139 return "attachments"; 140 } 141 }; 142 } 143 144 /** 145 * Returns an expression for the inbound message attachments 146 * 147 * @return an expression object which will return the inbound message attachments 148 */ 149 public static Expression attachmentValuesExpression() { 150 return new ExpressionAdapter() { 151 public Object evaluate(Exchange exchange) { 152 return exchange.getIn().getAttachments().values(); 153 } 154 155 @Override 156 public String toString() { 157 return "attachments"; 158 } 159 }; 160 } 161 162 /** 163 * Returns an expression for the header value with the given name 164 * <p/> 165 * Will fallback and look in properties if not found in headers. 166 * 167 * @param headerName the name of the header the expression will return 168 * @return an expression object which will return the header value 169 */ 170 public static Expression headerExpression(final String headerName) { 171 return new ExpressionAdapter() { 172 public Object evaluate(Exchange exchange) { 173 String name = simpleExpression(headerName).evaluate(exchange, String.class); 174 Object header = exchange.getIn().getHeader(name); 175 if (header == null) { 176 // fall back on a property 177 header = exchange.getProperty(name); 178 } 179 return header; 180 } 181 182 @Override 183 public String toString() { 184 return "header(" + headerName + ")"; 185 } 186 }; 187 } 188 189 /** 190 * Returns an expression for the header value with the given name converted to the given type 191 * <p/> 192 * Will fallback and look in properties if not found in headers. 193 * 194 * @param headerName the name of the header the expression will return 195 * @param type the type to convert to 196 * @return an expression object which will return the header value 197 */ 198 public static <T> Expression headerExpression(final String headerName, final Class<T> type) { 199 return new ExpressionAdapter() { 200 public Object evaluate(Exchange exchange) { 201 String name = simpleExpression(headerName).evaluate(exchange, String.class); 202 Object header = exchange.getIn().getHeader(name, type); 203 if (header == null) { 204 // fall back on a property 205 header = exchange.getProperty(name, type); 206 } 207 return header; 208 } 209 210 @Override 211 public String toString() { 212 return "headerAs(" + headerName + ", " + type + ")"; 213 } 214 }; 215 } 216 217 /** 218 * Returns an expression for the header value with the given name converted to the given type 219 * <p/> 220 * Will fallback and look in properties if not found in headers. 221 * 222 * @param headerName the name of the header the expression will return 223 * @param typeName the type to convert to as a FQN class name 224 * @return an expression object which will return the header value 225 */ 226 public static Expression headerExpression(final String headerName, final String typeName) { 227 return new ExpressionAdapter() { 228 public Object evaluate(Exchange exchange) { 229 Class<?> type; 230 try { 231 String text = simpleExpression(typeName).evaluate(exchange, String.class); 232 type = exchange.getContext().getClassResolver().resolveMandatoryClass(text); 233 } catch (ClassNotFoundException e) { 234 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 235 } 236 237 String text = simpleExpression(headerName).evaluate(exchange, String.class); 238 Object header = exchange.getIn().getHeader(text, type); 239 if (header == null) { 240 // fall back on a property 241 header = exchange.getProperty(text, type); 242 } 243 return header; 244 } 245 246 @Override 247 public String toString() { 248 return "headerAs(" + headerName + ", " + typeName + ")"; 249 } 250 }; 251 } 252 253 /** 254 * Returns the expression for the exchanges inbound message header invoking methods defined 255 * in a simple OGNL notation 256 * 257 * @param ognl methods to invoke on the header in a simple OGNL syntax 258 */ 259 public static Expression headersOgnlExpression(final String ognl) { 260 return new KeyedOgnlExpressionAdapter(ognl, "headerOgnl(" + ognl + ")", 261 new KeyedOgnlExpressionAdapter.KeyedEntityRetrievalStrategy() { 262 public Object getKeyedEntity(Exchange exchange, String key) { 263 String text = simpleExpression(key).evaluate(exchange, String.class); 264 return exchange.getIn().getHeader(text); 265 } 266 }); 267 } 268 269 /** 270 * Returns an expression for the inbound message headers 271 * 272 * @return an expression object which will return the inbound headers 273 */ 274 public static Expression headersExpression() { 275 return new ExpressionAdapter() { 276 public Object evaluate(Exchange exchange) { 277 return exchange.getIn().getHeaders(); 278 } 279 280 @Override 281 public String toString() { 282 return "headers"; 283 } 284 }; 285 } 286 287 /** 288 * Returns an expression for the out header value with the given name 289 * <p/> 290 * Will fallback and look in properties if not found in headers. 291 * 292 * @param headerName the name of the header the expression will return 293 * @return an expression object which will return the header value 294 */ 295 public static Expression outHeaderExpression(final String headerName) { 296 return new ExpressionAdapter() { 297 public Object evaluate(Exchange exchange) { 298 if (!exchange.hasOut()) { 299 return null; 300 } 301 302 String text = simpleExpression(headerName).evaluate(exchange, String.class); 303 Message out = exchange.getOut(); 304 Object header = out.getHeader(text); 305 if (header == null) { 306 // let's try the exchange header 307 header = exchange.getProperty(text); 308 } 309 return header; 310 } 311 312 @Override 313 public String toString() { 314 return "outHeader(" + headerName + ")"; 315 } 316 }; 317 } 318 319 /** 320 * Returns an expression for the outbound message headers 321 * 322 * @return an expression object which will return the headers, will be <tt>null</tt> if the 323 * exchange is not out capable. 324 */ 325 public static Expression outHeadersExpression() { 326 return new ExpressionAdapter() { 327 public Object evaluate(Exchange exchange) { 328 // only get out headers if the MEP is out capable 329 if (ExchangeHelper.isOutCapable(exchange)) { 330 return exchange.getOut().getHeaders(); 331 } else { 332 return null; 333 } 334 } 335 336 @Override 337 public String toString() { 338 return "outHeaders"; 339 } 340 }; 341 } 342 343 /** 344 * Returns an expression for the exchange pattern 345 * 346 * @see org.apache.camel.Exchange#getPattern() 347 * @return an expression object which will return the exchange pattern 348 */ 349 public static Expression exchangePatternExpression() { 350 return new ExpressionAdapter() { 351 public Object evaluate(Exchange exchange) { 352 return exchange.getPattern(); 353 } 354 355 @Override 356 public String toString() { 357 return "exchangePattern"; 358 } 359 }; 360 } 361 362 /** 363 * Returns an expression for an exception set on the exchange 364 * 365 * @see Exchange#getException() 366 * @return an expression object which will return the exception set on the exchange 367 */ 368 public static Expression exchangeExceptionExpression() { 369 return new ExpressionAdapter() { 370 public Object evaluate(Exchange exchange) { 371 Exception exception = exchange.getException(); 372 if (exception == null) { 373 exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); 374 } 375 return exception; 376 } 377 378 @Override 379 public String toString() { 380 return "exchangeException"; 381 } 382 }; 383 } 384 385 /** 386 * Returns an expression for an exception set on the exchange 387 * <p/> 388 * Is used to get the caused exception that typically have been wrapped in some sort 389 * of Camel wrapper exception 390 * @param type the exception type 391 * @see Exchange#getException(Class) 392 * @return an expression object which will return the exception set on the exchange 393 */ 394 public static Expression exchangeExceptionExpression(final Class<Exception> type) { 395 return new ExpressionAdapter() { 396 public Object evaluate(Exchange exchange) { 397 Exception exception = exchange.getException(type); 398 if (exception == null) { 399 exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); 400 return ObjectHelper.getException(type, exception); 401 } 402 return exception; 403 } 404 405 @Override 406 public String toString() { 407 return "exchangeException[" + type + "]"; 408 } 409 }; 410 } 411 412 /** 413 * Returns the expression for the exchanges exception invoking methods defined 414 * in a simple OGNL notation 415 * 416 * @param ognl methods to invoke on the body in a simple OGNL syntax 417 */ 418 public static Expression exchangeExceptionOgnlExpression(final String ognl) { 419 return new ExpressionAdapter() { 420 public Object evaluate(Exchange exchange) { 421 Object exception = exchange.getException(); 422 if (exception == null) { 423 exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); 424 } 425 426 if (exception == null) { 427 return null; 428 } 429 430 // ognl is able to evaluate method name if it contains nested functions 431 // so we should not eager evaluate ognl as a string 432 return new MethodCallExpression(exception, ognl).evaluate(exchange); 433 } 434 435 @Override 436 public String toString() { 437 return "exchangeExceptionOgnl(" + ognl + ")"; 438 } 439 }; 440 } 441 442 /** 443 * Returns an expression for the type converter 444 * 445 * @return an expression object which will return the type converter 446 */ 447 public static Expression typeConverterExpression() { 448 return new ExpressionAdapter() { 449 public Object evaluate(Exchange exchange) { 450 return exchange.getContext().getTypeConverter(); 451 } 452 453 @Override 454 public String toString() { 455 return "typeConverter"; 456 } 457 }; 458 } 459 460 /** 461 * Returns an expression for the {@link org.apache.camel.spi.Registry} 462 * 463 * @return an expression object which will return the registry 464 */ 465 public static Expression registryExpression() { 466 return new ExpressionAdapter() { 467 public Object evaluate(Exchange exchange) { 468 return exchange.getContext().getRegistry(); 469 } 470 471 @Override 472 public String toString() { 473 return "registry"; 474 } 475 }; 476 } 477 478 /** 479 * Returns an expression for lookup a bean in the {@link org.apache.camel.spi.Registry} 480 * 481 * @return an expression object which will return the bean 482 */ 483 public static Expression refExpression(final String ref) { 484 return new ExpressionAdapter() { 485 public Object evaluate(Exchange exchange) { 486 String text = simpleExpression(ref).evaluate(exchange, String.class); 487 return exchange.getContext().getRegistry().lookupByName(text); 488 } 489 490 @Override 491 public String toString() { 492 return "ref(" + ref + ")"; 493 } 494 }; 495 } 496 497 /** 498 * Returns an expression for the {@link org.apache.camel.CamelContext} 499 * 500 * @return an expression object which will return the camel context 501 */ 502 public static Expression camelContextExpression() { 503 return new ExpressionAdapter() { 504 public Object evaluate(Exchange exchange) { 505 return exchange.getContext(); 506 } 507 508 @Override 509 public String toString() { 510 return "camelContext"; 511 } 512 }; 513 } 514 515 /** 516 * Returns an expression for the {@link org.apache.camel.CamelContext} name 517 * 518 * @return an expression object which will return the camel context name 519 */ 520 public static Expression camelContextNameExpression() { 521 return new ExpressionAdapter() { 522 public Object evaluate(Exchange exchange) { 523 return exchange.getContext().getName(); 524 } 525 526 @Override 527 public String toString() { 528 return "camelContextName"; 529 } 530 }; 531 } 532 533 /** 534 * Returns an expression for an exception message set on the exchange 535 * 536 * @see <tt>Exchange.getException().getMessage()</tt> 537 * @return an expression object which will return the exception message set on the exchange 538 */ 539 public static Expression exchangeExceptionMessageExpression() { 540 return new ExpressionAdapter() { 541 public Object evaluate(Exchange exchange) { 542 Exception exception = exchange.getException(); 543 if (exception == null) { 544 exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); 545 } 546 return exception != null ? exception.getMessage() : null; 547 } 548 549 @Override 550 public String toString() { 551 return "exchangeExceptionMessage"; 552 } 553 }; 554 } 555 556 /** 557 * Returns an expression for an exception stacktrace set on the exchange 558 * 559 * @return an expression object which will return the exception stacktrace set on the exchange 560 */ 561 public static Expression exchangeExceptionStackTraceExpression() { 562 return new ExpressionAdapter() { 563 public Object evaluate(Exchange exchange) { 564 Exception exception = exchange.getException(); 565 if (exception == null) { 566 exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); 567 } 568 if (exception != null) { 569 StringWriter sw = new StringWriter(); 570 PrintWriter pw = new PrintWriter(sw); 571 exception.printStackTrace(pw); 572 IOHelper.close(pw, sw); 573 return sw.toString(); 574 } else { 575 return null; 576 } 577 } 578 579 @Override 580 public String toString() { 581 return "exchangeExceptionStackTrace"; 582 } 583 }; 584 } 585 586 /** 587 * Returns an expression for the property value of exchange with the given name 588 * 589 * @param propertyName the name of the property the expression will return 590 * @return an expression object which will return the property value 591 * @deprecated use {@link #exchangePropertyExpression(String)} instead 592 */ 593 @Deprecated 594 public static Expression propertyExpression(final String propertyName) { 595 return new ExpressionAdapter() { 596 public Object evaluate(Exchange exchange) { 597 String text = simpleExpression(propertyName).evaluate(exchange, String.class); 598 return exchange.getProperty(text); 599 } 600 601 @Override 602 public String toString() { 603 return "exchangeProperty(" + propertyName + ")"; 604 } 605 }; 606 } 607 608 /** 609 * Returns an expression for the property value of exchange with the given name 610 * 611 * @param propertyName the name of the property the expression will return 612 * @return an expression object which will return the property value 613 */ 614 public static Expression exchangePropertyExpression(final String propertyName) { 615 return new ExpressionAdapter() { 616 public Object evaluate(Exchange exchange) { 617 String text = simpleExpression(propertyName).evaluate(exchange, String.class); 618 return exchange.getProperty(text); 619 } 620 621 @Override 622 public String toString() { 623 return "exchangeProperty(" + propertyName + ")"; 624 } 625 }; 626 } 627 628 /** 629 * Returns an expression for the property value of exchange with the given name invoking methods defined 630 * in a simple OGNL notation 631 * 632 * @param ognl methods to invoke on the property in a simple OGNL syntax 633 */ 634 public static Expression propertyOgnlExpression(final String ognl) { 635 return new KeyedOgnlExpressionAdapter(ognl, "propertyOgnl(" + ognl + ")", 636 new KeyedOgnlExpressionAdapter.KeyedEntityRetrievalStrategy() { 637 public Object getKeyedEntity(Exchange exchange, String key) { 638 String text = simpleExpression(key).evaluate(exchange, String.class); 639 return exchange.getProperty(text); 640 } 641 }); 642 } 643 644 /** 645 * Returns an expression for the properties of exchange 646 * 647 * @return an expression object which will return the properties 648 * @deprecated use {@link #exchangeExceptionExpression()} instead 649 */ 650 @Deprecated 651 public static Expression propertiesExpression() { 652 return exchangeExceptionExpression(); 653 } 654 655 /** 656 * Returns an expression for the exchange properties of exchange 657 * 658 * @return an expression object which will return the exchange properties 659 */ 660 public static Expression exchangePropertiesExpression() { 661 return new ExpressionAdapter() { 662 public Object evaluate(Exchange exchange) { 663 return exchange.getProperties(); 664 } 665 666 @Override 667 public String toString() { 668 return "exchangeProperties"; 669 } 670 }; 671 } 672 673 /** 674 * Returns an expression for the properties of the camel context 675 * 676 * @return an expression object which will return the properties 677 */ 678 public static Expression camelContextPropertiesExpression() { 679 return new ExpressionAdapter() { 680 public Object evaluate(Exchange exchange) { 681 return exchange.getContext().getGlobalOptions(); 682 } 683 684 @Override 685 public String toString() { 686 return "camelContextProperties"; 687 } 688 }; 689 } 690 691 /** 692 * Returns an expression for the property value of the camel context with the given name 693 * 694 * @param propertyName the name of the property the expression will return 695 * @return an expression object which will return the property value 696 */ 697 public static Expression camelContextPropertyExpression(final String propertyName) { 698 return new ExpressionAdapter() { 699 public Object evaluate(Exchange exchange) { 700 String text = simpleExpression(propertyName).evaluate(exchange, String.class); 701 return exchange.getContext().getGlobalOption(text); 702 } 703 704 @Override 705 public String toString() { 706 return "camelContextProperty(" + propertyName + ")"; 707 } 708 }; 709 } 710 711 /** 712 * Returns an expression for a system property value with the given name 713 * 714 * @param propertyName the name of the system property the expression will return 715 * @return an expression object which will return the system property value 716 */ 717 public static Expression systemPropertyExpression(final String propertyName) { 718 return systemPropertyExpression(propertyName, null); 719 } 720 721 /** 722 * Returns an expression for a system property value with the given name 723 * 724 * @param propertyName the name of the system property the expression will return 725 * @param defaultValue default value to return if no system property exists 726 * @return an expression object which will return the system property value 727 */ 728 public static Expression systemPropertyExpression(final String propertyName, 729 final String defaultValue) { 730 return new ExpressionAdapter() { 731 public Object evaluate(Exchange exchange) { 732 String text = simpleExpression(propertyName).evaluate(exchange, String.class); 733 String text2 = simpleExpression(defaultValue).evaluate(exchange, String.class); 734 return System.getProperty(text, text2); 735 } 736 737 @Override 738 public String toString() { 739 return "systemProperty(" + propertyName + ")"; 740 } 741 }; 742 } 743 744 /** 745 * Returns an expression for a system environment value with the given name 746 * 747 * @param propertyName the name of the system environment the expression will return 748 * @return an expression object which will return the system property value 749 */ 750 public static Expression systemEnvironmentExpression(final String propertyName) { 751 return systemEnvironmentExpression(propertyName, null); 752 } 753 754 /** 755 * Returns an expression for a system environment value with the given name 756 * 757 * @param propertyName the name of the system environment the expression will return 758 * @param defaultValue default value to return if no system environment exists 759 * @return an expression object which will return the system environment value 760 */ 761 public static Expression systemEnvironmentExpression(final String propertyName, 762 final String defaultValue) { 763 return new ExpressionAdapter() { 764 public Object evaluate(Exchange exchange) { 765 String text = simpleExpression(propertyName).evaluate(exchange, String.class); 766 String answer = System.getenv(text.toUpperCase()); 767 if (answer == null) { 768 String text2 = simpleExpression(defaultValue).evaluate(exchange, String.class); 769 answer = text2; 770 } 771 return answer; 772 } 773 774 @Override 775 public String toString() { 776 return "systemEnvironment(" + propertyName + ")"; 777 } 778 }; 779 } 780 781 /** 782 * Returns an expression for the constant value 783 * 784 * @param value the value the expression will return 785 * @return an expression object which will return the constant value 786 */ 787 public static Expression constantExpression(final Object value) { 788 return new ExpressionAdapter() { 789 public Object evaluate(Exchange exchange) { 790 return value; 791 } 792 793 @Override 794 public String toString() { 795 return "" + value; 796 } 797 }; 798 } 799 800 /** 801 * Returns an expression for evaluating the expression/predicate using the given language 802 * 803 * @param expression the expression or predicate 804 * @return an expression object which will evaluate the expression/predicate using the given language 805 */ 806 public static Expression languageExpression(final String language, final String expression) { 807 return new ExpressionAdapter() { 808 public Object evaluate(Exchange exchange) { 809 Language lan = exchange.getContext().resolveLanguage(language); 810 if (lan != null) { 811 return lan.createExpression(expression).evaluate(exchange, Object.class); 812 } else { 813 throw new NoSuchLanguageException(language); 814 } 815 } 816 817 @Override 818 public boolean matches(Exchange exchange) { 819 Language lan = exchange.getContext().resolveLanguage(language); 820 if (lan != null) { 821 return lan.createPredicate(expression).matches(exchange); 822 } else { 823 throw new NoSuchLanguageException(language); 824 } 825 } 826 827 @Override 828 public String toString() { 829 return "language[" + language + ":" + expression + "]"; 830 } 831 }; 832 } 833 834 /** 835 * Returns an expression for a type value 836 * 837 * @param name the type name 838 * @return an expression object which will return the type value 839 */ 840 public static Expression typeExpression(final String name) { 841 return new ExpressionAdapter() { 842 public Object evaluate(Exchange exchange) { 843 // it may refer to a class type 844 String text = simpleExpression(name).evaluate(exchange, String.class); 845 Class<?> type = exchange.getContext().getClassResolver().resolveClass(text); 846 if (type != null) { 847 return type; 848 } 849 850 int pos = text.lastIndexOf("."); 851 if (pos > 0) { 852 String before = text.substring(0, pos); 853 String after = text.substring(pos + 1); 854 type = exchange.getContext().getClassResolver().resolveClass(before); 855 if (type != null) { 856 return ObjectHelper.lookupConstantFieldValue(type, after); 857 } 858 } 859 860 throw ObjectHelper.wrapCamelExecutionException(exchange, new ClassNotFoundException("Cannot find type " + text)); 861 } 862 863 @Override 864 public String toString() { 865 return "type:" + name; 866 } 867 }; 868 } 869 870 /** 871 * Returns an expression that caches the evaluation of another expression 872 * and returns the cached value, to avoid re-evaluating the expression. 873 * 874 * @param expression the target expression to cache 875 * @return the cached value 876 */ 877 public static Expression cacheExpression(final Expression expression) { 878 return new ExpressionAdapter() { 879 private final AtomicReference<Object> cache = new AtomicReference<Object>(); 880 881 public Object evaluate(Exchange exchange) { 882 Object answer = cache.get(); 883 if (answer == null) { 884 answer = expression.evaluate(exchange, Object.class); 885 cache.set(answer); 886 } 887 return answer; 888 } 889 890 @Override 891 public String toString() { 892 return expression.toString(); 893 } 894 }; 895 } 896 897 /** 898 * Returns the expression for the exchanges inbound message body 899 */ 900 public static Expression bodyExpression() { 901 return new ExpressionAdapter() { 902 public Object evaluate(Exchange exchange) { 903 return exchange.getIn().getBody(); 904 } 905 906 @Override 907 public String toString() { 908 return "body"; 909 } 910 }; 911 } 912 913 /** 914 * Returns a functional expression for the exchanges inbound message body 915 */ 916 public static Expression bodyExpression(final Function<Object, Object> function) { 917 return new ExpressionAdapter() { 918 public Object evaluate(Exchange exchange) { 919 return function.apply( 920 exchange.getIn().getBody() 921 ); 922 } 923 924 @Override 925 public String toString() { 926 return "bodyExpression"; 927 } 928 }; 929 } 930 931 /** 932 * Returns a functional expression for the exchanges inbound message body and headers 933 */ 934 public static Expression bodyExpression(final BiFunction<Object, Map<String, Object>, Object> function) { 935 return new ExpressionAdapter() { 936 public Object evaluate(Exchange exchange) { 937 return function.apply( 938 exchange.getIn().getBody(), 939 exchange.getIn().getHeaders() 940 ); 941 } 942 943 @Override 944 public String toString() { 945 return "bodyExpression"; 946 } 947 }; 948 } 949 950 /** 951 * Returns a functional expression for the exchanges inbound message body converted to a desired type 952 */ 953 public static <T> Expression bodyExpression(final Class<T> bodyType, final Function<T, Object> function) { 954 return new ExpressionAdapter() { 955 public Object evaluate(Exchange exchange) { 956 return function.apply( 957 exchange.getIn().getBody(bodyType) 958 ); 959 } 960 961 @Override 962 public String toString() { 963 return "bodyExpression (" + bodyType + ")"; 964 } 965 }; 966 } 967 968 /** 969 * Returns a functional expression for the exchanges inbound message body converted to a desired type and headers 970 */ 971 public static <T> Expression bodyExpression(final Class<T> bodyType, final BiFunction<T, Map<String, Object>, Object> function) { 972 return new ExpressionAdapter() { 973 public Object evaluate(Exchange exchange) { 974 return function.apply( 975 exchange.getIn().getBody(bodyType), 976 exchange.getIn().getHeaders() 977 ); 978 } 979 980 @Override 981 public String toString() { 982 return "bodyExpression (" + bodyType + ")"; 983 } 984 }; 985 } 986 987 /** 988 * Returns the expression for the exchanges inbound message body invoking methods defined 989 * in a simple OGNL notation 990 * 991 * @param ognl methods to invoke on the body in a simple OGNL syntax 992 */ 993 public static Expression bodyOgnlExpression(final String ognl) { 994 return new ExpressionAdapter() { 995 public Object evaluate(Exchange exchange) { 996 Object body = exchange.getIn().getBody(); 997 if (body == null) { 998 return null; 999 } 1000 // ognl is able to evaluate method name if it contains nested functions 1001 // so we should not eager evaluate ognl as a string 1002 return new MethodCallExpression(body, ognl).evaluate(exchange); 1003 } 1004 1005 @Override 1006 public String toString() { 1007 return "bodyOgnl(" + ognl + ")"; 1008 } 1009 }; 1010 } 1011 1012 /** 1013 * Returns the expression for invoking a method (support OGNL syntax) on the given expression 1014 * 1015 * @param exp the expression to evaluate and invoke the method on its result 1016 * @param ognl methods to invoke on the evaluated expression in a simple OGNL syntax 1017 */ 1018 public static Expression ognlExpression(final Expression exp, final String ognl) { 1019 return new ExpressionAdapter() { 1020 public Object evaluate(Exchange exchange) { 1021 Object value = exp.evaluate(exchange, Object.class); 1022 if (value == null) { 1023 return null; 1024 } 1025 // ognl is able to evaluate method name if it contains nested functions 1026 // so we should not eager evaluate ognl as a string 1027 return new MethodCallExpression(value, ognl).evaluate(exchange); 1028 } 1029 1030 @Override 1031 public String toString() { 1032 return "ognl(" + exp + ", " + ognl + ")"; 1033 } 1034 }; 1035 } 1036 1037 /** 1038 * Returns the expression for the exchanges camelContext invoking methods defined 1039 * in a simple OGNL notation 1040 * 1041 * @param ognl methods to invoke on the context in a simple OGNL syntax 1042 */ 1043 public static Expression camelContextOgnlExpression(final String ognl) { 1044 return new ExpressionAdapter() { 1045 public Object evaluate(Exchange exchange) { 1046 CamelContext context = exchange.getContext(); 1047 if (context == null) { 1048 return null; 1049 } 1050 // ognl is able to evaluate method name if it contains nested functions 1051 // so we should not eager evaluate ognl as a string 1052 return new MethodCallExpression(context, ognl).evaluate(exchange); 1053 } 1054 1055 @Override 1056 public String toString() { 1057 return "camelContextOgnl(" + ognl + ")"; 1058 } 1059 }; 1060 } 1061 1062 /** 1063 * Returns the expression for the exchange invoking methods defined 1064 * in a simple OGNL notation 1065 * 1066 * @param ognl methods to invoke on the exchange in a simple OGNL syntax 1067 */ 1068 public static Expression exchangeOgnlExpression(final String ognl) { 1069 return new ExpressionAdapter() { 1070 public Object evaluate(Exchange exchange) { 1071 // ognl is able to evaluate method name if it contains nested functions 1072 // so we should not eager evaluate ognl as a string 1073 return new MethodCallExpression(exchange, ognl).evaluate(exchange); 1074 } 1075 1076 @Override 1077 public String toString() { 1078 return "exchangeOgnl(" + ognl + ")"; 1079 } 1080 }; 1081 } 1082 1083 /** 1084 * Returns the expression for the exchanges inbound message body converted 1085 * to the given type 1086 */ 1087 public static <T> Expression bodyExpression(final Class<T> type) { 1088 return new ExpressionAdapter() { 1089 public Object evaluate(Exchange exchange) { 1090 return exchange.getIn().getBody(type); 1091 } 1092 1093 @Override 1094 public String toString() { 1095 return "bodyAs[" + type.getName() + "]"; 1096 } 1097 }; 1098 } 1099 1100 /** 1101 * Returns the expression for the exchanges inbound message body converted 1102 * to the given type 1103 */ 1104 public static Expression bodyExpression(final String name) { 1105 return new ExpressionAdapter() { 1106 public Object evaluate(Exchange exchange) { 1107 String text = simpleExpression(name).evaluate(exchange, String.class); 1108 Class<?> type; 1109 try { 1110 type = exchange.getContext().getClassResolver().resolveMandatoryClass(text); 1111 } catch (ClassNotFoundException e) { 1112 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 1113 } 1114 return exchange.getIn().getBody(type); 1115 } 1116 1117 @Override 1118 public String toString() { 1119 return "bodyAs[" + name + "]"; 1120 } 1121 }; 1122 } 1123 1124 /** 1125 * Returns the expression for the exchanges inbound message body converted 1126 * to the given type and invoking methods on the converted body defined in a simple OGNL notation 1127 */ 1128 public static Expression bodyOgnlExpression(final String name, final String ognl) { 1129 return new ExpressionAdapter() { 1130 public Object evaluate(Exchange exchange) { 1131 String text = simpleExpression(name).evaluate(exchange, String.class); 1132 Class<?> type; 1133 try { 1134 type = exchange.getContext().getClassResolver().resolveMandatoryClass(text); 1135 } catch (ClassNotFoundException e) { 1136 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 1137 } 1138 Object body = exchange.getIn().getBody(type); 1139 if (body != null) { 1140 // ognl is able to evaluate method name if it contains nested functions 1141 // so we should not eager evaluate ognl as a string 1142 MethodCallExpression call = new MethodCallExpression(exchange, ognl); 1143 // set the instance to use 1144 call.setInstance(body); 1145 return call.evaluate(exchange); 1146 } else { 1147 return null; 1148 } 1149 } 1150 1151 @Override 1152 public String toString() { 1153 return "bodyOgnlAs[" + name + "](" + ognl + ")"; 1154 } 1155 }; 1156 } 1157 1158 /** 1159 * Returns the expression for the exchanges inbound message body converted 1160 * to the given type 1161 */ 1162 public static Expression mandatoryBodyExpression(final String name) { 1163 return new ExpressionAdapter() { 1164 public Object evaluate(Exchange exchange) { 1165 String text = simpleExpression(name).evaluate(exchange, String.class); 1166 Class<?> type; 1167 try { 1168 type = exchange.getContext().getClassResolver().resolveMandatoryClass(text); 1169 } catch (ClassNotFoundException e) { 1170 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 1171 } 1172 try { 1173 return exchange.getIn().getMandatoryBody(type); 1174 } catch (InvalidPayloadException e) { 1175 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 1176 } 1177 } 1178 1179 @Override 1180 public String toString() { 1181 return "mandatoryBodyAs[" + name + "]"; 1182 } 1183 }; 1184 } 1185 1186 /** 1187 * Returns the expression for the exchanges inbound message body converted 1188 * to the given type and invoking methods on the converted body defined in a simple OGNL notation 1189 */ 1190 public static Expression mandatoryBodyOgnlExpression(final String name, final String ognl) { 1191 return new ExpressionAdapter() { 1192 public Object evaluate(Exchange exchange) { 1193 String text = simpleExpression(name).evaluate(exchange, String.class); 1194 Class<?> type; 1195 try { 1196 type = exchange.getContext().getClassResolver().resolveMandatoryClass(text); 1197 } catch (ClassNotFoundException e) { 1198 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 1199 } 1200 Object body; 1201 try { 1202 body = exchange.getIn().getMandatoryBody(type); 1203 } catch (InvalidPayloadException e) { 1204 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 1205 } 1206 // ognl is able to evaluate method name if it contains nested functions 1207 // so we should not eager evaluate ognl as a string 1208 MethodCallExpression call = new MethodCallExpression(exchange, ognl); 1209 // set the instance to use 1210 call.setInstance(body); 1211 return call.evaluate(exchange); 1212 } 1213 1214 @Override 1215 public String toString() { 1216 return "mandatoryBodyAs[" + name + "](" + ognl + ")"; 1217 } 1218 }; 1219 } 1220 1221 /** 1222 * Returns the expression for the current thread name 1223 */ 1224 public static Expression threadNameExpression() { 1225 return new ExpressionAdapter() { 1226 public Object evaluate(Exchange exchange) { 1227 return Thread.currentThread().getName(); 1228 } 1229 1230 @Override 1231 public String toString() { 1232 return "threadName"; 1233 } 1234 }; 1235 } 1236 1237 /** 1238 * Returns the expression for the {@code null} value 1239 */ 1240 public static Expression nullExpression() { 1241 return new ExpressionAdapter() { 1242 public Object evaluate(Exchange exchange) { 1243 return null; 1244 } 1245 1246 @Override 1247 public String toString() { 1248 return "null"; 1249 } 1250 }; 1251 } 1252 1253 /** 1254 * Returns the expression for the exchanges inbound message body converted 1255 * to the given type. 1256 * <p/> 1257 * Does <b>not</b> allow null bodies. 1258 */ 1259 public static <T> Expression mandatoryBodyExpression(final Class<T> type) { 1260 return mandatoryBodyExpression(type, false); 1261 } 1262 1263 /** 1264 * Returns the expression for the exchanges inbound message body converted 1265 * to the given type 1266 * 1267 * @param type the type 1268 * @param nullBodyAllowed whether null bodies is allowed and if so a null is returned, 1269 * otherwise an exception is thrown 1270 */ 1271 public static <T> Expression mandatoryBodyExpression(final Class<T> type, final boolean nullBodyAllowed) { 1272 return new ExpressionAdapter() { 1273 public Object evaluate(Exchange exchange) { 1274 if (nullBodyAllowed) { 1275 if (exchange.getIn().getBody() == null) { 1276 return null; 1277 } 1278 1279 // if its a bean invocation then if it has no arguments then it should be threaded as null body allowed 1280 if (exchange.getIn().getBody() instanceof BeanInvocation) { 1281 // BeanInvocation would be stored directly as the message body 1282 // do not force any type conversion attempts as it would just be unnecessary and cost a bit performance 1283 // so a regular instanceof check is sufficient 1284 BeanInvocation bi = (BeanInvocation) exchange.getIn().getBody(); 1285 if (bi.getArgs() == null || bi.getArgs().length == 0 || bi.getArgs()[0] == null) { 1286 return null; 1287 } 1288 } 1289 } 1290 1291 try { 1292 return exchange.getIn().getMandatoryBody(type); 1293 } catch (InvalidPayloadException e) { 1294 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 1295 } 1296 } 1297 1298 @Override 1299 public String toString() { 1300 return "mandatoryBodyAs[" + type.getName() + "]"; 1301 } 1302 }; 1303 } 1304 1305 /** 1306 * Returns the expression for the exchanges inbound message body type 1307 */ 1308 public static Expression bodyTypeExpression() { 1309 return new ExpressionAdapter() { 1310 public Object evaluate(Exchange exchange) { 1311 return exchange.getIn().getBody().getClass(); 1312 } 1313 1314 @Override 1315 public String toString() { 1316 return "bodyType"; 1317 } 1318 }; 1319 } 1320 1321 /** 1322 * Returns the expression for the out messages body 1323 */ 1324 public static Expression outBodyExpression() { 1325 return new ExpressionAdapter() { 1326 public Object evaluate(Exchange exchange) { 1327 if (exchange.hasOut()) { 1328 return exchange.getOut().getBody(); 1329 } else { 1330 return null; 1331 } 1332 } 1333 1334 @Override 1335 public String toString() { 1336 return "outBody"; 1337 } 1338 }; 1339 } 1340 1341 /** 1342 * Returns the expression for the exchanges outbound message body converted 1343 * to the given type 1344 */ 1345 public static <T> Expression outBodyExpression(final Class<T> type) { 1346 return new ExpressionAdapter() { 1347 public Object evaluate(Exchange exchange) { 1348 if (exchange.hasOut()) { 1349 return exchange.getOut().getBody(type); 1350 } else { 1351 return null; 1352 } 1353 } 1354 1355 @Override 1356 public String toString() { 1357 return "outBodyAs[" + type.getName() + "]"; 1358 } 1359 }; 1360 } 1361 1362 /** 1363 * Returns the expression for the fault messages body 1364 */ 1365 public static Expression faultBodyExpression() { 1366 return new ExpressionAdapter() { 1367 public Object evaluate(Exchange exchange) { 1368 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 1369 return msg.isFault() ? msg.getBody() : null; 1370 } 1371 1372 @Override 1373 public String toString() { 1374 return "faultBody"; 1375 } 1376 }; 1377 } 1378 1379 /** 1380 * Returns the expression for the exchanges fault message body converted 1381 * to the given type 1382 */ 1383 public static <T> Expression faultBodyExpression(final Class<T> type) { 1384 return new ExpressionAdapter() { 1385 public Object evaluate(Exchange exchange) { 1386 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 1387 return msg.isFault() ? msg.getBody(type) : null; 1388 } 1389 1390 @Override 1391 public String toString() { 1392 return "faultBodyAs[" + type.getName() + "]"; 1393 } 1394 }; 1395 } 1396 1397 /** 1398 * Returns the expression for the exchange 1399 */ 1400 public static Expression exchangeExpression() { 1401 return new ExpressionAdapter() { 1402 public Object evaluate(Exchange exchange) { 1403 return exchange; 1404 } 1405 1406 @Override 1407 public String toString() { 1408 return "exchange"; 1409 } 1410 }; 1411 } 1412 1413 /** 1414 * Returns a functional expression for the exchange 1415 */ 1416 public static Expression exchangeExpression(final Function<Exchange, Object> function) { 1417 return new ExpressionAdapter() { 1418 public Object evaluate(Exchange exchange) { 1419 return function.apply(exchange); 1420 } 1421 1422 @Override 1423 public String toString() { 1424 return "exchangeExpression"; 1425 } 1426 }; 1427 } 1428 1429 /** 1430 * Returns the expression for the IN message 1431 */ 1432 public static Expression messageExpression() { 1433 return inMessageExpression(); 1434 } 1435 1436 /** 1437 * Returns a functional expression for the IN message 1438 */ 1439 public static Expression messageExpression(final Function<Message, Object> function) { 1440 return inMessageExpression(function); 1441 } 1442 1443 /** 1444 * Returns the expression for the IN message 1445 */ 1446 public static Expression inMessageExpression() { 1447 return new ExpressionAdapter() { 1448 public Object evaluate(Exchange exchange) { 1449 return exchange.getIn(); 1450 } 1451 1452 @Override 1453 public String toString() { 1454 return "inMessage"; 1455 } 1456 }; 1457 } 1458 1459 /** 1460 * Returns a functional expression for the IN message 1461 */ 1462 public static Expression inMessageExpression(final Function<Message, Object> function) { 1463 return new ExpressionAdapter() { 1464 public Object evaluate(Exchange exchange) { 1465 return function.apply(exchange.getIn()); 1466 } 1467 1468 @Override 1469 public String toString() { 1470 return "inMessageExpression"; 1471 } 1472 }; 1473 } 1474 1475 /** 1476 * Returns the expression for the OUT message 1477 */ 1478 public static Expression outMessageExpression() { 1479 return new ExpressionAdapter() { 1480 public Object evaluate(Exchange exchange) { 1481 return exchange.getOut(); 1482 } 1483 1484 @Override 1485 public String toString() { 1486 return "outMessage"; 1487 } 1488 }; 1489 } 1490 1491 /** 1492 * Returns a functional expression for the OUT message 1493 */ 1494 public static Expression outMessageExpression(final Function<Message, Object> function) { 1495 return new ExpressionAdapter() { 1496 public Object evaluate(Exchange exchange) { 1497 return function.apply(exchange.getOut()); 1498 } 1499 1500 @Override 1501 public String toString() { 1502 return "outMessageExpression"; 1503 } 1504 }; 1505 } 1506 1507 /** 1508 * Returns an expression which converts the given expression to the given type 1509 */ 1510 public static Expression convertToExpression(final Expression expression, final Class<?> type) { 1511 return new ExpressionAdapter() { 1512 public Object evaluate(Exchange exchange) { 1513 if (type != null) { 1514 return expression.evaluate(exchange, type); 1515 } else { 1516 return expression; 1517 } 1518 } 1519 1520 @Override 1521 public String toString() { 1522 return "" + expression; 1523 } 1524 }; 1525 } 1526 1527 /** 1528 * Returns an expression which converts the given expression to the given type the type 1529 * expression is evaluated to 1530 */ 1531 public static Expression convertToExpression(final Expression expression, final Expression type) { 1532 return new ExpressionAdapter() { 1533 public Object evaluate(Exchange exchange) { 1534 Object result = type.evaluate(exchange, Object.class); 1535 if (result != null) { 1536 return expression.evaluate(exchange, result.getClass()); 1537 } else { 1538 return expression; 1539 } 1540 } 1541 1542 @Override 1543 public String toString() { 1544 return "" + expression; 1545 } 1546 }; 1547 } 1548 1549 /** 1550 * Returns a tokenize expression which will tokenize the string with the 1551 * given token 1552 */ 1553 public static Expression tokenizeExpression(final Expression expression, 1554 final String token) { 1555 return new ExpressionAdapter() { 1556 public Object evaluate(Exchange exchange) { 1557 String text = simpleExpression(token).evaluate(exchange, String.class); 1558 Object value = expression.evaluate(exchange, Object.class); 1559 Scanner scanner = ObjectHelper.getScanner(exchange, value); 1560 scanner.useDelimiter(text); 1561 return scanner; 1562 } 1563 1564 @Override 1565 public String toString() { 1566 return "tokenize(" + expression + ", " + token + ")"; 1567 } 1568 }; 1569 } 1570 1571 /** 1572 * Returns an expression that skips the first element 1573 */ 1574 public static Expression skipFirstExpression(final Expression expression) { 1575 return new ExpressionAdapter() { 1576 public Object evaluate(Exchange exchange) { 1577 Object value = expression.evaluate(exchange, Object.class); 1578 Iterator it = exchange.getContext().getTypeConverter().tryConvertTo(Iterator.class, exchange, value); 1579 if (it != null) { 1580 // skip first 1581 it.next(); 1582 return it; 1583 } else { 1584 return value; 1585 } 1586 } 1587 1588 @Override 1589 public String toString() { 1590 return "skipFirst(" + expression + ")"; 1591 } 1592 }; 1593 } 1594 1595 /** 1596 * Returns an {@link TokenPairExpressionIterator} expression 1597 */ 1598 public static Expression tokenizePairExpression(String startToken, String endToken, boolean includeTokens) { 1599 return new TokenPairExpressionIterator(startToken, endToken, includeTokens); 1600 } 1601 1602 /** 1603 * Returns an {@link TokenXMLExpressionIterator} expression 1604 */ 1605 public static Expression tokenizeXMLExpression(String tagName, String inheritNamespaceTagName) { 1606 ObjectHelper.notEmpty(tagName, "tagName"); 1607 return new TokenXMLExpressionIterator(tagName, inheritNamespaceTagName); 1608 } 1609 1610 public static Expression tokenizeXMLAwareExpression(String path, char mode) { 1611 ObjectHelper.notEmpty(path, "path"); 1612 return new XMLTokenExpressionIterator(path, mode); 1613 } 1614 1615 public static Expression tokenizeXMLAwareExpression(String path, char mode, int group) { 1616 ObjectHelper.notEmpty(path, "path"); 1617 return new XMLTokenExpressionIterator(path, mode, group); 1618 } 1619 1620 /** 1621 * Returns a tokenize expression which will tokenize the string with the 1622 * given regex 1623 */ 1624 public static Expression regexTokenizeExpression(final Expression expression, 1625 final String regexTokenizer) { 1626 final Pattern pattern = Pattern.compile(regexTokenizer); 1627 return new ExpressionAdapter() { 1628 public Object evaluate(Exchange exchange) { 1629 Object value = expression.evaluate(exchange, Object.class); 1630 Scanner scanner = ObjectHelper.getScanner(exchange, value); 1631 scanner.useDelimiter(pattern); 1632 return scanner; 1633 } 1634 1635 @Override 1636 public String toString() { 1637 return "regexTokenize(" + expression + ", " + pattern.pattern() + ")"; 1638 } 1639 }; 1640 } 1641 1642 public static Expression groupXmlIteratorExpression(final Expression expression, final int group) { 1643 return new ExpressionAdapter() { 1644 public Object evaluate(Exchange exchange) { 1645 // evaluate expression as iterator 1646 Iterator<?> it = expression.evaluate(exchange, Iterator.class); 1647 ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator"); 1648 // must use GroupTokenIterator in xml mode as we want to concat the xml parts into a single message 1649 return new GroupTokenIterator(exchange, it, null, group, false); 1650 } 1651 1652 @Override 1653 public String toString() { 1654 return "group " + expression + " " + group + " times"; 1655 } 1656 }; 1657 } 1658 1659 public static Expression groupIteratorExpression(final Expression expression, final String token, final int group, final boolean skipFirst) { 1660 return new ExpressionAdapter() { 1661 public Object evaluate(Exchange exchange) { 1662 // evaluate expression as iterator 1663 Iterator<?> it = expression.evaluate(exchange, Iterator.class); 1664 ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator"); 1665 if (token != null) { 1666 return new GroupTokenIterator(exchange, it, token, group, skipFirst); 1667 } else { 1668 return new GroupIterator(exchange, it, group, skipFirst); 1669 } 1670 } 1671 1672 @Override 1673 public String toString() { 1674 return "group " + expression + " " + group + " times"; 1675 } 1676 }; 1677 } 1678 1679 public static Expression skipIteratorExpression(final Expression expression, final int skip) { 1680 return new ExpressionAdapter() { 1681 public Object evaluate(Exchange exchange) { 1682 // evaluate expression as iterator 1683 Iterator<?> it = expression.evaluate(exchange, Iterator.class); 1684 ObjectHelper.notNull(it, "expression: " + expression + " evaluated on " + exchange + " must return an java.util.Iterator"); 1685 return new SkipIterator(exchange, it, skip); 1686 } 1687 1688 @Override 1689 public String toString() { 1690 return "skip " + expression + " " + skip + " times"; 1691 } 1692 }; 1693 } 1694 1695 /** 1696 * Returns a sort expression which will sort the expression with the given comparator. 1697 * <p/> 1698 * The expression is evaluated as a {@link List} object to allow sorting. 1699 */ 1700 @SuppressWarnings({"unchecked", "rawtypes"}) 1701 public static Expression sortExpression(final Expression expression, final Comparator comparator) { 1702 return new ExpressionAdapter() { 1703 public Object evaluate(Exchange exchange) { 1704 List<?> list = expression.evaluate(exchange, List.class); 1705 list.sort(comparator); 1706 return list; 1707 } 1708 1709 @Override 1710 public String toString() { 1711 return "sort(" + expression + " by: " + comparator + ")"; 1712 } 1713 }; 1714 } 1715 1716 /** 1717 * Transforms the expression into a String then performs the regex 1718 * replaceAll to transform the String and return the result 1719 */ 1720 public static Expression regexReplaceAll(final Expression expression, 1721 final String regex, final String replacement) { 1722 final Pattern pattern = Pattern.compile(regex); 1723 return new ExpressionAdapter() { 1724 public Object evaluate(Exchange exchange) { 1725 String text = expression.evaluate(exchange, String.class); 1726 if (text == null) { 1727 return null; 1728 } 1729 return pattern.matcher(text).replaceAll(replacement); 1730 } 1731 1732 @Override 1733 public String toString() { 1734 return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")"; 1735 } 1736 }; 1737 } 1738 1739 /** 1740 * Transforms the expression into a String then performs the regex 1741 * replaceAll to transform the String and return the result 1742 */ 1743 public static Expression regexReplaceAll(final Expression expression, 1744 final String regex, final Expression replacementExpression) { 1745 1746 final Pattern pattern = Pattern.compile(regex); 1747 return new ExpressionAdapter() { 1748 public Object evaluate(Exchange exchange) { 1749 String text = expression.evaluate(exchange, String.class); 1750 String replacement = replacementExpression.evaluate(exchange, String.class); 1751 if (text == null || replacement == null) { 1752 return null; 1753 } 1754 return pattern.matcher(text).replaceAll(replacement); 1755 } 1756 1757 @Override 1758 public String toString() { 1759 return "regexReplaceAll(" + expression + ", " + pattern.pattern() + ")"; 1760 } 1761 }; 1762 } 1763 1764 /** 1765 * Appends the String evaluations of the two expressions together 1766 */ 1767 public static Expression append(final Expression left, final Expression right) { 1768 return new ExpressionAdapter() { 1769 public Object evaluate(Exchange exchange) { 1770 return left.evaluate(exchange, String.class) + right.evaluate(exchange, String.class); 1771 } 1772 1773 @Override 1774 public String toString() { 1775 return "append(" + left + ", " + right + ")"; 1776 } 1777 }; 1778 } 1779 1780 /** 1781 * Prepends the String evaluations of the two expressions together 1782 */ 1783 public static Expression prepend(final Expression left, final Expression right) { 1784 return new ExpressionAdapter() { 1785 public Object evaluate(Exchange exchange) { 1786 return right.evaluate(exchange, String.class) + left.evaluate(exchange, String.class); 1787 } 1788 1789 @Override 1790 public String toString() { 1791 return "prepend(" + left + ", " + right + ")"; 1792 } 1793 }; 1794 } 1795 1796 /** 1797 * Returns an expression which returns the string concatenation value of the various 1798 * expressions 1799 * 1800 * @param expressions the expression to be concatenated dynamically 1801 * @return an expression which when evaluated will return the concatenated values 1802 */ 1803 public static Expression concatExpression(final Collection<Expression> expressions) { 1804 return concatExpression(expressions, null); 1805 } 1806 1807 /** 1808 * Returns an expression which returns the string concatenation value of the various 1809 * expressions 1810 * 1811 * @param expressions the expression to be concatenated dynamically 1812 * @param desription the text description of the expression 1813 * @return an expression which when evaluated will return the concatenated values 1814 */ 1815 public static Expression concatExpression(final Collection<Expression> expressions, final String desription) { 1816 return new ExpressionAdapter() { 1817 public Object evaluate(Exchange exchange) { 1818 StringBuilder buffer = new StringBuilder(); 1819 for (Expression expression : expressions) { 1820 String text = expression.evaluate(exchange, String.class); 1821 if (text != null) { 1822 buffer.append(text); 1823 } 1824 } 1825 return buffer.toString(); 1826 } 1827 1828 @Override 1829 public String toString() { 1830 if (desription != null) { 1831 return desription; 1832 } else { 1833 return "concat" + expressions; 1834 } 1835 } 1836 }; 1837 } 1838 1839 /** 1840 * Returns an Expression for the inbound message id 1841 */ 1842 public static Expression messageIdExpression() { 1843 return new ExpressionAdapter() { 1844 public Object evaluate(Exchange exchange) { 1845 return exchange.getIn().getMessageId(); 1846 } 1847 1848 @Override 1849 public String toString() { 1850 return "messageId"; 1851 } 1852 }; 1853 } 1854 1855 /** 1856 * Returns an Expression for the exchange id 1857 */ 1858 public static Expression exchangeIdExpression() { 1859 return new ExpressionAdapter() { 1860 public Object evaluate(Exchange exchange) { 1861 return exchange.getExchangeId(); 1862 } 1863 1864 @Override 1865 public String toString() { 1866 return "exchangeId"; 1867 } 1868 }; 1869 } 1870 1871 /** 1872 * Returns an Expression for the route id 1873 */ 1874 public static Expression routeIdExpression() { 1875 return new ExpressionAdapter() { 1876 public Object evaluate(Exchange exchange) { 1877 String answer = null; 1878 UnitOfWork uow = exchange.getUnitOfWork(); 1879 RouteContext rc = uow != null ? uow.getRouteContext() : null; 1880 if (rc != null) { 1881 answer = rc.getRoute().getId(); 1882 } 1883 if (answer == null) { 1884 // fallback and get from route id on the exchange 1885 answer = exchange.getFromRouteId(); 1886 } 1887 return answer; 1888 } 1889 1890 @Override 1891 public String toString() { 1892 return "routeId"; 1893 } 1894 }; 1895 } 1896 1897 public static Expression dateExpression(final String command) { 1898 return dateExpression(command, null, null); 1899 } 1900 1901 public static Expression dateExpression(final String command, final String pattern) { 1902 return dateExpression(command, null, pattern); 1903 } 1904 1905 public static Expression dateExpression(final String commandWithOffsets, final String timezone, final String pattern) { 1906 return new ExpressionAdapter() { 1907 public Object evaluate(Exchange exchange) { 1908 // Capture optional time offsets 1909 String command = commandWithOffsets.split("[+-]", 2)[0].trim(); 1910 List<Long> offsets = new ArrayList<>(); 1911 Matcher offsetMatcher = OFFSET_PATTERN.matcher(commandWithOffsets); 1912 while (offsetMatcher.find()) { 1913 try { 1914 long value = exchange.getContext().getTypeConverter().mandatoryConvertTo(long.class, exchange, offsetMatcher.group(2).trim()); 1915 offsets.add(offsetMatcher.group(1).equals("+") ? value : -value); 1916 } catch (NoTypeConversionAvailableException e) { 1917 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 1918 } 1919 } 1920 1921 Date date; 1922 if ("now".equals(command)) { 1923 date = new Date(); 1924 } else if (command.startsWith("header.") || command.startsWith("in.header.")) { 1925 String key = command.substring(command.lastIndexOf('.') + 1); 1926 date = exchange.getIn().getHeader(key, Date.class); 1927 if (date == null) { 1928 throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command); 1929 } 1930 } else if (command.startsWith("out.header.")) { 1931 String key = command.substring(command.lastIndexOf('.') + 1); 1932 date = exchange.getOut().getHeader(key, Date.class); 1933 if (date == null) { 1934 throw new IllegalArgumentException("Cannot find java.util.Date object at command: " + command); 1935 } 1936 } else if ("file".equals(command)) { 1937 Long num = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class); 1938 if (num != null && num > 0) { 1939 date = new Date(num); 1940 } else { 1941 date = exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Date.class); 1942 if (date == null) { 1943 throw new IllegalArgumentException("Cannot find " + Exchange.FILE_LAST_MODIFIED + " header at command: " + command); 1944 } 1945 } 1946 } else { 1947 throw new IllegalArgumentException("Command not supported for dateExpression: " + command); 1948 } 1949 1950 // Apply offsets 1951 long dateAsLong = date.getTime(); 1952 for (long offset : offsets) { 1953 dateAsLong += offset; 1954 } 1955 date = new Date(dateAsLong); 1956 1957 if (pattern != null && !pattern.isEmpty()) { 1958 SimpleDateFormat df = new SimpleDateFormat(pattern); 1959 if (timezone != null && !timezone.isEmpty()) { 1960 df.setTimeZone(TimeZone.getTimeZone(timezone)); 1961 } 1962 return df.format(date); 1963 } else { 1964 return date; 1965 } 1966 } 1967 1968 @Override 1969 public String toString() { 1970 return "date(" + commandWithOffsets + ":" + pattern + ":" + timezone + ")"; 1971 } 1972 }; 1973 } 1974 1975 public static Expression simpleExpression(final String expression) { 1976 return new ExpressionAdapter() { 1977 public Object evaluate(Exchange exchange) { 1978 if (SimpleLanguage.hasSimpleFunction(expression)) { 1979 // resolve language using context to have a clear separation of packages 1980 // must call evaluate to return the nested language evaluate when evaluating 1981 // stacked expressions 1982 Language language = exchange.getContext().resolveLanguage("simple"); 1983 return language.createExpression(expression).evaluate(exchange, Object.class); 1984 } else { 1985 return expression; 1986 } 1987 } 1988 1989 @Override 1990 public String toString() { 1991 return "simple(" + expression + ")"; 1992 } 1993 }; 1994 } 1995 1996 public static Expression beanExpression(final String expression) { 1997 return new ExpressionAdapter() { 1998 public Object evaluate(Exchange exchange) { 1999 // bean is able to evaluate method name if it contains nested functions 2000 // so we should not eager evaluate expression as a string 2001 // resolve language using context to have a clear separation of packages 2002 // must call evaluate to return the nested language evaluate when evaluating 2003 // stacked expressions 2004 Language language = exchange.getContext().resolveLanguage("bean"); 2005 return language.createExpression(expression).evaluate(exchange, Object.class); 2006 } 2007 2008 @Override 2009 public String toString() { 2010 return "bean(" + expression + ")"; 2011 } 2012 }; 2013 } 2014 2015 public static Expression beanExpression(final Class<?> beanType, final String methodName) { 2016 return BeanLanguage.bean(beanType, methodName); 2017 } 2018 2019 public static Expression beanExpression(final Object bean, final String methodName) { 2020 return BeanLanguage.bean(bean, methodName); 2021 } 2022 2023 public static Expression beanExpression(final String beanRef, final String methodName) { 2024 String expression = methodName != null ? beanRef + "." + methodName : beanRef; 2025 return beanExpression(expression); 2026 } 2027 2028 /** 2029 * Returns an expression processing the exchange to the given endpoint uri 2030 * 2031 * @param uri endpoint uri to send the exchange to 2032 * @return an expression object which will return the OUT body 2033 */ 2034 public static Expression toExpression(final String uri) { 2035 return new ExpressionAdapter() { 2036 public Object evaluate(Exchange exchange) { 2037 String text = simpleExpression(uri).evaluate(exchange, String.class); 2038 Endpoint endpoint = exchange.getContext().getEndpoint(text); 2039 if (endpoint == null) { 2040 throw new NoSuchEndpointException(text); 2041 } 2042 2043 Producer producer; 2044 try { 2045 producer = endpoint.createProducer(); 2046 producer.start(); 2047 producer.process(exchange); 2048 producer.stop(); 2049 } catch (Exception e) { 2050 throw ObjectHelper.wrapRuntimeCamelException(e); 2051 } 2052 2053 // return the OUT body, but check for exchange pattern 2054 if (ExchangeHelper.isOutCapable(exchange)) { 2055 return exchange.getOut().getBody(); 2056 } else { 2057 return exchange.getIn().getBody(); 2058 } 2059 } 2060 2061 @Override 2062 public String toString() { 2063 return "to(" + uri + ")"; 2064 } 2065 }; 2066 } 2067 2068 public static Expression fileNameExpression() { 2069 return new ExpressionAdapter() { 2070 public Object evaluate(Exchange exchange) { 2071 return exchange.getIn().getHeader(Exchange.FILE_NAME, String.class); 2072 } 2073 2074 @Override 2075 public String toString() { 2076 return "file:name"; 2077 } 2078 }; 2079 } 2080 2081 public static Expression fileOnlyNameExpression() { 2082 return new ExpressionAdapter() { 2083 public Object evaluate(Exchange exchange) { 2084 String answer = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class); 2085 if (answer == null) { 2086 answer = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class); 2087 answer = FileUtil.stripPath(answer); 2088 } 2089 return answer; 2090 } 2091 2092 @Override 2093 public String toString() { 2094 return "file:onlyname"; 2095 } 2096 }; 2097 } 2098 2099 public static Expression fileNameNoExtensionExpression() { 2100 return new ExpressionAdapter() { 2101 public Object evaluate(Exchange exchange) { 2102 String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class); 2103 return FileUtil.stripExt(name); 2104 } 2105 2106 @Override 2107 public String toString() { 2108 return "file:name.noext"; 2109 } 2110 }; 2111 } 2112 2113 public static Expression fileNameNoExtensionSingleExpression() { 2114 return new ExpressionAdapter() { 2115 public Object evaluate(Exchange exchange) { 2116 String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class); 2117 return FileUtil.stripExt(name, true); 2118 } 2119 2120 @Override 2121 public String toString() { 2122 return "file:name.noext.single"; 2123 } 2124 }; 2125 } 2126 2127 public static Expression fileOnlyNameNoExtensionExpression() { 2128 return new ExpressionAdapter() { 2129 public Object evaluate(Exchange exchange) { 2130 String name = fileOnlyNameExpression().evaluate(exchange, String.class); 2131 return FileUtil.stripExt(name); 2132 } 2133 2134 @Override 2135 public String toString() { 2136 return "file:onlyname.noext"; 2137 } 2138 }; 2139 } 2140 2141 public static Expression fileOnlyNameNoExtensionSingleExpression() { 2142 return new ExpressionAdapter() { 2143 public Object evaluate(Exchange exchange) { 2144 String name = fileOnlyNameExpression().evaluate(exchange, String.class); 2145 return FileUtil.stripExt(name, true); 2146 } 2147 2148 @Override 2149 public String toString() { 2150 return "file:onlyname.noext.single"; 2151 } 2152 }; 2153 } 2154 2155 public static Expression fileExtensionExpression() { 2156 return new ExpressionAdapter() { 2157 public Object evaluate(Exchange exchange) { 2158 String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class); 2159 return FileUtil.onlyExt(name); 2160 } 2161 2162 @Override 2163 public String toString() { 2164 return "file:ext"; 2165 } 2166 }; 2167 } 2168 2169 public static Expression fileExtensionSingleExpression() { 2170 return new ExpressionAdapter() { 2171 public Object evaluate(Exchange exchange) { 2172 String name = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class); 2173 return FileUtil.onlyExt(name, true); 2174 } 2175 2176 @Override 2177 public String toString() { 2178 return "file:ext.single"; 2179 } 2180 }; 2181 } 2182 2183 public static Expression fileParentExpression() { 2184 return new ExpressionAdapter() { 2185 public Object evaluate(Exchange exchange) { 2186 return exchange.getIn().getHeader("CamelFileParent", String.class); 2187 } 2188 2189 @Override 2190 public String toString() { 2191 return "file:parent"; 2192 } 2193 }; 2194 } 2195 2196 public static Expression filePathExpression() { 2197 return new ExpressionAdapter() { 2198 public Object evaluate(Exchange exchange) { 2199 return exchange.getIn().getHeader("CamelFilePath", String.class); 2200 } 2201 2202 @Override 2203 public String toString() { 2204 return "file:path"; 2205 } 2206 }; 2207 } 2208 2209 public static Expression fileAbsolutePathExpression() { 2210 return new ExpressionAdapter() { 2211 public Object evaluate(Exchange exchange) { 2212 return exchange.getIn().getHeader("CamelFileAbsolutePath", String.class); 2213 } 2214 2215 @Override 2216 public String toString() { 2217 return "file:absolute.path"; 2218 } 2219 }; 2220 } 2221 2222 public static Expression fileAbsoluteExpression() { 2223 return new ExpressionAdapter() { 2224 public Object evaluate(Exchange exchange) { 2225 return exchange.getIn().getHeader("CamelFileAbsolute", Boolean.class); 2226 } 2227 2228 @Override 2229 public String toString() { 2230 return "file:absolute"; 2231 } 2232 }; 2233 } 2234 2235 public static Expression fileSizeExpression() { 2236 return new ExpressionAdapter() { 2237 public Object evaluate(Exchange exchange) { 2238 return exchange.getIn().getHeader(Exchange.FILE_LENGTH, Long.class); 2239 } 2240 2241 @Override 2242 public String toString() { 2243 return "file:length"; 2244 } 2245 }; 2246 } 2247 2248 public static Expression fileLastModifiedExpression() { 2249 return new ExpressionAdapter() { 2250 public Object evaluate(Exchange exchange) { 2251 return exchange.getIn().getHeader(Exchange.FILE_LAST_MODIFIED, Long.class); 2252 } 2253 2254 @Override 2255 public String toString() { 2256 return "file:modified"; 2257 } 2258 }; 2259 } 2260 2261 /** 2262 * Returns Simple expression or fallback to Constant expression if expression str is not Simple expression. 2263 */ 2264 public static Expression parseSimpleOrFallbackToConstantExpression(String str, CamelContext camelContext) { 2265 if (StringHelper.hasStartToken(str, "simple")) { 2266 return camelContext.resolveLanguage("simple").createExpression(str); 2267 } 2268 return constantExpression(str); 2269 } 2270 2271 public static Expression propertiesComponentExpression(final String key, final String locations, final String defaultValue) { 2272 return new ExpressionAdapter() { 2273 public Object evaluate(Exchange exchange) { 2274 String text = simpleExpression(key).evaluate(exchange, String.class); 2275 String text2 = simpleExpression(locations).evaluate(exchange, String.class); 2276 try { 2277 if (text2 != null) { 2278 // the properties component is optional as we got locations 2279 // getComponent will create a new component if none already exists 2280 Component component = exchange.getContext().getComponent("properties"); 2281 PropertiesComponent pc = exchange.getContext().getTypeConverter() 2282 .mandatoryConvertTo(PropertiesComponent.class, component); 2283 // enclose key with {{ }} to force parsing 2284 String[] paths = text2.split(","); 2285 return pc.parseUri(pc.getPrefixToken() + text + pc.getSuffixToken(), paths); 2286 } else { 2287 // the properties component is mandatory if no locations provided 2288 Component component = exchange.getContext().hasComponent("properties"); 2289 if (component == null) { 2290 throw new IllegalArgumentException("PropertiesComponent with name properties must be defined" 2291 + " in CamelContext to support property placeholders in expressions"); 2292 } 2293 PropertiesComponent pc = exchange.getContext().getTypeConverter() 2294 .mandatoryConvertTo(PropertiesComponent.class, component); 2295 // enclose key with {{ }} to force parsing 2296 return pc.parseUri(pc.getPrefixToken() + text + pc.getSuffixToken()); 2297 } 2298 } catch (Exception e) { 2299 // property with key not found, use default value if provided 2300 if (defaultValue != null) { 2301 return defaultValue; 2302 } 2303 throw ObjectHelper.wrapRuntimeCamelException(e); 2304 } 2305 } 2306 2307 @Override 2308 public String toString() { 2309 return "properties(" + key + ")"; 2310 } 2311 }; 2312 } 2313 2314 /** 2315 * Returns a random number between 0 and upperbound (exclusive) 2316 */ 2317 public static Expression randomExpression(final int upperbound) { 2318 return randomExpression(0, upperbound); 2319 } 2320 2321 /** 2322 * Returns a random number between min and max 2323 */ 2324 public static Expression randomExpression(final int min, final int max) { 2325 return new ExpressionAdapter() { 2326 public Object evaluate(Exchange exchange) { 2327 Random random = new Random(); 2328 int randomNum = random.nextInt(max - min) + min; 2329 return randomNum; 2330 } 2331 2332 @Override 2333 public String toString() { 2334 return "random"; 2335 } 2336 }; 2337 } 2338 2339 /** 2340 * Returns a random number between min and max 2341 */ 2342 public static Expression randomExpression(final String min, final String max) { 2343 return new ExpressionAdapter() { 2344 public Object evaluate(Exchange exchange) { 2345 int num1 = simpleExpression(min).evaluate(exchange, Integer.class); 2346 int num2 = simpleExpression(max).evaluate(exchange, Integer.class); 2347 Random random = new Random(); 2348 int randomNum = random.nextInt(num2 - num1) + num1; 2349 return randomNum; 2350 } 2351 2352 @Override 2353 public String toString() { 2354 return "random"; 2355 } 2356 }; 2357 } 2358 2359 /** 2360 * Returns an iterator to skip (iterate) the given expression 2361 */ 2362 public static Expression skipExpression(final String expression, final int number) { 2363 return new ExpressionAdapter() { 2364 public Object evaluate(Exchange exchange) { 2365 // use simple language 2366 Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression); 2367 return ExpressionBuilder.skipIteratorExpression(exp, number).evaluate(exchange, Object.class); 2368 } 2369 2370 @Override 2371 public String toString() { 2372 return "skip(" + expression + "," + number + ")"; 2373 } 2374 }; 2375 } 2376 2377 /** 2378 * Returns an iterator to collate (iterate) the given expression 2379 */ 2380 public static Expression collateExpression(final String expression, final int group) { 2381 return new ExpressionAdapter() { 2382 public Object evaluate(Exchange exchange) { 2383 // use simple language 2384 Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression); 2385 return ExpressionBuilder.groupIteratorExpression(exp, null, group, false).evaluate(exchange, Object.class); 2386 } 2387 2388 @Override 2389 public String toString() { 2390 return "collate(" + expression + "," + group + ")"; 2391 } 2392 }; 2393 } 2394 2395 /** 2396 * Returns the message history (including exchange details or not) 2397 */ 2398 public static Expression messageHistoryExpression(final boolean detailed) { 2399 return new ExpressionAdapter() { 2400 2401 private ExchangeFormatter formatter; 2402 2403 public Object evaluate(Exchange exchange) { 2404 ExchangeFormatter ef = null; 2405 if (detailed) { 2406 // use the exchange formatter to log exchange details 2407 ef = getOrCreateExchangeFormatter(exchange.getContext()); 2408 } 2409 return MessageHelper.dumpMessageHistoryStacktrace(exchange, ef, false); 2410 } 2411 2412 private ExchangeFormatter getOrCreateExchangeFormatter(CamelContext camelContext) { 2413 if (formatter == null) { 2414 Set<ExchangeFormatter> formatters = camelContext.getRegistry().findByType(ExchangeFormatter.class); 2415 if (formatters != null && formatters.size() == 1) { 2416 formatter = formatters.iterator().next(); 2417 } else { 2418 // setup exchange formatter to be used for message history dump 2419 DefaultExchangeFormatter def = new DefaultExchangeFormatter(); 2420 def.setShowExchangeId(true); 2421 def.setMultiline(true); 2422 def.setShowHeaders(true); 2423 def.setStyle(DefaultExchangeFormatter.OutputStyle.Fixed); 2424 try { 2425 Integer maxChars = CamelContextHelper.parseInteger(camelContext, camelContext.getGlobalOption(Exchange.LOG_DEBUG_BODY_MAX_CHARS)); 2426 if (maxChars != null) { 2427 def.setMaxChars(maxChars); 2428 } 2429 } catch (Exception e) { 2430 throw ObjectHelper.wrapRuntimeCamelException(e); 2431 } 2432 formatter = def; 2433 } 2434 } 2435 return formatter; 2436 } 2437 2438 @Override 2439 public String toString() { 2440 return "messageHistory(" + detailed + ")"; 2441 } 2442 }; 2443 } 2444 2445 /** 2446 * Expression adapter for OGNL expression from Message Header or Exchange property 2447 */ 2448 private static class KeyedOgnlExpressionAdapter extends ExpressionAdapter { 2449 private final String ognl; 2450 private final String toStringValue; 2451 private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy; 2452 2453 KeyedOgnlExpressionAdapter(String ognl, String toStringValue, 2454 KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) { 2455 this.ognl = ognl; 2456 this.toStringValue = toStringValue; 2457 this.keyedEntityRetrievalStrategy = keyedEntityRetrievalStrategy; 2458 } 2459 2460 public Object evaluate(Exchange exchange) { 2461 // try with full name first 2462 Object property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, ognl); 2463 if (property != null) { 2464 return property; 2465 } 2466 2467 2468 // Split ognl except when this is not a Map, Array 2469 // and we would like to keep the dots within the key name 2470 List<String> methods = OgnlHelper.splitOgnl(ognl); 2471 2472 String key = methods.get(0); 2473 String keySuffix = ""; 2474 // if ognl starts with a key inside brackets (eg: [foo.bar]) 2475 // remove starting and ending brackets from key 2476 if (key.startsWith("[") && key.endsWith("]")) { 2477 key = StringHelper.removeLeadingAndEndingQuotes(key.substring(1, key.length() - 1)); 2478 keySuffix = StringHelper.after(methods.get(0), key); 2479 } 2480 // remove any OGNL operators so we got the pure key name 2481 key = OgnlHelper.removeOperators(key); 2482 2483 2484 property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, key); 2485 if (property == null) { 2486 return null; 2487 } 2488 // the remainder is the rest of the ognl without the key 2489 String remainder = ObjectHelper.after(ognl, key + keySuffix); 2490 return new MethodCallExpression(property, remainder).evaluate(exchange); 2491 } 2492 2493 @Override 2494 public String toString() { 2495 return toStringValue; 2496 } 2497 2498 /** 2499 * Strategy to retrieve the value based on the key 2500 */ 2501 public interface KeyedEntityRetrievalStrategy { 2502 Object getKeyedEntity(Exchange exchange, String key); 2503 } 2504 } 2505 2506}