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