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