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