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