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     */
017    package org.apache.camel.model;
018    
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.List;
022    import javax.xml.bind.annotation.XmlAccessType;
023    import javax.xml.bind.annotation.XmlAccessorType;
024    import javax.xml.bind.annotation.XmlAttribute;
025    import javax.xml.bind.annotation.XmlElementRef;
026    import javax.xml.bind.annotation.XmlRootElement;
027    import javax.xml.bind.annotation.XmlTransient;
028    import javax.xml.bind.annotation.XmlType;
029    
030    import org.apache.camel.CamelContext;
031    import org.apache.camel.CamelContextAware;
032    import org.apache.camel.Endpoint;
033    import org.apache.camel.FailedToCreateRouteException;
034    import org.apache.camel.NoSuchEndpointException;
035    import org.apache.camel.Route;
036    import org.apache.camel.ServiceStatus;
037    import org.apache.camel.ShutdownRoute;
038    import org.apache.camel.ShutdownRunningTask;
039    import org.apache.camel.builder.ErrorHandlerBuilder;
040    import org.apache.camel.builder.ErrorHandlerBuilderRef;
041    import org.apache.camel.builder.RouteBuilder;
042    import org.apache.camel.impl.DefaultRouteContext;
043    import org.apache.camel.processor.interceptor.Delayer;
044    import org.apache.camel.processor.interceptor.HandleFault;
045    import org.apache.camel.processor.interceptor.StreamCaching;
046    import org.apache.camel.spi.LifecycleStrategy;
047    import org.apache.camel.spi.RouteContext;
048    import org.apache.camel.spi.RoutePolicy;
049    import org.apache.camel.util.CamelContextHelper;
050    
051    /**
052     * Represents an XML <route/> element
053     *
054     * @version $Revision: 893963 $
055     */
056    @XmlRootElement(name = "route")
057    @XmlType(propOrder = {"inputs", "outputs" })
058    @XmlAccessorType(XmlAccessType.PROPERTY)
059    public class RouteDefinition extends ProcessorDefinition<ProcessorDefinition> implements CamelContextAware {
060        private List<FromDefinition> inputs = new ArrayList<FromDefinition>();
061        private List<ProcessorDefinition> outputs = new ArrayList<ProcessorDefinition>();
062        private CamelContext camelContext;
063        private String group;
064        private Boolean streamCache;
065        private Boolean trace;
066        private Boolean handleFault;
067        private Long delayer;
068        private Boolean autoStartup = Boolean.TRUE;
069        private Integer startupOrder;
070        private RoutePolicy routePolicy;
071        private String routePolicyRef;
072        private ShutdownRoute shutdownRoute;
073        private ShutdownRunningTask shutdownRunningTask;
074    
075        public RouteDefinition() {
076        }
077    
078        public RouteDefinition(String uri) {
079            from(uri);
080        }
081    
082        public RouteDefinition(Endpoint endpoint) {
083            from(endpoint);
084        }
085    
086        @Override
087        public String toString() {
088            return "Route[" + inputs + " -> " + outputs + "]";
089        }
090    
091        @Override
092        public String getShortName() {
093            return "route";
094        }
095    
096        /**
097         * Returns the status of the route if it has been registered with a {@link CamelContext}
098         */
099        public ServiceStatus getStatus() {
100            if (camelContext != null) {
101                ServiceStatus answer = camelContext.getRouteStatus(this.getId());
102                if (answer == null) {
103                    answer = ServiceStatus.Stopped;
104                }
105                return answer;
106            }
107            return null;
108        }
109    
110        public boolean isStartable() {
111            ServiceStatus status = getStatus();
112            if (status == null) {
113                return true;
114            } else {
115                return status.isStartable();
116            }
117        }
118    
119        public boolean isStoppable() {
120            ServiceStatus status = getStatus();
121            if (status == null) {
122                return false;
123            } else {
124                return status.isStoppable();
125            }
126        }
127        
128        public List<RouteContext> addRoutes(CamelContext context, Collection<Route> routes) throws Exception {
129            List<RouteContext> answer = new ArrayList<RouteContext>();
130            setCamelContext(context);
131    
132            ErrorHandlerBuilder handler = context.getErrorHandlerBuilder();
133            if (handler != null) {
134                setErrorHandlerBuilderIfNull(handler);
135            }
136    
137            for (FromDefinition fromType : inputs) {
138                RouteContext routeContext;
139                try {
140                    routeContext = addRoutes(routes, fromType);
141                } catch (FailedToCreateRouteException e) {
142                    throw e;
143                } catch (Exception e) {
144                    // wrap in exception which provide more details about which route was failing
145                    throw new FailedToCreateRouteException(getId(), toString(), e);
146                }
147                answer.add(routeContext);
148            }
149            return answer;
150        }
151    
152    
153        public Endpoint resolveEndpoint(String uri) throws NoSuchEndpointException {
154            CamelContext context = getCamelContext();
155            if (context == null) {
156                throw new IllegalArgumentException("CamelContext has not been injected!");
157            }
158            return CamelContextHelper.getMandatoryEndpoint(context, uri);
159        }
160    
161        /**
162         * Advices this route with the route builder.
163         * <p/>
164         * The advice process will add the interceptors, on exceptions, on completions etc. configured
165         * from the route builder to this route.
166         * <p/>
167         * This is mostly used for testing purpose to add interceptors and the likes to an existing route.
168         * <p/>
169         * Will stop and remove the old route from camel context and add and start this new advised route.
170         *
171         * @param builder the route builder
172         * @return a new route which is this route merged with the route builder
173         * @throws Exception can be thrown from the route builder
174         */
175        public RouteDefinition adviceWith(RouteBuilder builder) throws Exception {
176            CamelContext context = getCamelContext();
177            if (context == null) {
178                throw new IllegalArgumentException("CamelContext has not been injected!");
179            }
180    
181            // configure and prepare the routes from the builder
182            RoutesDefinition routes = builder.configureRoutes(context);
183    
184            // we can only advice with a route builder without any routes
185            if (!routes.getRoutes().isEmpty()) {
186                throw new IllegalArgumentException("You can only advice from a RouteBuilder which has no existing routes."
187                        + " Remove all routes from the route builder.");
188            }
189    
190            // stop and remove this existing route
191            List<RouteDefinition> list = new ArrayList<RouteDefinition>();
192            list.add(this);
193            context.removeRouteDefinitions(list);
194    
195            // now merge which also ensures that interceptors and the likes get mixed in correctly as well
196            RouteDefinition merged = routes.route(this);
197    
198            // add the new merged route
199            context.getRouteDefinitions().add(0, merged);
200    
201            // and start it
202            context.startRoute(merged);
203            return merged;
204        }
205    
206        // Fluent API
207        // -----------------------------------------------------------------------
208    
209        /**
210         * Creates an input to the route
211         *
212         * @param uri  the from uri
213         * @return the builder
214         */
215        public RouteDefinition from(String uri) {
216            getInputs().add(new FromDefinition(uri));
217            return this;
218        }
219    
220        /**
221         * Creates an input to the route
222         *
223         * @param endpoint  the from endpoint
224         * @return the builder
225         */
226        public RouteDefinition from(Endpoint endpoint) {
227            getInputs().add(new FromDefinition(endpoint));
228            return this;
229        }
230    
231        /**
232         * Creates inputs to the route
233         *
234         * @param uris  the from uris
235         * @return the builder
236         */
237        public RouteDefinition from(String... uris) {
238            for (String uri : uris) {
239                getInputs().add(new FromDefinition(uri));
240            }
241            return this;
242        }
243    
244        /**
245         * Creates inputs to the route
246         *
247         * @param endpoints  the from endpoints
248         * @return the builder
249         */
250        public RouteDefinition from(Endpoint... endpoints) {
251            for (Endpoint endpoint : endpoints) {
252                getInputs().add(new FromDefinition(endpoint));
253            }
254            return this;
255        }
256    
257        /**
258         * Set the group name for this route
259         *
260         * @param name  the group name
261         * @return the builder
262         */
263        public RouteDefinition group(String name) {
264            setGroup(name);
265            return this;
266        }
267    
268        /**
269         * Set the route id for this route
270         *
271         * @param id  the route id
272         * @return the builder
273         */
274        public RouteDefinition routeId(String id) {
275            setId(id);
276            return this;
277        }
278    
279        /**
280         * Disable stream caching for this route.
281         */
282        public RouteDefinition noStreamCaching() {
283            setStreamCache(Boolean.FALSE);
284            StreamCaching.noStreamCaching(getInterceptStrategies());
285            return this;
286        }
287    
288        /**
289         * Enable stream caching for this route.
290         */
291        public RouteDefinition streamCaching() {
292            setStreamCache(Boolean.TRUE);
293            StreamCaching cache = StreamCaching.getStreamCaching(getCamelContext());
294            if (cache == null) {
295                cache = new StreamCaching();
296            }
297    
298            getInterceptStrategies().add(cache);
299            return this;
300        }
301    
302        /**
303         * Disable tracing for this route.
304         */
305        public RouteDefinition noTracing() {
306            setTrace(false);
307            return this;
308        }
309    
310        /**
311         * Enable tracing for this route.
312         */
313        public RouteDefinition tracing() {
314            setTrace(true);
315            return this;
316        }
317    
318        /**
319         * Disable handle fault for this route.
320         */
321        public RouteDefinition noHandleFault() {
322            setHandleFault(false);
323            return this;
324        }
325    
326        /**
327         * Enable handle fault for this route.
328         */
329        public RouteDefinition handleFault() {
330            setHandleFault(true);
331            return this;
332        }
333    
334        /**
335         * Disable delayer for this route.
336         */
337        public RouteDefinition noDelayer() {
338            setDelayer(0L);
339            return this;
340        }
341    
342        /**
343         * Enable delayer for this route.
344         *
345         * @param delay delay in millis
346         */
347        public RouteDefinition delayer(long delay) {
348            setDelayer(delay);
349            return this;
350        }
351    
352        /**
353         * Installs the given <a href="http://camel.apache.org/error-handler.html">error handler</a> builder.
354         *
355         * @param errorHandlerBuilder the error handler to be used by default for all child routes
356         * @return the current builder with the error handler configured
357         */
358        public RouteDefinition errorHandler(ErrorHandlerBuilder errorHandlerBuilder) {
359            setErrorHandlerBuilder(errorHandlerBuilder);
360            return this;
361        }
362    
363        /**
364         * Disables this route from being auto started when Camel starts.
365         */
366        public RouteDefinition noAutoStartup() {
367            setAutoStartup(Boolean.FALSE);
368            return this;
369        }
370    
371        /**
372         * Configures the startup order for this route
373         * <p/>
374         * Camel will reorder routes and star them ordered by 0..N where 0 is the lowest number and N the highest number.
375         * Camel will stop routes in reverse order when its stopping.
376         *
377         * @param order the order represented as a number
378         * @return this builder
379         */
380        public RouteDefinition startupOrder(int order) {
381            setStartupOrder(order);
382            return this;
383        }
384    
385        /**
386         * Disables this route from being auto started when Camel starts.
387         */
388        public RouteDefinition routePolicy(RoutePolicy routePolicy) {
389            setRoutePolicy(routePolicy);
390            return this;
391        }
392    
393        /**
394         * Configures a route policy for this route
395         *
396         * @param routePolicyRef reference to a {@link RoutePolicy} to lookup and use.
397         */
398        public RouteDefinition routePolicyRef(String routePolicyRef) {
399            setRoutePolicyRef(routePolicyRef);
400            return this;
401        }
402    
403        /**
404         * Configures a shutdown route option.
405         *
406         * @param shutdownRoute the option to use when shutting down this route
407         */
408        public RouteDefinition shutdownRoute(ShutdownRoute shutdownRoute) {
409            setShutdownRoute(shutdownRoute);
410            return this;
411        }
412    
413        /**
414         * Configures a shutdown running task option.
415         *
416         * @param shutdownRunningTask the option to use when shutting down and how to act upon running tasks.
417         */
418        public RouteDefinition shutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
419            setShutdownRunningTask(shutdownRunningTask);
420            return this;
421        }
422    
423        // Properties
424        // -----------------------------------------------------------------------
425    
426        public List<FromDefinition> getInputs() {
427            return inputs;
428        }
429    
430        @XmlElementRef
431        public void setInputs(List<FromDefinition> inputs) {
432            this.inputs = inputs;
433        }
434    
435        public List<ProcessorDefinition> getOutputs() {
436            return outputs;
437        }
438    
439        @XmlElementRef
440        public void setOutputs(List<ProcessorDefinition> outputs) {
441            this.outputs = outputs;
442    
443            if (outputs != null) {
444                for (ProcessorDefinition output : outputs) {
445                    configureChild(output);
446                }
447            }
448        }
449    
450        public CamelContext getCamelContext() {
451            return camelContext;
452        }
453    
454        @XmlTransient
455        public void setCamelContext(CamelContext camelContext) {
456            this.camelContext = camelContext;
457        }
458    
459        /**
460         * The group that this route belongs to; could be the name of the RouteBuilder class
461         * or be explicitly configured in the XML.
462         *
463         * May be null.
464         */
465        public String getGroup() {
466            return group;
467        }
468    
469        @XmlAttribute
470        public void setGroup(String group) {
471            this.group = group;
472        }
473    
474        public Boolean isStreamCache() {
475            return streamCache;
476        }
477    
478        @XmlAttribute
479        public void setStreamCache(Boolean streamCache) {
480            this.streamCache = streamCache;
481        }
482    
483        public Boolean isTrace() {
484            return trace;
485        }
486    
487        @XmlAttribute
488        public void setTrace(Boolean trace) {
489            this.trace = trace;
490        }
491    
492        public Boolean isHandleFault() {
493            return handleFault;
494        }
495    
496        @XmlAttribute
497        public void setHandleFault(Boolean handleFault) {
498            this.handleFault = handleFault;
499        }
500    
501        public Long getDelayer() {
502            return delayer;
503        }
504    
505        @XmlAttribute
506        public void setDelayer(Long delayer) {
507            this.delayer = delayer;
508        }
509    
510        public Boolean isAutoStartup() {
511            return autoStartup;
512        }
513    
514        @XmlAttribute
515        public void setAutoStartup(Boolean autoStartup) {
516            this.autoStartup = autoStartup;
517        }
518    
519        public Integer getStartupOrder() {
520            return startupOrder;
521        }
522    
523        @XmlAttribute
524        public void setStartupOrder(Integer startupOrder) {
525            this.startupOrder = startupOrder;
526        }
527    
528        /**
529         * Sets the bean ref name of the error handler builder to use on this route
530         */
531        @XmlAttribute
532        public void setErrorHandlerRef(String errorHandlerRef) {
533            this.errorHandlerRef = errorHandlerRef;
534            // we use an specific error handler ref (from Spring DSL) then wrap that
535            // with a error handler build ref so Camel knows its not just the default one
536            setErrorHandlerBuilder(new ErrorHandlerBuilderRef(errorHandlerRef));
537        }
538    
539        public String getErrorHandlerRef() {
540            return errorHandlerRef;
541        }
542    
543        /**
544         * Sets the error handler if one is not already set
545         */
546        protected void setErrorHandlerBuilderIfNull(ErrorHandlerBuilder errorHandlerBuilder) {
547            if (this.errorHandlerBuilder == null) {
548                setErrorHandlerBuilder(errorHandlerBuilder);
549            }
550        }
551    
552        @XmlAttribute
553        public void setRoutePolicyRef(String routePolicyRef) {
554            this.routePolicyRef = routePolicyRef;
555        }
556    
557        public String getRoutePolicyRef() {
558            return routePolicyRef;
559        }
560    
561        @XmlTransient
562        public void setRoutePolicy(RoutePolicy routePolicy) {
563            this.routePolicy = routePolicy;
564        }
565    
566        public RoutePolicy getRoutePolicy() {
567            return routePolicy;
568        }
569    
570        public ShutdownRoute getShutdownRoute() {
571            return shutdownRoute;
572        }
573    
574        @XmlAttribute
575        public void setShutdownRoute(ShutdownRoute shutdownRoute) {
576            this.shutdownRoute = shutdownRoute;
577        }
578    
579        public ShutdownRunningTask getShutdownRunningTask() {
580            return shutdownRunningTask;
581        }
582    
583        @XmlAttribute
584        public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
585            this.shutdownRunningTask = shutdownRunningTask;
586        }
587    
588        // Implementation methods
589        // -------------------------------------------------------------------------
590        protected RouteContext addRoutes(Collection<Route> routes, FromDefinition fromType) throws Exception {
591            RouteContext routeContext = new DefaultRouteContext(getCamelContext(), this, fromType, routes);
592    
593            // configure tracing
594            if (trace != null) {
595                routeContext.setTracing(isTrace());
596                if (isTrace()) {
597                    if (log.isDebugEnabled()) {
598                        log.debug("Tracing is enabled on route: " + this);
599                    }
600                    // tracing is added in the DefaultChannel so we can enable it on the fly
601                }
602            }
603    
604            // configure stream caching
605            if (streamCache != null) {
606                routeContext.setStreamCaching(isStreamCache());
607                if (isStreamCache()) {
608                    if (log.isDebugEnabled()) {
609                        log.debug("StreamCaching is enabled on route: " + this);
610                    }
611                    // only add a new stream cache if not already a global configured on camel context
612                    if (StreamCaching.getStreamCaching(getCamelContext()) == null) {
613                        addInterceptStrategy(new StreamCaching());
614                    }
615                }
616            }
617    
618            // configure handle fault
619            if (handleFault != null) {
620                routeContext.setHandleFault(isHandleFault());
621                if (isHandleFault()) {
622                    if (log.isDebugEnabled()) {
623                        log.debug("HandleFault is enabled on route: " + this);
624                    }
625                    // only add a new handle fault if not already a global configured on camel context
626                    if (HandleFault.getHandleFault(getCamelContext()) == null) {
627                        addInterceptStrategy(new HandleFault());
628                    }
629                }
630            }
631    
632            // configure delayer
633            if (delayer != null) {
634                routeContext.setDelayer(getDelayer());
635                if (getDelayer() != null) {
636                    long millis = getDelayer();
637                    if (millis > 0) {
638                        if (log.isDebugEnabled()) {
639                            log.debug("Delayer is enabled with: " + millis + " ms. on route: " + this);
640                        }
641                        addInterceptStrategy(new Delayer(millis));
642                    } else {
643                        if (log.isDebugEnabled()) {
644                            log.debug("Delayer is disabled on route: " + this);
645                        }
646                    }
647                }
648            }
649    
650            // configure route policy
651            if (routePolicy != null) {
652                if (log.isDebugEnabled()) {
653                    log.debug("RoutePolicy is enabled: " + routePolicy + " on route: " + this);
654                }
655                routeContext.setRoutePolicy(getRoutePolicy());
656            } else if (routePolicyRef != null) {
657                RoutePolicy policy = CamelContextHelper.mandatoryLookup(getCamelContext(), routePolicyRef, RoutePolicy.class);
658                if (log.isDebugEnabled()) {
659                    log.debug("RoutePolicy is enabled: " + policy + " on route: " + this);
660                }
661                routeContext.setRoutePolicy(policy);
662            }
663    
664            // configure auto startup
665            if (autoStartup != null) {
666                routeContext.setAutoStartup(isAutoStartup());
667            }
668    
669            // configure shutdown
670            if (shutdownRoute != null) {
671                routeContext.setShutdownRoute(getShutdownRoute());
672            }
673            if (shutdownRunningTask != null) {
674                routeContext.setShutdownRunningTask(getShutdownRunningTask());
675            }
676    
677            // should inherit the intercept strategies we have defined
678            routeContext.setInterceptStrategies(this.getInterceptStrategies());
679            // force endpoint resolution
680            routeContext.getEndpoint();
681            if (camelContext != null) {
682                for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
683                    strategy.onRouteContextCreate(routeContext);
684                }
685            }
686    
687            List<ProcessorDefinition> list = new ArrayList<ProcessorDefinition>(outputs);
688            for (ProcessorDefinition output : list) {
689                try {
690                    output.addRoutes(routeContext, routes);
691                } catch (Exception e) {
692                    RouteDefinition route = routeContext.getRoute();
693                    throw new FailedToCreateRouteException(route.getId(), route.toString(), output.toString(), e);
694                }
695            }
696    
697            routeContext.commit();
698            return routeContext;
699        }
700    
701    }