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