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.builder;
018    
019    import java.util.concurrent.atomic.AtomicBoolean;
020    
021    import org.apache.camel.CamelContext;
022    import org.apache.camel.Endpoint;
023    import org.apache.camel.RoutesBuilder;
024    import org.apache.camel.impl.DefaultCamelContext;
025    import org.apache.camel.model.InterceptDefinition;
026    import org.apache.camel.model.InterceptFromDefinition;
027    import org.apache.camel.model.InterceptSendToEndpointDefinition;
028    import org.apache.camel.model.ModelCamelContext;
029    import org.apache.camel.model.OnCompletionDefinition;
030    import org.apache.camel.model.OnExceptionDefinition;
031    import org.apache.camel.model.RouteDefinition;
032    import org.apache.camel.model.RoutesDefinition;
033    
034    import org.slf4j.Logger;
035    import org.slf4j.LoggerFactory;
036    
037    /**
038     * A <a href="http://camel.apache.org/dsl.html">Java DSL</a> which is
039     * used to build {@link org.apache.camel.impl.DefaultRoute} instances in a {@link CamelContext} for smart routing.
040     *
041     * @version 
042     */
043    public abstract class RouteBuilder extends BuilderSupport implements RoutesBuilder {
044        protected Logger log = LoggerFactory.getLogger(getClass());
045        private AtomicBoolean initialized = new AtomicBoolean(false);
046        private RoutesDefinition routeCollection = new RoutesDefinition();
047    
048        public RouteBuilder() {
049            this(null);
050        }
051    
052        public RouteBuilder(CamelContext context) {
053            super(context);
054        }
055    
056        @Override
057        public String toString() {
058            return getRouteCollection().toString();
059        }
060    
061        /**
062         * <b>Called on initialization to build the routes using the fluent builder syntax.</b>
063         * <p/>
064         * This is a central method for RouteBuilder implementations to implement
065         * the routes using the Java fluent builder syntax.
066         *
067         * @throws Exception can be thrown during configuration
068         */
069        public abstract void configure() throws Exception;
070    
071        /**
072         * Creates a new route from the given URI input
073         *
074         * @param uri  the from uri
075         * @return the builder
076         */
077        public RouteDefinition from(String uri) {
078            getRouteCollection().setCamelContext(getContext());
079            RouteDefinition answer = getRouteCollection().from(uri);
080            configureRoute(answer);
081            return answer;
082        }
083    
084        /**
085         * Creates a new route from the given URI input
086         *
087         * @param uri  the String formatted from uri
088         * @param args arguments for the string formatting of the uri
089         * @return the builder
090         */
091        public RouteDefinition fromF(String uri, Object... args) {
092            getRouteCollection().setCamelContext(getContext());
093            RouteDefinition answer = getRouteCollection().from(String.format(uri, args));
094            configureRoute(answer);
095            return answer;
096        }
097    
098        /**
099         * Creates a new route from the given endpoint
100         *
101         * @param endpoint  the from endpoint
102         * @return the builder
103         */
104        public RouteDefinition from(Endpoint endpoint) {
105            getRouteCollection().setCamelContext(getContext());
106            RouteDefinition answer = getRouteCollection().from(endpoint);
107            configureRoute(answer);
108            return answer;
109        }
110    
111        /**
112         * Creates a new route from the given URIs input
113         *
114         * @param uris  the from uris
115         * @return the builder
116         */
117        public RouteDefinition from(String... uris) {
118            getRouteCollection().setCamelContext(getContext());
119            RouteDefinition answer = getRouteCollection().from(uris);
120            configureRoute(answer);
121            return answer;
122        }
123    
124        /**
125         * Creates a new route from the given endpoint
126         *
127         * @param endpoints  the from endpoints
128         * @return the builder
129         */
130        public RouteDefinition from(Endpoint... endpoints) {
131            getRouteCollection().setCamelContext(getContext());
132            RouteDefinition answer = getRouteCollection().from(endpoints);
133            configureRoute(answer);
134            return answer;
135        }
136    
137        /**
138         * Installs the given <a href="http://camel.apache.org/error-handler.html">error handler</a> builder
139         *
140         * @param errorHandlerBuilder  the error handler to be used by default for all child routes
141         */
142        public void errorHandler(ErrorHandlerBuilder errorHandlerBuilder) {
143            if (!getRouteCollection().getRoutes().isEmpty()) {
144                throw new IllegalArgumentException("errorHandler must be defined before any routes in the RouteBuilder");
145            }
146            getRouteCollection().setCamelContext(getContext());
147            setErrorHandlerBuilder(errorHandlerBuilder);
148        }
149    
150        /**
151         * Adds a route for an interceptor that intercepts every processing step.
152         *
153         * @return the builder
154         */
155        public InterceptDefinition intercept() {
156            if (!getRouteCollection().getRoutes().isEmpty()) {
157                throw new IllegalArgumentException("intercept must be defined before any routes in the RouteBuilder");
158            }
159            getRouteCollection().setCamelContext(getContext());
160            return getRouteCollection().intercept();
161        }
162    
163        /**
164         * Adds a route for an interceptor that intercepts incoming messages on any inputs in this route
165         *
166         * @return the builder
167         */
168        public InterceptFromDefinition interceptFrom() {
169            if (!getRouteCollection().getRoutes().isEmpty()) {
170                throw new IllegalArgumentException("interceptFrom must be defined before any routes in the RouteBuilder");
171            }
172            getRouteCollection().setCamelContext(getContext());
173            return getRouteCollection().interceptFrom();
174        }
175    
176        /**
177         * Adds a route for an interceptor that intercepts incoming messages on the given endpoint.
178         *
179         * @param uri  endpoint uri
180         * @return the builder
181         */
182        public InterceptFromDefinition interceptFrom(String uri) {
183            if (!getRouteCollection().getRoutes().isEmpty()) {
184                throw new IllegalArgumentException("interceptFrom must be defined before any routes in the RouteBuilder");
185            }
186            getRouteCollection().setCamelContext(getContext());
187            return getRouteCollection().interceptFrom(uri);
188        }
189    
190        /**
191         * Applies a route for an interceptor if an exchange is send to the given endpoint
192         *
193         * @param uri  endpoint uri
194         * @return the builder
195         */
196        public InterceptSendToEndpointDefinition interceptSendToEndpoint(String uri) {
197            if (!getRouteCollection().getRoutes().isEmpty()) {
198                throw new IllegalArgumentException("interceptSendToEndpoint must be defined before any routes in the RouteBuilder");
199            }
200            getRouteCollection().setCamelContext(getContext());
201            return getRouteCollection().interceptSendToEndpoint(uri);
202        }
203    
204        /**
205         * <a href="http://camel.apache.org/exception-clause.html">Exception clause</a>
206         * for catching certain exceptions and handling them.
207         *
208         * @param exception exception to catch
209         * @return the builder
210         */
211        public OnExceptionDefinition onException(Class<? extends Throwable> exception) {
212            // is only allowed at the top currently
213            if (!getRouteCollection().getRoutes().isEmpty()) {
214                throw new IllegalArgumentException("onException must be defined before any routes in the RouteBuilder");
215            }
216            getRouteCollection().setCamelContext(getContext());
217            return getRouteCollection().onException(exception);
218        }
219    
220        /**
221         * <a href="http://camel.apache.org/exception-clause.html">Exception clause</a>
222         * for catching certain exceptions and handling them.
223         *
224         * @param exceptions list of exceptions to catch
225         * @return the builder
226         */
227        public OnExceptionDefinition onException(Class<? extends Throwable>... exceptions) {
228            OnExceptionDefinition last = null;
229            for (Class<? extends Throwable> ex : exceptions) {
230                last = last == null ? onException(ex) : last.onException(ex);
231            }
232            return last != null ? last : onException(Exception.class);
233        }
234    
235        /**
236         * <a href="http://camel.apache.org/oncompletion.html">On completion</a>
237         * callback for doing custom routing when the {@link org.apache.camel.Exchange} is complete.
238         *
239         * @return the builder
240         */
241        public OnCompletionDefinition onCompletion() {
242            // is only allowed at the top currently
243            if (!getRouteCollection().getRoutes().isEmpty()) {
244                throw new IllegalArgumentException("onCompletion must be defined before any routes in the RouteBuilder");
245            }
246            getRouteCollection().setCamelContext(getContext());
247            return getRouteCollection().onCompletion();
248        }
249        
250        // Properties
251        // -----------------------------------------------------------------------
252        public ModelCamelContext getContext() {
253            ModelCamelContext context = super.getContext();
254            if (context == null) {
255                context = createContainer();
256                setContext(context);
257            }
258            return context;
259        }
260    
261        public void addRoutesToCamelContext(CamelContext context) throws Exception {
262            configureRoutes((ModelCamelContext)context);
263            // add routes to Camel by populating them
264            populateRoutes();
265        }
266    
267        /**
268         * Configures the routes
269         *
270         * @param context the Camel context
271         * @return the routes configured
272         * @throws Exception can be thrown during configuration
273         */
274        public RoutesDefinition configureRoutes(ModelCamelContext context) throws Exception {
275            setContext(context);
276            checkInitialized();
277            routeCollection.setCamelContext(context);
278            return routeCollection;
279        }
280    
281        /**
282         * Includes the routes from the build to this builder.
283         * <p/>
284         * This allows you to use other builds as route templates.
285         * @param routes other builder with routes to include
286         *
287         * @throws Exception can be thrown during configuration
288         */
289        public void includeRoutes(RoutesBuilder routes) throws Exception {
290            // TODO: We should support including multiple routes so I think invoking configure()
291            // needs to be deferred to later
292            if (routes instanceof RouteBuilder) {
293                // if its a RouteBuilder then let it use my route collection and error handler
294                // then we are integrated seamless
295                RouteBuilder builder = (RouteBuilder) routes;
296                builder.setContext(this.getContext());
297                builder.setRouteCollection(this.getRouteCollection());
298                builder.setErrorHandlerBuilder(this.getErrorHandlerBuilder());
299                // must invoke configure on the original builder so it adds its configuration to me
300                builder.configure();
301            } else {
302                getContext().addRoutes(routes);
303            }
304        }
305    
306        @Override
307        public void setErrorHandlerBuilder(ErrorHandlerBuilder errorHandlerBuilder) {
308            super.setErrorHandlerBuilder(errorHandlerBuilder);
309            getRouteCollection().setErrorHandlerBuilder(getErrorHandlerBuilder());
310        }
311    
312        // Implementation methods
313        // -----------------------------------------------------------------------
314        @SuppressWarnings("deprecation")
315        protected void checkInitialized() throws Exception {
316            if (initialized.compareAndSet(false, true)) {
317                // Set the CamelContext ErrorHandler here
318                ModelCamelContext camelContext = getContext();
319                if (camelContext.getErrorHandlerBuilder() != null) {
320                    setErrorHandlerBuilder(camelContext.getErrorHandlerBuilder());
321                }
322                configure();
323                // mark all route definitions as custom prepared because
324                // a route builder prepares the route definitions correctly already
325                for (RouteDefinition route : getRouteCollection().getRoutes()) {
326                    route.markPrepared();
327                }
328            }
329        }
330    
331        protected void populateRoutes() throws Exception {
332            ModelCamelContext camelContext = getContext();
333            if (camelContext == null) {
334                throw new IllegalArgumentException("CamelContext has not been injected!");
335            }
336            getRouteCollection().setCamelContext(camelContext);
337            camelContext.addRouteDefinitions(getRouteCollection().getRoutes());
338        }
339    
340        public void setRouteCollection(RoutesDefinition routeCollection) {
341            this.routeCollection = routeCollection;
342        }
343    
344        public RoutesDefinition getRouteCollection() {
345            return this.routeCollection;
346        }
347    
348        /**
349         * Factory method
350         *
351         * @return the CamelContext
352         */
353        protected ModelCamelContext createContainer() {
354            return new DefaultCamelContext();
355        }
356    
357        protected void configureRoute(RouteDefinition route) {
358            route.setGroup(getClass().getName());
359        }
360    
361        /**
362         * Adds a collection of routes to this context
363         *
364         * @param routes the routes
365         * @throws Exception if the routes could not be created for whatever reason
366         * @deprecated will be removed in Camel 3.0. Instead use {@link #includeRoutes(org.apache.camel.RoutesBuilder) includeRoutes} instead.
367         */
368        @Deprecated
369        protected void addRoutes(RoutesBuilder routes) throws Exception {
370            includeRoutes(routes);
371        }
372    
373    }