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.builder;
018
019import java.util.concurrent.atomic.AtomicBoolean;
020
021import org.apache.camel.CamelContext;
022import org.apache.camel.Endpoint;
023import org.apache.camel.RoutesBuilder;
024import org.apache.camel.impl.DefaultCamelContext;
025import org.apache.camel.model.InterceptDefinition;
026import org.apache.camel.model.InterceptFromDefinition;
027import org.apache.camel.model.InterceptSendToEndpointDefinition;
028import org.apache.camel.model.ModelCamelContext;
029import org.apache.camel.model.OnCompletionDefinition;
030import org.apache.camel.model.OnExceptionDefinition;
031import org.apache.camel.model.RouteDefinition;
032import org.apache.camel.model.RoutesDefinition;
033
034import org.slf4j.Logger;
035import 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 */
043public 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}