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 // stop and remove this existing route 282 camelContext.removeRouteDefinition(this); 283 284 // any advice with tasks we should execute first? 285 if (builder instanceof AdviceWithRouteBuilder) { 286 List<AdviceWithTask> tasks = ((AdviceWithRouteBuilder) builder).getAdviceWithTasks(); 287 for (AdviceWithTask task : tasks) { 288 task.task(); 289 } 290 } 291 292 // now merge which also ensures that interceptors and the likes get mixed in correctly as well 293 RouteDefinition merged = routes.route(this); 294 295 // add the new merged route 296 camelContext.getRouteDefinitions().add(0, merged); 297 298 // log the merged route at info level to make it easier to end users to spot any mistakes they may have made 299 log.info("AdviceWith route after: " + merged); 300 301 // If the camel context is started then we start the route 302 if (camelContext instanceof StatefulService) { 303 StatefulService service = (StatefulService) camelContext; 304 if (service.isStarted()) { 305 camelContext.startRoute(merged); 306 } 307 } 308 return merged; 309 } 310 311 // Fluent API 312 // ----------------------------------------------------------------------- 313 314 /** 315 * Creates an input to the route 316 * 317 * @param uri the from uri 318 * @return the builder 319 */ 320 public RouteDefinition from(@AsEndpointUri String uri) { 321 getInputs().add(new FromDefinition(uri)); 322 return this; 323 } 324 325 /** 326 * Creates an input to the route 327 * 328 * @param endpoint the from endpoint 329 * @return the builder 330 */ 331 public RouteDefinition from(Endpoint endpoint) { 332 getInputs().add(new FromDefinition(endpoint)); 333 return this; 334 } 335 336 /** 337 * Creates inputs to the route 338 * 339 * @param uris the from uris 340 * @return the builder 341 */ 342 public RouteDefinition from(@AsEndpointUri String... uris) { 343 for (String uri : uris) { 344 getInputs().add(new FromDefinition(uri)); 345 } 346 return this; 347 } 348 349 /** 350 * Creates inputs to the route 351 * 352 * @param endpoints the from endpoints 353 * @return the builder 354 */ 355 public RouteDefinition from(Endpoint... endpoints) { 356 for (Endpoint endpoint : endpoints) { 357 getInputs().add(new FromDefinition(endpoint)); 358 } 359 return this; 360 } 361 362 /** 363 * Set the group name for this route 364 * 365 * @param name the group name 366 * @return the builder 367 */ 368 public RouteDefinition group(String name) { 369 setGroup(name); 370 return this; 371 } 372 373 /** 374 * Set the route id for this route 375 * 376 * @param id the route id 377 * @return the builder 378 */ 379 public RouteDefinition routeId(String id) { 380 setId(id); 381 return this; 382 } 383 384 /** 385 * Set the route description for this route 386 * 387 * @param description the route description 388 * @return the builder 389 */ 390 public RouteDefinition routeDescription(String description) { 391 DescriptionDefinition desc = new DescriptionDefinition(); 392 desc.setText(description); 393 setDescription(desc); 394 return this; 395 } 396 397 /** 398 * Disable stream caching for this route. 399 * 400 * @return the builder 401 */ 402 public RouteDefinition noStreamCaching() { 403 setStreamCache("false"); 404 return this; 405 } 406 407 /** 408 * Enable stream caching for this route. 409 * 410 * @return the builder 411 */ 412 public RouteDefinition streamCaching() { 413 setStreamCache("true"); 414 return this; 415 } 416 417 /** 418 * Enable stream caching for this route. 419 * 420 * @param streamCache whether to use stream caching (true or false), the value can be a property placeholder 421 * @return the builder 422 */ 423 public RouteDefinition streamCaching(String streamCache) { 424 setStreamCache(streamCache); 425 return this; 426 } 427 428 /** 429 * Disable tracing for this route. 430 * 431 * @return the builder 432 */ 433 public RouteDefinition noTracing() { 434 setTrace("false"); 435 return this; 436 } 437 438 /** 439 * Enable tracing for this route. 440 * 441 * @return the builder 442 */ 443 public RouteDefinition tracing() { 444 setTrace("true"); 445 return this; 446 } 447 448 /** 449 * Enable tracing for this route. 450 * 451 * @param tracing whether to use tracing (true or false), the value can be a property placeholder 452 * @return the builder 453 */ 454 public RouteDefinition tracing(String tracing) { 455 setTrace(tracing); 456 return this; 457 } 458 459 /** 460 * Enable message history for this route. 461 * 462 * @return the builder 463 */ 464 public RouteDefinition messageHistory() { 465 setMessageHistory("true"); 466 return this; 467 } 468 469 /** 470 * Enable message history for this route. 471 * 472 * @param messageHistory whether to use message history (true or false), the value can be a property placeholder 473 * @return the builder 474 */ 475 public RouteDefinition messageHistory(String messageHistory) { 476 setMessageHistory(messageHistory); 477 return this; 478 } 479 480 /** 481 * Enable security mask for Logging on this route. 482 * 483 * @return the builder 484 */ 485 public RouteDefinition logMask() { 486 setLogMask("true"); 487 return this; 488 } 489 490 /** 491 * Sets whether security mask for logging is enabled on this route. 492 * 493 * @param logMask whether to enable security mask for Logging (true or false), the value can be a property placeholder 494 * @return the builder 495 */ 496 public RouteDefinition logMask(String logMask) { 497 setLogMask(logMask); 498 return this; 499 } 500 501 /** 502 * Disable message history for this route. 503 * 504 * @return the builder 505 */ 506 public RouteDefinition noMessageHistory() { 507 setMessageHistory("false"); 508 return this; 509 } 510 511 /** 512 * Disable handle fault for this route. 513 * 514 * @return the builder 515 */ 516 public RouteDefinition noHandleFault() { 517 setHandleFault("false"); 518 return this; 519 } 520 521 /** 522 * Enable handle fault for this route. 523 * 524 * @return the builder 525 */ 526 public RouteDefinition handleFault() { 527 setHandleFault("true"); 528 return this; 529 } 530 531 /** 532 * Disable delayer for this route. 533 * 534 * @return the builder 535 */ 536 public RouteDefinition noDelayer() { 537 setDelayer("0"); 538 return this; 539 } 540 541 /** 542 * Enable delayer for this route. 543 * 544 * @param delay delay in millis 545 * @return the builder 546 */ 547 public RouteDefinition delayer(long delay) { 548 setDelayer("" + delay); 549 return this; 550 } 551 552 /** 553 * Installs the given <a href="http://camel.apache.org/error-handler.html">error handler</a> builder. 554 * 555 * @param errorHandlerBuilder the error handler to be used by default for all child routes 556 * @return the current builder with the error handler configured 557 */ 558 public RouteDefinition errorHandler(ErrorHandlerFactory errorHandlerBuilder) { 559 setErrorHandlerBuilder(errorHandlerBuilder); 560 // we are now using a route scoped error handler 561 contextScopedErrorHandler = false; 562 return this; 563 } 564 565 /** 566 * Disables this route from being auto started when Camel starts. 567 * 568 * @return the builder 569 */ 570 public RouteDefinition noAutoStartup() { 571 setAutoStartup("false"); 572 return this; 573 } 574 575 /** 576 * Sets the auto startup property on this route. 577 * 578 * @param autoStartup whether to auto startup (true or false), the value can be a property placeholder 579 * @return the builder 580 */ 581 public RouteDefinition autoStartup(String autoStartup) { 582 setAutoStartup(autoStartup); 583 return this; 584 } 585 586 /** 587 * Sets the auto startup property on this route. 588 * 589 * @param autoStartup - boolean indicator 590 * @return the builder 591 */ 592 public RouteDefinition autoStartup(boolean autoStartup) { 593 setAutoStartup(Boolean.toString(autoStartup)); 594 return this; 595 } 596 597 /** 598 * Configures the startup order for this route 599 * <p/> 600 * Camel will reorder routes and star them ordered by 0..N where 0 is the lowest number and N the highest number. 601 * Camel will stop routes in reverse order when its stopping. 602 * 603 * @param order the order represented as a number 604 * @return the builder 605 */ 606 public RouteDefinition startupOrder(int order) { 607 setStartupOrder(order); 608 return this; 609 } 610 611 /** 612 * Configures route policies for this route 613 * 614 * @param policies the route policies 615 * @return the builder 616 */ 617 public RouteDefinition routePolicy(RoutePolicy... policies) { 618 if (routePolicies == null) { 619 routePolicies = new ArrayList<RoutePolicy>(); 620 } 621 for (RoutePolicy policy : policies) { 622 routePolicies.add(policy); 623 } 624 return this; 625 } 626 627 /** 628 * Configures a route policy for this route 629 * 630 * @param routePolicyRef reference to a {@link RoutePolicy} to lookup and use. 631 * You can specify multiple references by separating using comma. 632 * @return the builder 633 */ 634 public RouteDefinition routePolicyRef(String routePolicyRef) { 635 setRoutePolicyRef(routePolicyRef); 636 return this; 637 } 638 639 /** 640 * Configures a shutdown route option. 641 * 642 * @param shutdownRoute the option to use when shutting down this route 643 * @return the builder 644 */ 645 public RouteDefinition shutdownRoute(ShutdownRoute shutdownRoute) { 646 setShutdownRoute(shutdownRoute); 647 return this; 648 } 649 650 /** 651 * Configures a shutdown running task option. 652 * 653 * @param shutdownRunningTask the option to use when shutting down and how to act upon running tasks. 654 * @return the builder 655 */ 656 public RouteDefinition shutdownRunningTask(ShutdownRunningTask shutdownRunningTask) { 657 setShutdownRunningTask(shutdownRunningTask); 658 return this; 659 } 660 661 /** 662 * Declare the expected data type of the input message. If the actual message type is different 663 * at runtime, camel look for a required {@link Transformer} and apply if exists. 664 * The type name consists of two parts, 'scheme' and 'name' connected with ':'. For Java type 'name' 665 * is a fully qualified class name. For example {@code java:java.lang.String}, {@code json:ABCOrder}. 666 * 667 * @see org.apache.camel.spi.Transformer 668 * 669 * @param urn input type URN 670 * @return the builder 671 */ 672 public RouteDefinition inputType(String urn) { 673 inputType = new InputTypeDefinition(); 674 inputType.setUrn(urn); 675 inputType.setValidate(false); 676 return this; 677 } 678 679 /** 680 * Declare the expected data type of the input message with content validation enabled. 681 * If the actual message type is different at runtime, camel look for a required 682 * {@link Transformer} and apply if exists, and then applies {@link Validator} as well. 683 * The type name consists of two parts, 'scheme' and 'name' connected with ':'. For Java type 'name' 684 * is a fully qualified class name. For example {@code java:java.lang.String}, {@code json:ABCOrder}. 685 * 686 * @see org.apache.camel.spi.Transformer 687 * @see org.apache.camel.spi.Validator 688 * 689 * @param urn input type URN 690 * @return the builder 691 */ 692 public RouteDefinition inputTypeWithValidate(String urn) { 693 inputType = new InputTypeDefinition(); 694 inputType.setUrn(urn); 695 inputType.setValidate(true); 696 return this; 697 } 698 699 /** 700 * Declare the expected data type of the input message by Java class. 701 * If the actual message type is different at runtime, camel look for a required 702 * {@link Transformer} and apply if exists. 703 * 704 * @see org.apache.camel.spi.Transformer 705 * 706 * @param clazz Class object of the input type 707 * @return the builder 708 */ 709 public RouteDefinition inputType(Class clazz) { 710 inputType = new InputTypeDefinition(); 711 inputType.setJavaClass(clazz); 712 inputType.setValidate(false); 713 return this; 714 } 715 716 /** 717 * Declare the expected data type of the input message by Java class with content validation enabled. 718 * If the actual message type is different at runtime, camel look for a required 719 * {@link Transformer} and apply if exists, and then applies {@link Validator} as well. 720 * 721 * @see org.apache.camel.spi.Transformer 722 * @see org.apache.camel.spi.Validator 723 * 724 * @param clazz Class object of the input type 725 * @return the builder 726 */ 727 public RouteDefinition inputTypeWithValidate(Class clazz) { 728 inputType = new InputTypeDefinition(); 729 inputType.setJavaClass(clazz); 730 inputType.setValidate(true); 731 return this; 732 } 733 734 /** 735 * Declare the expected data type of the output message. If the actual message type is different 736 * at runtime, camel look for a required {@link Transformer} and apply if exists. 737 * The type name consists of two parts, 'scheme' and 'name' connected with ':'. For Java type 'name' 738 * is a fully qualified class name. For example {@code java:java.lang.String}, {@code json:ABCOrder}. 739 * 740 * @see org.apache.camel.spi.Transformer 741 * 742 * @param urn output type URN 743 * @return the builder 744 */ 745 public RouteDefinition outputType(String urn) { 746 outputType = new OutputTypeDefinition(); 747 outputType.setUrn(urn); 748 outputType.setValidate(false); 749 return this; 750 } 751 752 /** 753 * Declare the expected data type of the output message with content validation enabled. 754 * If the actual message type is different at runtime, camel look for a required 755 * {@link Transformer} and apply if exists, and then applies {@link Validator} as well. 756 * The type name consists of two parts, 'scheme' and 'name' connected with ':'. For Java type 'name' 757 * is a fully qualified class name. For example {@code java:java.lang.String}, {@code json:ABCOrder}. 758 * 759 * @see org.apache.camel.spi.Transformer 760 * @see org.apache.camel.spi.Validator 761 * 762 * @param urn output type URN 763 * @return the builder 764 */ 765 public RouteDefinition outputTypeWithValidate(String urn) { 766 outputType = new OutputTypeDefinition(); 767 outputType.setUrn(urn); 768 outputType.setValidate(true); 769 return this; 770 } 771 772 /** 773 * Declare the expected data type of the output message by Java class. 774 * If the actual message type is different at runtime, camel look for a required 775 * {@link Transformer} and apply if exists. 776 * 777 * @see org.apache.camel.spi.Transformer 778 * 779 * @param clazz Class object of the output type 780 * @return the builder 781 */ 782 public RouteDefinition outputType(Class clazz) { 783 outputType = new OutputTypeDefinition(); 784 outputType.setJavaClass(clazz); 785 outputType.setValidate(false); 786 return this; 787 } 788 789 /** 790 * Declare the expected data type of the ouput message by Java class with content validation enabled. 791 * If the actual message type is different at runtime, camel look for a required 792 * {@link Transformer} and apply if exists, and then applies {@link Validator} as well. 793 * 794 * @see org.apache.camel.spi.Transformer 795 * @see org.apache.camel.spi.Validator 796 * @param clazz Class object of the output type 797 * @return the builder 798 */ 799 public RouteDefinition outputTypeWithValidate(Class clazz) { 800 outputType = new OutputTypeDefinition(); 801 outputType.setJavaClass(clazz); 802 outputType.setValidate(true); 803 return this; 804 } 805 806 // Properties 807 // ----------------------------------------------------------------------- 808 809 public List<FromDefinition> getInputs() { 810 return inputs; 811 } 812 813 /** 814 * Input to the route. 815 */ 816 @XmlElementRef 817 public void setInputs(List<FromDefinition> inputs) { 818 this.inputs = inputs; 819 } 820 821 public List<ProcessorDefinition<?>> getOutputs() { 822 return outputs; 823 } 824 825 /** 826 * Outputs are processors that determines how messages are processed by this route. 827 */ 828 @XmlElementRef 829 public void setOutputs(List<ProcessorDefinition<?>> outputs) { 830 this.outputs = outputs; 831 832 if (outputs != null) { 833 for (ProcessorDefinition<?> output : outputs) { 834 configureChild(output); 835 } 836 } 837 } 838 839 public boolean isOutputSupported() { 840 return true; 841 } 842 843 /** 844 * The group that this route belongs to; could be the name of the RouteBuilder class 845 * or be explicitly configured in the XML. 846 * <p/> 847 * May be null. 848 */ 849 public String getGroup() { 850 return group; 851 } 852 853 /** 854 * The group that this route belongs to; could be the name of the RouteBuilder class 855 * or be explicitly configured in the XML. 856 * <p/> 857 * May be null. 858 */ 859 @XmlAttribute 860 public void setGroup(String group) { 861 this.group = group; 862 } 863 864 /** 865 * Whether stream caching is enabled on this route. 866 */ 867 public String getStreamCache() { 868 return streamCache; 869 } 870 871 /** 872 * Whether stream caching is enabled on this route. 873 */ 874 @XmlAttribute 875 public void setStreamCache(String streamCache) { 876 this.streamCache = streamCache; 877 } 878 879 /** 880 * Whether tracing is enabled on this route. 881 */ 882 public String getTrace() { 883 return trace; 884 } 885 886 /** 887 * Whether tracing is enabled on this route. 888 */ 889 @XmlAttribute 890 public void setTrace(String trace) { 891 this.trace = trace; 892 } 893 894 /** 895 * Whether message history is enabled on this route. 896 */ 897 public String getMessageHistory() { 898 return messageHistory; 899 } 900 901 /** 902 * Whether message history is enabled on this route. 903 */ 904 @XmlAttribute @Metadata(defaultValue = "true") 905 public void setMessageHistory(String messageHistory) { 906 this.messageHistory = messageHistory; 907 } 908 909 /** 910 * Whether security mask for Logging is enabled on this route. 911 */ 912 public String getLogMask() { 913 return logMask; 914 } 915 916 /** 917 * Whether security mask for Logging is enabled on this route. 918 */ 919 @XmlAttribute 920 public void setLogMask(String logMask) { 921 this.logMask = logMask; 922 } 923 924 /** 925 * Whether handle fault is enabled on this route. 926 */ 927 public String getHandleFault() { 928 return handleFault; 929 } 930 931 /** 932 * Whether handle fault is enabled on this route. 933 */ 934 @XmlAttribute 935 public void setHandleFault(String handleFault) { 936 this.handleFault = handleFault; 937 } 938 939 /** 940 * Whether to slow down processing messages by a given delay in msec. 941 */ 942 public String getDelayer() { 943 return delayer; 944 } 945 946 /** 947 * Whether to slow down processing messages by a given delay in msec. 948 */ 949 @XmlAttribute 950 public void setDelayer(String delayer) { 951 this.delayer = delayer; 952 } 953 954 /** 955 * Whether to auto start this route 956 */ 957 public String getAutoStartup() { 958 return autoStartup; 959 } 960 961 public boolean isAutoStartup(CamelContext camelContext) throws Exception { 962 if (getAutoStartup() == null) { 963 // should auto startup by default 964 return true; 965 } 966 Boolean isAutoStartup = CamelContextHelper.parseBoolean(camelContext, getAutoStartup()); 967 return isAutoStartup != null && isAutoStartup; 968 } 969 970 /** 971 * Whether to auto start this route 972 */ 973 @XmlAttribute @Metadata(defaultValue = "true") 974 public void setAutoStartup(String autoStartup) { 975 this.autoStartup = autoStartup; 976 } 977 978 /** 979 * To configure the ordering of the routes being started 980 */ 981 public Integer getStartupOrder() { 982 return startupOrder; 983 } 984 985 /** 986 * To configure the ordering of the routes being started 987 */ 988 @XmlAttribute 989 public void setStartupOrder(Integer startupOrder) { 990 this.startupOrder = startupOrder; 991 } 992 993 /** 994 * Sets the bean ref name of the error handler builder to use on this route 995 */ 996 @XmlAttribute 997 public void setErrorHandlerRef(String errorHandlerRef) { 998 this.errorHandlerRef = errorHandlerRef; 999 // we use an specific error handler ref (from Spring DSL) then wrap that 1000 // with a error handler build ref so Camel knows its not just the default one 1001 setErrorHandlerBuilder(new ErrorHandlerBuilderRef(errorHandlerRef)); 1002 } 1003 1004 /** 1005 * Sets the bean ref name of the error handler builder to use on this route 1006 */ 1007 public String getErrorHandlerRef() { 1008 return errorHandlerRef; 1009 } 1010 1011 /** 1012 * Sets the error handler if one is not already set 1013 */ 1014 public void setErrorHandlerBuilderIfNull(ErrorHandlerFactory errorHandlerBuilder) { 1015 if (this.errorHandlerBuilder == null) { 1016 setErrorHandlerBuilder(errorHandlerBuilder); 1017 } 1018 } 1019 1020 /** 1021 * Reference to custom {@link org.apache.camel.spi.RoutePolicy} to use by the route. 1022 * Multiple policies can be configured by separating values using comma. 1023 */ 1024 @XmlAttribute 1025 public void setRoutePolicyRef(String routePolicyRef) { 1026 this.routePolicyRef = routePolicyRef; 1027 } 1028 1029 /** 1030 * Reference to custom {@link org.apache.camel.spi.RoutePolicy} to use by the route. 1031 * Multiple policies can be configured by separating values using comma. 1032 */ 1033 public String getRoutePolicyRef() { 1034 return routePolicyRef; 1035 } 1036 1037 public List<RoutePolicy> getRoutePolicies() { 1038 return routePolicies; 1039 } 1040 1041 @XmlTransient 1042 public void setRoutePolicies(List<RoutePolicy> routePolicies) { 1043 this.routePolicies = routePolicies; 1044 } 1045 1046 public ShutdownRoute getShutdownRoute() { 1047 return shutdownRoute; 1048 } 1049 1050 /** 1051 * To control how to shutdown the route. 1052 */ 1053 @XmlAttribute @Metadata(defaultValue = "Default") 1054 public void setShutdownRoute(ShutdownRoute shutdownRoute) { 1055 this.shutdownRoute = shutdownRoute; 1056 } 1057 1058 /** 1059 * To control how to shutdown the route. 1060 */ 1061 public ShutdownRunningTask getShutdownRunningTask() { 1062 return shutdownRunningTask; 1063 } 1064 1065 /** 1066 * To control how to shutdown the route. 1067 */ 1068 @XmlAttribute @Metadata(defaultValue = "CompleteCurrentTaskOnly") 1069 public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) { 1070 this.shutdownRunningTask = shutdownRunningTask; 1071 } 1072 1073 private ErrorHandlerFactory createErrorHandlerBuilder() { 1074 if (errorHandlerRef != null) { 1075 return new ErrorHandlerBuilderRef(errorHandlerRef); 1076 } 1077 1078 // return a reference to the default error handler 1079 return new ErrorHandlerBuilderRef(ErrorHandlerBuilderRef.DEFAULT_ERROR_HANDLER_BUILDER); 1080 } 1081 1082 @XmlTransient 1083 public ErrorHandlerFactory getErrorHandlerBuilder() { 1084 if (errorHandlerBuilder == null) { 1085 errorHandlerBuilder = createErrorHandlerBuilder(); 1086 } 1087 return errorHandlerBuilder; 1088 } 1089 1090 /** 1091 * Sets the error handler to use with processors created by this builder 1092 */ 1093 public void setErrorHandlerBuilder(ErrorHandlerFactory errorHandlerBuilder) { 1094 this.errorHandlerBuilder = errorHandlerBuilder; 1095 } 1096 1097 @XmlAttribute 1098 public Boolean isRest() { 1099 return rest; 1100 } 1101 1102 public RestDefinition getRestDefinition() { 1103 return restDefinition; 1104 } 1105 1106 @XmlTransient 1107 public void setRestDefinition(RestDefinition restDefinition) { 1108 this.restDefinition = restDefinition; 1109 } 1110 1111 public RestBindingDefinition getRestBindingDefinition() { 1112 return restBindingDefinition; 1113 } 1114 1115 @XmlTransient 1116 public void setRestBindingDefinition(RestBindingDefinition restBindingDefinition) { 1117 this.restBindingDefinition = restBindingDefinition; 1118 } 1119 1120 @SuppressWarnings("deprecation") 1121 public boolean isContextScopedErrorHandler(CamelContext context) { 1122 if (!contextScopedErrorHandler) { 1123 return false; 1124 } 1125 // if error handler ref is configured it may refer to a context scoped, so we need to check this first 1126 // the XML DSL will configure error handlers using refs, so we need this additional test 1127 if (errorHandlerRef != null) { 1128 ErrorHandlerFactory routeScoped = getErrorHandlerBuilder(); 1129 ErrorHandlerFactory contextScoped = context.getErrorHandlerBuilder(); 1130 return routeScoped != null && contextScoped != null && routeScoped == contextScoped; 1131 } 1132 1133 return true; 1134 } 1135 1136 @XmlElementRef(required = false) 1137 public void setInputType(InputTypeDefinition inputType) { 1138 this.inputType = inputType; 1139 } 1140 1141 public InputTypeDefinition getInputType() { 1142 return this.inputType; 1143 } 1144 1145 @XmlElementRef(required = false) 1146 public void setOutputType(OutputTypeDefinition outputType) { 1147 this.outputType = outputType; 1148 } 1149 1150 public OutputTypeDefinition getOutputType() { 1151 return this.outputType; 1152 } 1153 1154 // Implementation methods 1155 // ------------------------------------------------------------------------- 1156 protected RouteContext addRoutes(CamelContext camelContext, Collection<Route> routes, FromDefinition fromType) throws Exception { 1157 RouteContext routeContext = new DefaultRouteContext(camelContext, this, fromType, routes); 1158 1159 // configure tracing 1160 if (trace != null) { 1161 Boolean isTrace = CamelContextHelper.parseBoolean(camelContext, getTrace()); 1162 if (isTrace != null) { 1163 routeContext.setTracing(isTrace); 1164 if (isTrace) { 1165 log.debug("Tracing is enabled on route: {}", getId()); 1166 // tracing is added in the DefaultChannel so we can enable it on the fly 1167 } 1168 } 1169 } 1170 1171 // configure message history 1172 if (messageHistory != null) { 1173 Boolean isMessageHistory = CamelContextHelper.parseBoolean(camelContext, getMessageHistory()); 1174 if (isMessageHistory != null) { 1175 routeContext.setMessageHistory(isMessageHistory); 1176 if (isMessageHistory) { 1177 log.debug("Message history is enabled on route: {}", getId()); 1178 } 1179 } 1180 } 1181 1182 // configure Log EIP mask 1183 if (logMask != null) { 1184 Boolean isLogMask = CamelContextHelper.parseBoolean(camelContext, getLogMask()); 1185 if (isLogMask != null) { 1186 routeContext.setLogMask(isLogMask); 1187 if (isLogMask) { 1188 log.debug("Security mask for Logging is enabled on route: {}", getId()); 1189 } 1190 } 1191 } 1192 1193 // configure stream caching 1194 if (streamCache != null) { 1195 Boolean isStreamCache = CamelContextHelper.parseBoolean(camelContext, getStreamCache()); 1196 if (isStreamCache != null) { 1197 routeContext.setStreamCaching(isStreamCache); 1198 if (isStreamCache) { 1199 log.debug("StreamCaching is enabled on route: {}", getId()); 1200 } 1201 } 1202 } 1203 1204 // configure handle fault 1205 if (handleFault != null) { 1206 Boolean isHandleFault = CamelContextHelper.parseBoolean(camelContext, getHandleFault()); 1207 if (isHandleFault != null) { 1208 routeContext.setHandleFault(isHandleFault); 1209 if (isHandleFault) { 1210 log.debug("HandleFault is enabled on route: {}", getId()); 1211 // only add a new handle fault if not already a global configured on camel context 1212 if (HandleFault.getHandleFault(camelContext) == null) { 1213 addInterceptStrategy(new HandleFault()); 1214 } 1215 } 1216 } 1217 } 1218 1219 // configure delayer 1220 if (delayer != null) { 1221 Long delayer = CamelContextHelper.parseLong(camelContext, getDelayer()); 1222 if (delayer != null) { 1223 routeContext.setDelayer(delayer); 1224 if (delayer > 0) { 1225 log.debug("Delayer is enabled with: {} ms. on route: {}", delayer, getId()); 1226 } else { 1227 log.debug("Delayer is disabled on route: {}", getId()); 1228 } 1229 } 1230 } 1231 1232 // configure route policy 1233 if (routePolicies != null && !routePolicies.isEmpty()) { 1234 for (RoutePolicy policy : routePolicies) { 1235 log.debug("RoutePolicy is enabled: {} on route: {}", policy, getId()); 1236 routeContext.getRoutePolicyList().add(policy); 1237 } 1238 } 1239 if (routePolicyRef != null) { 1240 StringTokenizer policyTokens = new StringTokenizer(routePolicyRef, ","); 1241 while (policyTokens.hasMoreTokens()) { 1242 String ref = policyTokens.nextToken().trim(); 1243 RoutePolicy policy = CamelContextHelper.mandatoryLookup(camelContext, ref, RoutePolicy.class); 1244 log.debug("RoutePolicy is enabled: {} on route: {}", policy, getId()); 1245 routeContext.getRoutePolicyList().add(policy); 1246 } 1247 } 1248 if (camelContext.getRoutePolicyFactories() != null) { 1249 for (RoutePolicyFactory factory : camelContext.getRoutePolicyFactories()) { 1250 RoutePolicy policy = factory.createRoutePolicy(camelContext, getId(), this); 1251 if (policy != null) { 1252 log.debug("RoutePolicy is enabled: {} on route: {}", policy, getId()); 1253 routeContext.getRoutePolicyList().add(policy); 1254 } 1255 } 1256 } 1257 1258 // configure auto startup 1259 Boolean isAutoStartup = CamelContextHelper.parseBoolean(camelContext, getAutoStartup()); 1260 if (isAutoStartup != null) { 1261 log.debug("Using AutoStartup {} on route: {}", isAutoStartup, getId()); 1262 routeContext.setAutoStartup(isAutoStartup); 1263 } 1264 1265 // configure shutdown 1266 if (shutdownRoute != null) { 1267 log.debug("Using ShutdownRoute {} on route: {}", getShutdownRoute(), getId()); 1268 routeContext.setShutdownRoute(getShutdownRoute()); 1269 } 1270 if (shutdownRunningTask != null) { 1271 log.debug("Using ShutdownRunningTask {} on route: {}", getShutdownRunningTask(), getId()); 1272 routeContext.setShutdownRunningTask(getShutdownRunningTask()); 1273 } 1274 1275 // should inherit the intercept strategies we have defined 1276 routeContext.setInterceptStrategies(this.getInterceptStrategies()); 1277 // force endpoint resolution 1278 routeContext.getEndpoint(); 1279 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 1280 strategy.onRouteContextCreate(routeContext); 1281 } 1282 1283 // validate route has output processors 1284 if (!ProcessorDefinitionHelper.hasOutputs(outputs, true)) { 1285 RouteDefinition route = routeContext.getRoute(); 1286 String at = fromType.toString(); 1287 Exception cause = new IllegalArgumentException("Route " + route.getId() + " has no output processors." 1288 + " You need to add outputs to the route such as to(\"log:foo\")."); 1289 throw new FailedToCreateRouteException(route.getId(), route.toString(), at, cause); 1290 } 1291 1292 List<ProcessorDefinition<?>> list = new ArrayList<ProcessorDefinition<?>>(outputs); 1293 for (ProcessorDefinition<?> output : list) { 1294 try { 1295 output.addRoutes(routeContext, routes); 1296 } catch (Exception e) { 1297 RouteDefinition route = routeContext.getRoute(); 1298 throw new FailedToCreateRouteException(route.getId(), route.toString(), output.toString(), e); 1299 } 1300 } 1301 1302 routeContext.commit(); 1303 return routeContext; 1304 } 1305 1306 1307 // **************************** 1308 // Static helpers 1309 // **************************** 1310 1311 public static RouteDefinition fromUri(String uri) { 1312 return new RouteDefinition().from(uri); 1313 } 1314 1315 public static RouteDefinition fromEndpoint(Endpoint endpoint) { 1316 return new RouteDefinition().from(endpoint); 1317 } 1318 1319}