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}