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.model; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.List; 022import java.util.StringTokenizer; 023import java.util.concurrent.atomic.AtomicBoolean; 024 025import javax.xml.bind.annotation.XmlAccessType; 026import javax.xml.bind.annotation.XmlAccessorType; 027import javax.xml.bind.annotation.XmlAttribute; 028import javax.xml.bind.annotation.XmlElement; 029import javax.xml.bind.annotation.XmlElementRef; 030import javax.xml.bind.annotation.XmlRootElement; 031import javax.xml.bind.annotation.XmlTransient; 032import javax.xml.bind.annotation.XmlType; 033 034import org.apache.camel.CamelContext; 035import org.apache.camel.Endpoint; 036import org.apache.camel.ErrorHandlerFactory; 037import org.apache.camel.FailedToCreateRouteException; 038import org.apache.camel.NoSuchEndpointException; 039import org.apache.camel.Route; 040import org.apache.camel.ServiceStatus; 041import org.apache.camel.ShutdownRoute; 042import org.apache.camel.ShutdownRunningTask; 043import org.apache.camel.StatefulService; 044import org.apache.camel.builder.AdviceWithRouteBuilder; 045import org.apache.camel.builder.AdviceWithTask; 046import org.apache.camel.builder.ErrorHandlerBuilderRef; 047import org.apache.camel.builder.RouteBuilder; 048import org.apache.camel.impl.DefaultRouteContext; 049import org.apache.camel.model.rest.RestBindingDefinition; 050import org.apache.camel.model.rest.RestDefinition; 051import org.apache.camel.processor.interceptor.HandleFault; 052import org.apache.camel.spi.AsEndpointUri; 053import org.apache.camel.spi.LifecycleStrategy; 054import org.apache.camel.spi.Metadata; 055import org.apache.camel.spi.RouteContext; 056import org.apache.camel.spi.RoutePolicy; 057import org.apache.camel.spi.RoutePolicyFactory; 058import org.apache.camel.spi.Transformer; 059import org.apache.camel.spi.Validator; 060import org.apache.camel.util.CamelContextHelper; 061import org.apache.camel.util.ObjectHelper; 062 063/** 064 * A Camel route 065 * 066 * @version 067 */ 068@Metadata(label = "configuration") 069@XmlRootElement(name = "route") 070@XmlType(propOrder = {"inputs", "inputType", "outputType", "outputs", "routeProperties"}) 071@XmlAccessorType(XmlAccessType.PROPERTY) 072// must use XmlAccessType.PROPERTY as there is some custom logic needed to be executed in the setter methods 073public class RouteDefinition extends ProcessorDefinition<RouteDefinition> { 074 private final AtomicBoolean prepared = new AtomicBoolean(false); 075 private List<FromDefinition> inputs = new ArrayList<>(); 076 private List<ProcessorDefinition<?>> outputs = new ArrayList<>(); 077 private String group; 078 private String streamCache; 079 private String trace; 080 private String messageHistory; 081 private String logMask; 082 private String handleFault; 083 private String delayer; 084 private String autoStartup; 085 private Integer startupOrder; 086 private List<RoutePolicy> routePolicies; 087 private String routePolicyRef; 088 private ShutdownRoute shutdownRoute; 089 private ShutdownRunningTask shutdownRunningTask; 090 private String errorHandlerRef; 091 private ErrorHandlerFactory errorHandlerBuilder; 092 // keep state whether the error handler is context scoped or not 093 // (will by default be context scoped of no explicit error handler configured) 094 private boolean contextScopedErrorHandler = true; 095 private Boolean rest; 096 private RestDefinition restDefinition; 097 private RestBindingDefinition restBindingDefinition; 098 private InputTypeDefinition inputType; 099 private OutputTypeDefinition outputType; 100 private List<PropertyDefinition> routeProperties; 101 102 public RouteDefinition() { 103 } 104 105 public RouteDefinition(@AsEndpointUri String uri) { 106 from(uri); 107 } 108 109 public RouteDefinition(Endpoint endpoint) { 110 from(endpoint); 111 } 112 113 /** 114 * This route is created from the REST DSL. 115 */ 116 public void fromRest(@AsEndpointUri String uri) { 117 from(uri); 118 rest = true; 119 } 120 121 /** 122 * Prepares the route definition to be ready to be added to {@link CamelContext} 123 * 124 * @param context the camel context 125 */ 126 public void prepare(ModelCamelContext context) { 127 if (prepared.compareAndSet(false, true)) { 128 RouteDefinitionHelper.prepareRoute(context, this); 129 } 130 } 131 132 /** 133 * Marks the route definition as prepared. 134 * <p/> 135 * This is needed if routes have been created by components such as 136 * <tt>camel-spring</tt> or <tt>camel-blueprint</tt>. 137 * Usually they share logic in the <tt>camel-core-xml</tt> module which prepares the routes. 138 */ 139 public void markPrepared() { 140 prepared.set(true); 141 } 142 143 /** 144 * Marks the route definition as un-prepared. 145 * <p/> 146 * This is needed if routes have been created by components such as 147 * <tt>camel-scala</tt>. To unset the prepare so the routes can be prepared 148 * at a later stage when scala has build the routes completely. 149 */ 150 public void markUnprepared() { 151 prepared.set(false); 152 } 153 154 @Override 155 public String toString() { 156 if (getId() != null) { 157 return "Route(" + getId() + ")[" + inputs + " -> " + outputs + "]"; 158 } else { 159 return "Route[" + inputs + " -> " + outputs + "]"; 160 } 161 } 162 163 @Override 164 public String getShortName() { 165 return "route"; 166 } 167 168 /** 169 * Returns the status of the route if it has been registered with a {@link CamelContext} 170 */ 171 public ServiceStatus getStatus(CamelContext camelContext) { 172 if (camelContext != null) { 173 ServiceStatus answer = camelContext.getRouteStatus(this.getId()); 174 if (answer == null) { 175 answer = ServiceStatus.Stopped; 176 } 177 return answer; 178 } 179 return null; 180 } 181 182 public boolean isStartable(CamelContext camelContext) { 183 ServiceStatus status = getStatus(camelContext); 184 if (status == null) { 185 return true; 186 } else { 187 return status.isStartable(); 188 } 189 } 190 191 public boolean isStoppable(CamelContext camelContext) { 192 ServiceStatus status = getStatus(camelContext); 193 if (status == null) { 194 return false; 195 } else { 196 return status.isStoppable(); 197 } 198 } 199 200 public List<RouteContext> addRoutes(ModelCamelContext camelContext, Collection<Route> routes) throws Exception { 201 List<RouteContext> answer = new ArrayList<>(); 202 203 @SuppressWarnings("deprecation") 204 ErrorHandlerFactory handler = camelContext.getErrorHandlerBuilder(); 205 if (handler != null) { 206 setErrorHandlerBuilderIfNull(handler); 207 } 208 209 for (FromDefinition fromType : inputs) { 210 RouteContext routeContext; 211 try { 212 routeContext = addRoutes(camelContext, routes, fromType); 213 } catch (FailedToCreateRouteException e) { 214 throw e; 215 } catch (Exception e) { 216 // wrap in exception which provide more details about which route was failing 217 throw new FailedToCreateRouteException(getId(), toString(), e); 218 } 219 answer.add(routeContext); 220 } 221 return answer; 222 } 223 224 225 public Endpoint resolveEndpoint(CamelContext camelContext, String uri) throws NoSuchEndpointException { 226 ObjectHelper.notNull(camelContext, "CamelContext"); 227 return CamelContextHelper.getMandatoryEndpoint(camelContext, uri); 228 } 229 230 public RouteDefinition adviceWith(CamelContext camelContext, RouteBuilder builder) throws Exception { 231 return adviceWith((ModelCamelContext)camelContext, builder); 232 } 233 234 /** 235 * Advices this route with the route builder. 236 * <p/> 237 * <b>Important:</b> It is recommended to only advice a given route once (you can of course advice multiple routes). 238 * If you do it multiple times, then it may not work as expected, especially when any kind of error handling is involved. 239 * The Camel team plan for Camel 3.0 to support this as internal refactorings in the routing engine is needed to support this properly. 240 * <p/> 241 * You can use a regular {@link RouteBuilder} but the specialized {@link org.apache.camel.builder.AdviceWithRouteBuilder} 242 * has additional features when using the <a href="http://camel.apache.org/advicewith.html">advice with</a> feature. 243 * We therefore suggest you to use the {@link org.apache.camel.builder.AdviceWithRouteBuilder}. 244 * <p/> 245 * The advice process will add the interceptors, on exceptions, on completions etc. configured 246 * from the route builder to this route. 247 * <p/> 248 * This is mostly used for testing purpose to add interceptors and the likes to an existing route. 249 * <p/> 250 * Will stop and remove the old route from camel context and add and start this new advised route. 251 * 252 * @param camelContext the camel context 253 * @param builder the route builder 254 * @return a new route which is this route merged with the route builder 255 * @throws Exception can be thrown from the route builder 256 * @see AdviceWithRouteBuilder 257 */ 258 @SuppressWarnings("deprecation") 259 public RouteDefinition adviceWith(ModelCamelContext camelContext, RouteBuilder builder) throws Exception { 260 ObjectHelper.notNull(camelContext, "CamelContext"); 261 ObjectHelper.notNull(builder, "RouteBuilder"); 262 263 log.debug("AdviceWith route before: {}", this); 264 265 // inject this route into the advice route builder so it can access this route 266 // and offer features to manipulate the route directly 267 if (builder instanceof AdviceWithRouteBuilder) { 268 ((AdviceWithRouteBuilder) builder).setOriginalRoute(this); 269 } 270 271 // configure and prepare the routes from the builder 272 RoutesDefinition routes = builder.configureRoutes(camelContext); 273 274 log.debug("AdviceWith routes: {}", routes); 275 276 // we can only advice with a route builder without any routes 277 if (!builder.getRouteCollection().getRoutes().isEmpty()) { 278 throw new IllegalArgumentException("You can only advice from a RouteBuilder which has no existing routes." 279 + " Remove all routes from the route builder."); 280 } 281 // we can not advice with error handlers (if you added a new error handler in the route builder) 282 // we must check the error handler on builder is not the same as on camel context, as that would be the default 283 // context scoped error handler, in case no error handlers was configured 284 if (builder.getRouteCollection().getErrorHandlerBuilder() != null 285 && camelContext.getErrorHandlerBuilder() != builder.getRouteCollection().getErrorHandlerBuilder()) { 286 throw new IllegalArgumentException("You can not advice with error handlers. Remove the error handlers from the route builder."); 287 } 288 289 String beforeAsXml = ModelHelper.dumpModelAsXml(camelContext, this); 290 291 // stop and remove this existing route 292 camelContext.removeRouteDefinition(this); 293 294 // any advice with tasks we should execute first? 295 if (builder instanceof AdviceWithRouteBuilder) { 296 List<AdviceWithTask> tasks = ((AdviceWithRouteBuilder) builder).getAdviceWithTasks(); 297 for (AdviceWithTask task : tasks) { 298 task.task(); 299 } 300 } 301 302 // now merge which also ensures that interceptors and the likes get mixed in correctly as well 303 RouteDefinition merged = routes.route(this); 304 305 // add the new merged route 306 camelContext.getRouteDefinitions().add(0, merged); 307 308 // log the merged route at info level to make it easier to end users to spot any mistakes they may have made 309 log.info("AdviceWith route after: {}", merged); 310 311 String afterAsXml = ModelHelper.dumpModelAsXml(camelContext, merged); 312 log.info("Adviced route before/after as XML:\n{}\n{}", beforeAsXml, afterAsXml); 313 314 // If the camel context is started then we start the route 315 if (camelContext instanceof StatefulService) { 316 StatefulService service = (StatefulService) camelContext; 317 if (service.isStarted()) { 318 camelContext.startRoute(merged); 319 } 320 } 321 return merged; 322 } 323 324 // Fluent API 325 // ----------------------------------------------------------------------- 326 327 /** 328 * Creates an input to the route 329 * 330 * @param uri the from uri 331 * @return the builder 332 */ 333 public RouteDefinition from(@AsEndpointUri String uri) { 334 getInputs().add(new FromDefinition(uri)); 335 return this; 336 } 337 338 /** 339 * Creates an input to the route 340 * 341 * @param endpoint the from endpoint 342 * @return the builder 343 */ 344 public RouteDefinition from(Endpoint endpoint) { 345 getInputs().add(new FromDefinition(endpoint)); 346 return this; 347 } 348 349 /** 350 * Creates inputs to the route 351 * 352 * @param uris the from uris 353 * @return the builder 354 */ 355 public RouteDefinition from(@AsEndpointUri String... uris) { 356 for (String uri : uris) { 357 getInputs().add(new FromDefinition(uri)); 358 } 359 return this; 360 } 361 362 /** 363 * Creates inputs to the route 364 * 365 * @param endpoints the from endpoints 366 * @return the builder 367 */ 368 public RouteDefinition from(Endpoint... endpoints) { 369 for (Endpoint endpoint : endpoints) { 370 getInputs().add(new FromDefinition(endpoint)); 371 } 372 return this; 373 } 374 375 /** 376 * Set the group name for this route 377 * 378 * @param name the group name 379 * @return the builder 380 */ 381 public RouteDefinition group(String name) { 382 setGroup(name); 383 return this; 384 } 385 386 /** 387 * Set the route group for this route 388 * 389 * @param group the route group 390 * @return the builder 391 */ 392 public RouteDefinition routeGroup(String group) { 393 setGroup(group); 394 return this; 395 } 396 397 /** 398 * Set the route id for this route 399 * 400 * @param id the route id 401 * @return the builder 402 */ 403 public RouteDefinition routeId(String id) { 404 setId(id); 405 return this; 406 } 407 408 /** 409 * Set the route description for this route 410 * 411 * @param description the route description 412 * @return the builder 413 */ 414 public RouteDefinition routeDescription(String description) { 415 DescriptionDefinition desc = new DescriptionDefinition(); 416 desc.setText(description); 417 setDescription(desc); 418 return this; 419 } 420 421 /** 422 * Disable stream caching for this route. 423 * 424 * @return the builder 425 */ 426 public RouteDefinition noStreamCaching() { 427 setStreamCache("false"); 428 return this; 429 } 430 431 /** 432 * Enable stream caching for this route. 433 * 434 * @return the builder 435 */ 436 public RouteDefinition streamCaching() { 437 setStreamCache("true"); 438 return this; 439 } 440 441 /** 442 * Enable stream caching for this route. 443 * 444 * @param streamCache whether to use stream caching (true or false), the value can be a property placeholder 445 * @return the builder 446 */ 447 public RouteDefinition streamCaching(String streamCache) { 448 setStreamCache(streamCache); 449 return this; 450 } 451 452 /** 453 * Disable tracing for this route. 454 * 455 * @return the builder 456 */ 457 public RouteDefinition noTracing() { 458 setTrace("false"); 459 return this; 460 } 461 462 /** 463 * Enable tracing for this route. 464 * 465 * @return the builder 466 */ 467 public RouteDefinition tracing() { 468 setTrace("true"); 469 return this; 470 } 471 472 /** 473 * Enable tracing for this route. 474 * 475 * @param tracing whether to use tracing (true or false), the value can be a property placeholder 476 * @return the builder 477 */ 478 public RouteDefinition tracing(String tracing) { 479 setTrace(tracing); 480 return this; 481 } 482 483 /** 484 * Enable message history for this route. 485 * 486 * @return the builder 487 */ 488 public RouteDefinition messageHistory() { 489 setMessageHistory("true"); 490 return this; 491 } 492 493 /** 494 * Enable message history for this route. 495 * 496 * @param messageHistory whether to use message history (true or false), the value can be a property placeholder 497 * @return the builder 498 */ 499 public RouteDefinition messageHistory(String messageHistory) { 500 setMessageHistory(messageHistory); 501 return this; 502 } 503 504 /** 505 * Enable security mask for Logging on this route. 506 * 507 * @return the builder 508 */ 509 public RouteDefinition logMask() { 510 setLogMask("true"); 511 return this; 512 } 513 514 /** 515 * Sets whether security mask for logging is enabled on this route. 516 * 517 * @param logMask whether to enable security mask for Logging (true or false), the value can be a property placeholder 518 * @return the builder 519 */ 520 public RouteDefinition logMask(String logMask) { 521 setLogMask(logMask); 522 return this; 523 } 524 525 /** 526 * Disable message history for this route. 527 * 528 * @return the builder 529 */ 530 public RouteDefinition noMessageHistory() { 531 setMessageHistory("false"); 532 return this; 533 } 534 535 /** 536 * Disable handle fault for this route. 537 * 538 * @return the builder 539 */ 540 public RouteDefinition noHandleFault() { 541 setHandleFault("false"); 542 return this; 543 } 544 545 /** 546 * Enable handle fault for this route. 547 * 548 * @return the builder 549 */ 550 public RouteDefinition handleFault() { 551 setHandleFault("true"); 552 return this; 553 } 554 555 /** 556 * Disable delayer for this route. 557 * 558 * @return the builder 559 */ 560 public RouteDefinition noDelayer() { 561 setDelayer("0"); 562 return this; 563 } 564 565 /** 566 * Enable delayer for this route. 567 * 568 * @param delay delay in millis 569 * @return the builder 570 */ 571 public RouteDefinition delayer(long delay) { 572 setDelayer("" + delay); 573 return this; 574 } 575 576 /** 577 * Installs the given <a href="http://camel.apache.org/error-handler.html">error handler</a> builder. 578 * 579 * @param errorHandlerBuilder the error handler to be used by default for all child routes 580 * @return the current builder with the error handler configured 581 */ 582 public RouteDefinition errorHandler(ErrorHandlerFactory errorHandlerBuilder) { 583 setErrorHandlerBuilder(errorHandlerBuilder); 584 // we are now using a route scoped error handler 585 contextScopedErrorHandler = false; 586 return this; 587 } 588 589 /** 590 * Disables this route from being auto started when Camel starts. 591 * 592 * @return the builder 593 */ 594 public RouteDefinition noAutoStartup() { 595 setAutoStartup("false"); 596 return this; 597 } 598 599 /** 600 * Sets the auto startup property on this route. 601 * 602 * @param autoStartup whether to auto startup (true or false), the value can be a property placeholder 603 * @return the builder 604 */ 605 public RouteDefinition autoStartup(String autoStartup) { 606 setAutoStartup(autoStartup); 607 return this; 608 } 609 610 /** 611 * Sets the auto startup property on this route. 612 * 613 * @param autoStartup - boolean indicator 614 * @return the builder 615 */ 616 public RouteDefinition autoStartup(boolean autoStartup) { 617 setAutoStartup(Boolean.toString(autoStartup)); 618 return this; 619 } 620 621 /** 622 * Configures the startup order for this route 623 * <p/> 624 * Camel will reorder routes and star them ordered by 0..N where 0 is the lowest number and N the highest number. 625 * Camel will stop routes in reverse order when its stopping. 626 * 627 * @param order the order represented as a number 628 * @return the builder 629 */ 630 public RouteDefinition startupOrder(int order) { 631 setStartupOrder(order); 632 return this; 633 } 634 635 /** 636 * Configures route policies for this route 637 * 638 * @param policies the route policies 639 * @return the builder 640 */ 641 public RouteDefinition routePolicy(RoutePolicy... policies) { 642 if (routePolicies == null) { 643 routePolicies = new ArrayList<>(); 644 } 645 for (RoutePolicy policy : policies) { 646 routePolicies.add(policy); 647 } 648 return this; 649 } 650 651 /** 652 * Configures a route policy for this route 653 * 654 * @param routePolicyRef reference to a {@link RoutePolicy} to lookup and use. 655 * You can specify multiple references by separating using comma. 656 * @return the builder 657 */ 658 public RouteDefinition routePolicyRef(String routePolicyRef) { 659 setRoutePolicyRef(routePolicyRef); 660 return this; 661 } 662 663 /** 664 * Configures a shutdown route option. 665 * 666 * @param shutdownRoute the option to use when shutting down this route 667 * @return the builder 668 */ 669 public RouteDefinition shutdownRoute(ShutdownRoute shutdownRoute) { 670 setShutdownRoute(shutdownRoute); 671 return this; 672 } 673 674 /** 675 * Configures a shutdown running task option. 676 * 677 * @param shutdownRunningTask the option to use when shutting down and how to act upon running tasks. 678 * @return the builder 679 */ 680 public RouteDefinition shutdownRunningTask(ShutdownRunningTask shutdownRunningTask) { 681 setShutdownRunningTask(shutdownRunningTask); 682 return this; 683 } 684 685 /** 686 * Declare the expected data type of the input message. If the actual message type is different 687 * at runtime, camel look for a required {@link Transformer} and apply if exists. 688 * The type name consists of two parts, 'scheme' and 'name' connected with ':'. For Java type 'name' 689 * is a fully qualified class name. For example {@code java:java.lang.String}, {@code json:ABCOrder}. 690 * 691 * @see org.apache.camel.spi.Transformer 692 * 693 * @param urn input type URN 694 * @return the builder 695 */ 696 public RouteDefinition inputType(String urn) { 697 inputType = new InputTypeDefinition(); 698 inputType.setUrn(urn); 699 inputType.setValidate(false); 700 return this; 701 } 702 703 /** 704 * Declare the expected data type of the input message with content validation enabled. 705 * If the actual message type is different at runtime, camel look for a required 706 * {@link Transformer} and apply if exists, and then applies {@link Validator} as well. 707 * The type name consists of two parts, 'scheme' and 'name' connected with ':'. For Java type 'name' 708 * is a fully qualified class name. For example {@code java:java.lang.String}, {@code json:ABCOrder}. 709 * 710 * @see org.apache.camel.spi.Transformer 711 * @see org.apache.camel.spi.Validator 712 * 713 * @param urn input type URN 714 * @return the builder 715 */ 716 public RouteDefinition inputTypeWithValidate(String urn) { 717 inputType = new InputTypeDefinition(); 718 inputType.setUrn(urn); 719 inputType.setValidate(true); 720 return this; 721 } 722 723 /** 724 * Declare the expected data type of the input message by Java class. 725 * If the actual message type is different at runtime, camel look for a required 726 * {@link Transformer} and apply if exists. 727 * 728 * @see org.apache.camel.spi.Transformer 729 * 730 * @param clazz Class object of the input type 731 * @return the builder 732 */ 733 public RouteDefinition inputType(Class clazz) { 734 inputType = new InputTypeDefinition(); 735 inputType.setJavaClass(clazz); 736 inputType.setValidate(false); 737 return this; 738 } 739 740 /** 741 * Declare the expected data type of the input message by Java class with content validation enabled. 742 * If the actual message type is different at runtime, camel look for a required 743 * {@link Transformer} and apply if exists, and then applies {@link Validator} as well. 744 * 745 * @see org.apache.camel.spi.Transformer 746 * @see org.apache.camel.spi.Validator 747 * 748 * @param clazz Class object of the input type 749 * @return the builder 750 */ 751 public RouteDefinition inputTypeWithValidate(Class clazz) { 752 inputType = new InputTypeDefinition(); 753 inputType.setJavaClass(clazz); 754 inputType.setValidate(true); 755 return this; 756 } 757 758 /** 759 * Declare the expected data type of the output message. If the actual message type is different 760 * at runtime, camel look for a required {@link Transformer} and apply if exists. 761 * The type name consists of two parts, 'scheme' and 'name' connected with ':'. For Java type 'name' 762 * is a fully qualified class name. For example {@code java:java.lang.String}, {@code json:ABCOrder}. 763 * 764 * @see org.apache.camel.spi.Transformer 765 * 766 * @param urn output type URN 767 * @return the builder 768 */ 769 public RouteDefinition outputType(String urn) { 770 outputType = new OutputTypeDefinition(); 771 outputType.setUrn(urn); 772 outputType.setValidate(false); 773 return this; 774 } 775 776 /** 777 * Declare the expected data type of the output message with content validation enabled. 778 * If the actual message type is different at runtime, Camel look for a required 779 * {@link Transformer} and apply if exists, and then applies {@link Validator} as well. 780 * The type name consists of two parts, 'scheme' and 'name' connected with ':'. For Java type 'name' 781 * is a fully qualified class name. For example {@code java:java.lang.String}, {@code json:ABCOrder}. 782 * 783 * @see org.apache.camel.spi.Transformer 784 * @see org.apache.camel.spi.Validator 785 * 786 * @param urn output type URN 787 * @return the builder 788 */ 789 public RouteDefinition outputTypeWithValidate(String urn) { 790 outputType = new OutputTypeDefinition(); 791 outputType.setUrn(urn); 792 outputType.setValidate(true); 793 return this; 794 } 795 796 /** 797 * Declare the expected data type of the output message by Java class. 798 * If the actual message type is different at runtime, camel look for a required 799 * {@link Transformer} and apply if exists. 800 * 801 * @see org.apache.camel.spi.Transformer 802 * 803 * @param clazz Class object of the output type 804 * @return the builder 805 */ 806 public RouteDefinition outputType(Class clazz) { 807 outputType = new OutputTypeDefinition(); 808 outputType.setJavaClass(clazz); 809 outputType.setValidate(false); 810 return this; 811 } 812 813 /** 814 * Declare the expected data type of the ouput message by Java class with content validation enabled. 815 * If the actual message type is different at runtime, camel look for a required 816 * {@link Transformer} and apply if exists, and then applies {@link Validator} as well. 817 * 818 * @see org.apache.camel.spi.Transformer 819 * @see org.apache.camel.spi.Validator 820 * @param clazz Class object of the output type 821 * @return the builder 822 */ 823 public RouteDefinition outputTypeWithValidate(Class clazz) { 824 outputType = new OutputTypeDefinition(); 825 outputType.setJavaClass(clazz); 826 outputType.setValidate(true); 827 return this; 828 } 829 830 /** 831 * Adds a custom property on the route. 832 */ 833 public RouteDefinition routeProperty(String key, String value) { 834 if (routeProperties == null) { 835 routeProperties = new ArrayList<>(); 836 } 837 838 PropertyDefinition prop = new PropertyDefinition(); 839 prop.setKey(key); 840 prop.setValue(value); 841 842 routeProperties.add(prop); 843 844 return this; 845 } 846 847 // Properties 848 // ----------------------------------------------------------------------- 849 850 public List<FromDefinition> getInputs() { 851 return inputs; 852 } 853 854 /** 855 * Input to the route. 856 */ 857 @XmlElementRef 858 public void setInputs(List<FromDefinition> inputs) { 859 this.inputs = inputs; 860 } 861 862 public List<ProcessorDefinition<?>> getOutputs() { 863 return outputs; 864 } 865 866 /** 867 * Outputs are processors that determines how messages are processed by this route. 868 */ 869 @XmlElementRef 870 public void setOutputs(List<ProcessorDefinition<?>> outputs) { 871 this.outputs = outputs; 872 873 if (outputs != null) { 874 for (ProcessorDefinition<?> output : outputs) { 875 configureChild(output); 876 } 877 } 878 } 879 880 public boolean isOutputSupported() { 881 return true; 882 } 883 884 /** 885 * The group that this route belongs to; could be the name of the RouteBuilder class 886 * or be explicitly configured in the XML. 887 * <p/> 888 * May be null. 889 */ 890 public String getGroup() { 891 return group; 892 } 893 894 /** 895 * The group that this route belongs to; could be the name of the RouteBuilder class 896 * or be explicitly configured in the XML. 897 * <p/> 898 * May be null. 899 */ 900 @XmlAttribute 901 public void setGroup(String group) { 902 this.group = group; 903 } 904 905 /** 906 * Whether stream caching is enabled on this route. 907 */ 908 public String getStreamCache() { 909 return streamCache; 910 } 911 912 /** 913 * Whether stream caching is enabled on this route. 914 */ 915 @XmlAttribute 916 public void setStreamCache(String streamCache) { 917 this.streamCache = streamCache; 918 } 919 920 /** 921 * Whether tracing is enabled on this route. 922 */ 923 public String getTrace() { 924 return trace; 925 } 926 927 /** 928 * Whether tracing is enabled on this route. 929 */ 930 @XmlAttribute 931 public void setTrace(String trace) { 932 this.trace = trace; 933 } 934 935 /** 936 * Whether message history is enabled on this route. 937 */ 938 public String getMessageHistory() { 939 return messageHistory; 940 } 941 942 /** 943 * Whether message history is enabled on this route. 944 */ 945 @XmlAttribute @Metadata(defaultValue = "true") 946 public void setMessageHistory(String messageHistory) { 947 this.messageHistory = messageHistory; 948 } 949 950 /** 951 * Whether security mask for Logging is enabled on this route. 952 */ 953 public String getLogMask() { 954 return logMask; 955 } 956 957 /** 958 * Whether security mask for Logging is enabled on this route. 959 */ 960 @XmlAttribute 961 public void setLogMask(String logMask) { 962 this.logMask = logMask; 963 } 964 965 /** 966 * Whether handle fault is enabled on this route. 967 */ 968 public String getHandleFault() { 969 return handleFault; 970 } 971 972 /** 973 * Whether handle fault is enabled on this route. 974 */ 975 @XmlAttribute 976 public void setHandleFault(String handleFault) { 977 this.handleFault = handleFault; 978 } 979 980 /** 981 * Whether to slow down processing messages by a given delay in msec. 982 */ 983 public String getDelayer() { 984 return delayer; 985 } 986 987 /** 988 * Whether to slow down processing messages by a given delay in msec. 989 */ 990 @XmlAttribute 991 public void setDelayer(String delayer) { 992 this.delayer = delayer; 993 } 994 995 /** 996 * Whether to auto start this route 997 */ 998 public String getAutoStartup() { 999 return autoStartup; 1000 } 1001 1002 public boolean isAutoStartup(CamelContext camelContext) throws Exception { 1003 if (getAutoStartup() == null) { 1004 // should auto startup by default 1005 return true; 1006 } 1007 Boolean isAutoStartup = CamelContextHelper.parseBoolean(camelContext, getAutoStartup()); 1008 return isAutoStartup != null && isAutoStartup; 1009 } 1010 1011 /** 1012 * Whether to auto start this route 1013 */ 1014 @XmlAttribute @Metadata(defaultValue = "true") 1015 public void setAutoStartup(String autoStartup) { 1016 this.autoStartup = autoStartup; 1017 } 1018 1019 /** 1020 * To configure the ordering of the routes being started 1021 */ 1022 public Integer getStartupOrder() { 1023 return startupOrder; 1024 } 1025 1026 /** 1027 * To configure the ordering of the routes being started 1028 */ 1029 @XmlAttribute 1030 public void setStartupOrder(Integer startupOrder) { 1031 this.startupOrder = startupOrder; 1032 } 1033 1034 /** 1035 * Sets the bean ref name of the error handler builder to use on this route 1036 */ 1037 @XmlAttribute 1038 public void setErrorHandlerRef(String errorHandlerRef) { 1039 this.errorHandlerRef = errorHandlerRef; 1040 // we use an specific error handler ref (from Spring DSL) then wrap that 1041 // with a error handler build ref so Camel knows its not just the default one 1042 setErrorHandlerBuilder(new ErrorHandlerBuilderRef(errorHandlerRef)); 1043 } 1044 1045 /** 1046 * Sets the bean ref name of the error handler builder to use on this route 1047 */ 1048 public String getErrorHandlerRef() { 1049 return errorHandlerRef; 1050 } 1051 1052 /** 1053 * Sets the error handler if one is not already set 1054 */ 1055 public void setErrorHandlerBuilderIfNull(ErrorHandlerFactory errorHandlerBuilder) { 1056 if (this.errorHandlerBuilder == null) { 1057 setErrorHandlerBuilder(errorHandlerBuilder); 1058 } 1059 } 1060 1061 /** 1062 * Reference to custom {@link org.apache.camel.spi.RoutePolicy} to use by the route. 1063 * Multiple policies can be configured by separating values using comma. 1064 */ 1065 @XmlAttribute 1066 public void setRoutePolicyRef(String routePolicyRef) { 1067 this.routePolicyRef = routePolicyRef; 1068 } 1069 1070 /** 1071 * Reference to custom {@link org.apache.camel.spi.RoutePolicy} to use by the route. 1072 * Multiple policies can be configured by separating values using comma. 1073 */ 1074 public String getRoutePolicyRef() { 1075 return routePolicyRef; 1076 } 1077 1078 public List<RoutePolicy> getRoutePolicies() { 1079 return routePolicies; 1080 } 1081 1082 @XmlTransient 1083 public void setRoutePolicies(List<RoutePolicy> routePolicies) { 1084 this.routePolicies = routePolicies; 1085 } 1086 1087 public ShutdownRoute getShutdownRoute() { 1088 return shutdownRoute; 1089 } 1090 1091 /** 1092 * To control how to shutdown the route. 1093 */ 1094 @XmlAttribute @Metadata(defaultValue = "Default") 1095 public void setShutdownRoute(ShutdownRoute shutdownRoute) { 1096 this.shutdownRoute = shutdownRoute; 1097 } 1098 1099 /** 1100 * To control how to shutdown the route. 1101 */ 1102 public ShutdownRunningTask getShutdownRunningTask() { 1103 return shutdownRunningTask; 1104 } 1105 1106 /** 1107 * To control how to shutdown the route. 1108 */ 1109 @XmlAttribute @Metadata(defaultValue = "CompleteCurrentTaskOnly") 1110 public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) { 1111 this.shutdownRunningTask = shutdownRunningTask; 1112 } 1113 1114 private ErrorHandlerFactory createErrorHandlerBuilder() { 1115 if (errorHandlerRef != null) { 1116 return new ErrorHandlerBuilderRef(errorHandlerRef); 1117 } 1118 1119 // return a reference to the default error handler 1120 return new ErrorHandlerBuilderRef(ErrorHandlerBuilderRef.DEFAULT_ERROR_HANDLER_BUILDER); 1121 } 1122 1123 @XmlTransient 1124 public ErrorHandlerFactory getErrorHandlerBuilder() { 1125 if (errorHandlerBuilder == null) { 1126 errorHandlerBuilder = createErrorHandlerBuilder(); 1127 } 1128 return errorHandlerBuilder; 1129 } 1130 1131 /** 1132 * Sets the error handler to use with processors created by this builder 1133 */ 1134 public void setErrorHandlerBuilder(ErrorHandlerFactory errorHandlerBuilder) { 1135 this.errorHandlerBuilder = errorHandlerBuilder; 1136 } 1137 1138 @XmlAttribute 1139 public Boolean isRest() { 1140 return rest; 1141 } 1142 1143 public RestDefinition getRestDefinition() { 1144 return restDefinition; 1145 } 1146 1147 @XmlTransient 1148 public void setRestDefinition(RestDefinition restDefinition) { 1149 this.restDefinition = restDefinition; 1150 } 1151 1152 public RestBindingDefinition getRestBindingDefinition() { 1153 return restBindingDefinition; 1154 } 1155 1156 @XmlTransient 1157 public void setRestBindingDefinition(RestBindingDefinition restBindingDefinition) { 1158 this.restBindingDefinition = restBindingDefinition; 1159 } 1160 1161 @SuppressWarnings("deprecation") 1162 public boolean isContextScopedErrorHandler(CamelContext context) { 1163 if (!contextScopedErrorHandler) { 1164 return false; 1165 } 1166 // if error handler ref is configured it may refer to a context scoped, so we need to check this first 1167 // the XML DSL will configure error handlers using refs, so we need this additional test 1168 if (errorHandlerRef != null) { 1169 ErrorHandlerFactory routeScoped = getErrorHandlerBuilder(); 1170 ErrorHandlerFactory contextScoped = context.getErrorHandlerBuilder(); 1171 return routeScoped != null && contextScoped != null && routeScoped == contextScoped; 1172 } 1173 1174 return true; 1175 } 1176 1177 @XmlElementRef(required = false) 1178 public void setInputType(InputTypeDefinition inputType) { 1179 this.inputType = inputType; 1180 } 1181 1182 public InputTypeDefinition getInputType() { 1183 return this.inputType; 1184 } 1185 1186 @XmlElementRef(required = false) 1187 public void setOutputType(OutputTypeDefinition outputType) { 1188 this.outputType = outputType; 1189 } 1190 1191 public OutputTypeDefinition getOutputType() { 1192 return this.outputType; 1193 } 1194 1195 public List<PropertyDefinition> getRouteProperties() { 1196 return routeProperties; 1197 } 1198 1199 /** 1200 * To set metadata as properties on the route. 1201 */ 1202 @XmlElement(name = "routeProperty") 1203 @Metadata(label = "advanced") 1204 public void setRouteProperties(List<PropertyDefinition> routeProperties) { 1205 this.routeProperties = routeProperties; 1206 } 1207 1208 // Implementation methods 1209 // ------------------------------------------------------------------------- 1210 protected RouteContext addRoutes(CamelContext camelContext, Collection<Route> routes, FromDefinition fromType) throws Exception { 1211 RouteContext routeContext = new DefaultRouteContext(camelContext, this, fromType, routes); 1212 1213 // configure tracing 1214 if (trace != null) { 1215 Boolean isTrace = CamelContextHelper.parseBoolean(camelContext, getTrace()); 1216 if (isTrace != null) { 1217 routeContext.setTracing(isTrace); 1218 if (isTrace) { 1219 log.debug("Tracing is enabled on route: {}", getId()); 1220 // tracing is added in the DefaultChannel so we can enable it on the fly 1221 } 1222 } 1223 } 1224 1225 // configure message history 1226 if (messageHistory != null) { 1227 Boolean isMessageHistory = CamelContextHelper.parseBoolean(camelContext, getMessageHistory()); 1228 if (isMessageHistory != null) { 1229 routeContext.setMessageHistory(isMessageHistory); 1230 if (isMessageHistory) { 1231 log.debug("Message history is enabled on route: {}", getId()); 1232 } 1233 } 1234 } 1235 1236 // configure Log EIP mask 1237 if (logMask != null) { 1238 Boolean isLogMask = CamelContextHelper.parseBoolean(camelContext, getLogMask()); 1239 if (isLogMask != null) { 1240 routeContext.setLogMask(isLogMask); 1241 if (isLogMask) { 1242 log.debug("Security mask for Logging is enabled on route: {}", getId()); 1243 } 1244 } 1245 } 1246 1247 // configure stream caching 1248 if (streamCache != null) { 1249 Boolean isStreamCache = CamelContextHelper.parseBoolean(camelContext, getStreamCache()); 1250 if (isStreamCache != null) { 1251 routeContext.setStreamCaching(isStreamCache); 1252 if (isStreamCache) { 1253 log.debug("StreamCaching is enabled on route: {}", getId()); 1254 } 1255 } 1256 } 1257 1258 // configure handle fault 1259 if (handleFault != null) { 1260 Boolean isHandleFault = CamelContextHelper.parseBoolean(camelContext, getHandleFault()); 1261 if (isHandleFault != null) { 1262 routeContext.setHandleFault(isHandleFault); 1263 if (isHandleFault) { 1264 log.debug("HandleFault is enabled on route: {}", getId()); 1265 // only add a new handle fault if not already a global configured on camel context 1266 if (HandleFault.getHandleFault(camelContext) == null) { 1267 addInterceptStrategy(new HandleFault()); 1268 } 1269 } 1270 } 1271 } 1272 1273 // configure delayer 1274 if (delayer != null) { 1275 Long delayer = CamelContextHelper.parseLong(camelContext, getDelayer()); 1276 if (delayer != null) { 1277 routeContext.setDelayer(delayer); 1278 if (delayer > 0) { 1279 log.debug("Delayer is enabled with: {} ms. on route: {}", delayer, getId()); 1280 } else { 1281 log.debug("Delayer is disabled on route: {}", getId()); 1282 } 1283 } 1284 } 1285 1286 // configure route policy 1287 if (routePolicies != null && !routePolicies.isEmpty()) { 1288 for (RoutePolicy policy : routePolicies) { 1289 log.debug("RoutePolicy is enabled: {} on route: {}", policy, getId()); 1290 routeContext.getRoutePolicyList().add(policy); 1291 } 1292 } 1293 if (routePolicyRef != null) { 1294 StringTokenizer policyTokens = new StringTokenizer(routePolicyRef, ","); 1295 while (policyTokens.hasMoreTokens()) { 1296 String ref = policyTokens.nextToken().trim(); 1297 RoutePolicy policy = CamelContextHelper.mandatoryLookup(camelContext, ref, RoutePolicy.class); 1298 log.debug("RoutePolicy is enabled: {} on route: {}", policy, getId()); 1299 routeContext.getRoutePolicyList().add(policy); 1300 } 1301 } 1302 if (camelContext.getRoutePolicyFactories() != null) { 1303 for (RoutePolicyFactory factory : camelContext.getRoutePolicyFactories()) { 1304 RoutePolicy policy = factory.createRoutePolicy(camelContext, getId(), this); 1305 if (policy != null) { 1306 log.debug("RoutePolicy is enabled: {} on route: {}", policy, getId()); 1307 routeContext.getRoutePolicyList().add(policy); 1308 } 1309 } 1310 } 1311 1312 // configure auto startup 1313 Boolean isAutoStartup = CamelContextHelper.parseBoolean(camelContext, getAutoStartup()); 1314 if (isAutoStartup != null) { 1315 log.debug("Using AutoStartup {} on route: {}", isAutoStartup, getId()); 1316 routeContext.setAutoStartup(isAutoStartup); 1317 } 1318 1319 // configure shutdown 1320 if (shutdownRoute != null) { 1321 log.debug("Using ShutdownRoute {} on route: {}", getShutdownRoute(), getId()); 1322 routeContext.setShutdownRoute(getShutdownRoute()); 1323 } 1324 if (shutdownRunningTask != null) { 1325 log.debug("Using ShutdownRunningTask {} on route: {}", getShutdownRunningTask(), getId()); 1326 routeContext.setShutdownRunningTask(getShutdownRunningTask()); 1327 } 1328 1329 // should inherit the intercept strategies we have defined 1330 routeContext.setInterceptStrategies(this.getInterceptStrategies()); 1331 // force endpoint resolution 1332 routeContext.getEndpoint(); 1333 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 1334 strategy.onRouteContextCreate(routeContext); 1335 } 1336 1337 // validate route has output processors 1338 if (!ProcessorDefinitionHelper.hasOutputs(outputs, true)) { 1339 RouteDefinition route = routeContext.getRoute(); 1340 String at = fromType.toString(); 1341 Exception cause = new IllegalArgumentException("Route " + route.getId() + " has no output processors." 1342 + " You need to add outputs to the route such as to(\"log:foo\")."); 1343 throw new FailedToCreateRouteException(route.getId(), route.toString(), at, cause); 1344 } 1345 1346 List<ProcessorDefinition<?>> list = new ArrayList<>(outputs); 1347 for (ProcessorDefinition<?> output : list) { 1348 try { 1349 output.addRoutes(routeContext, routes); 1350 } catch (Exception e) { 1351 RouteDefinition route = routeContext.getRoute(); 1352 throw new FailedToCreateRouteException(route.getId(), route.toString(), output.toString(), e); 1353 } 1354 } 1355 1356 routeContext.commit(); 1357 return routeContext; 1358 } 1359 1360 1361 // **************************** 1362 // Static helpers 1363 // **************************** 1364 1365 public static RouteDefinition fromUri(String uri) { 1366 return new RouteDefinition().from(uri); 1367 } 1368 1369 public static RouteDefinition fromEndpoint(Endpoint endpoint) { 1370 return new RouteDefinition().from(endpoint); 1371 } 1372 1373}