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