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