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