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 }