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.impl; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.function.Function; 027 028import org.apache.camel.CamelContext; 029import org.apache.camel.Expression; 030import org.apache.camel.FailedToStartRouteException; 031import org.apache.camel.LoggingLevel; 032import org.apache.camel.Predicate; 033import org.apache.camel.Processor; 034import org.apache.camel.Route; 035import org.apache.camel.RouteTemplateContext; 036import org.apache.camel.StartupStep; 037import org.apache.camel.ValueHolder; 038import org.apache.camel.api.management.JmxSystemPropertyKeys; 039import org.apache.camel.impl.engine.DefaultExecutorServiceManager; 040import org.apache.camel.impl.engine.RouteService; 041import org.apache.camel.impl.engine.SimpleCamelContext; 042import org.apache.camel.impl.engine.TransformerKey; 043import org.apache.camel.impl.engine.ValidatorKey; 044import org.apache.camel.impl.scan.AssignableToPackageScanFilter; 045import org.apache.camel.impl.scan.InvertingPackageScanFilter; 046import org.apache.camel.model.DataFormatDefinition; 047import org.apache.camel.model.FaultToleranceConfigurationDefinition; 048import org.apache.camel.model.Model; 049import org.apache.camel.model.ModelCamelContext; 050import org.apache.camel.model.ModelLifecycleStrategy; 051import org.apache.camel.model.ProcessorDefinition; 052import org.apache.camel.model.ProcessorDefinitionHelper; 053import org.apache.camel.model.Resilience4jConfigurationDefinition; 054import org.apache.camel.model.RouteConfigurationDefinition; 055import org.apache.camel.model.RouteDefinition; 056import org.apache.camel.model.RouteDefinitionHelper; 057import org.apache.camel.model.RouteTemplateDefinition; 058import org.apache.camel.model.RouteTemplatesDefinition; 059import org.apache.camel.model.RoutesDefinition; 060import org.apache.camel.model.TemplatedRouteDefinition; 061import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition; 062import org.apache.camel.model.language.ExpressionDefinition; 063import org.apache.camel.model.rest.RestDefinition; 064import org.apache.camel.model.rest.RestsDefinition; 065import org.apache.camel.model.transformer.TransformerDefinition; 066import org.apache.camel.model.validator.ValidatorDefinition; 067import org.apache.camel.spi.BeanRepository; 068import org.apache.camel.spi.DataFormat; 069import org.apache.camel.spi.DataType; 070import org.apache.camel.spi.ExecutorServiceManager; 071import org.apache.camel.spi.LocalBeanRepositoryAware; 072import org.apache.camel.spi.ModelReifierFactory; 073import org.apache.camel.spi.ModelToXMLDumper; 074import org.apache.camel.spi.ModelToYAMLDumper; 075import org.apache.camel.spi.PackageScanClassResolver; 076import org.apache.camel.spi.PropertiesComponent; 077import org.apache.camel.spi.Registry; 078import org.apache.camel.spi.StartupStepRecorder; 079import org.apache.camel.spi.Transformer; 080import org.apache.camel.spi.UuidGenerator; 081import org.apache.camel.spi.Validator; 082import org.apache.camel.support.CamelContextHelper; 083import org.apache.camel.support.DefaultRegistry; 084import org.apache.camel.support.LocalBeanRegistry; 085import org.apache.camel.support.PluginHelper; 086import org.apache.camel.support.SimpleUuidGenerator; 087import org.apache.camel.util.ObjectHelper; 088import org.apache.camel.util.OrderedLocationProperties; 089import org.apache.camel.util.StopWatch; 090import org.apache.camel.util.StringHelper; 091import org.apache.camel.util.concurrent.NamedThreadLocal; 092import org.slf4j.Logger; 093import org.slf4j.LoggerFactory; 094 095/** 096 * Represents the context used to configure routes and the policies to use. 097 */ 098public class DefaultCamelContext extends SimpleCamelContext implements ModelCamelContext { 099 100 // global options that can be set on CamelContext as part of concurrent testing 101 // which means options should be isolated via thread-locals and not a static instance 102 // use a HashMap to store only JDK classes in the thread-local so there will not be any Camel classes leaking 103 private static final ThreadLocal<Map<String, Object>> OPTIONS = new NamedThreadLocal<>("CamelContextOptions", HashMap::new); 104 private static final String OPTION_NO_START = "OptionNoStart"; 105 private static final String OPTION_DISABLE_JMX = "OptionDisableJMX"; 106 private static final String OPTION_EXCLUDE_ROUTES = "OptionExcludeRoutes"; 107 108 private static final Logger LOG = LoggerFactory.getLogger(DefaultCamelContext.class); 109 private static final UuidGenerator UUID = new SimpleUuidGenerator(); 110 111 private Model model = new DefaultModel(this); 112 113 /** 114 * Creates the {@link ModelCamelContext} using {@link org.apache.camel.support.DefaultRegistry} as registry. 115 * <p/> 116 * Use one of the other constructors to force use an explicit registry. 117 */ 118 public DefaultCamelContext() { 119 this(true); 120 } 121 122 /** 123 * Creates the {@link CamelContext} using the given {@link BeanRepository} as first-choice repository, and the 124 * {@link org.apache.camel.support.SimpleRegistry} as fallback, via the {@link DefaultRegistry} implementation. 125 * 126 * @param repository the bean repository. 127 */ 128 public DefaultCamelContext(BeanRepository repository) { 129 this(new DefaultRegistry(repository)); 130 } 131 132 /** 133 * Creates the {@link ModelCamelContext} using the given registry 134 * 135 * @param registry the registry 136 */ 137 public DefaultCamelContext(Registry registry) { 138 this(); 139 getCamelContextExtension().setRegistry(registry); 140 } 141 142 public DefaultCamelContext(boolean init) { 143 super(init); 144 if (isDisableJmx()) { 145 disableJMX(); 146 } 147 } 148 149 @Override 150 protected void doStop() throws Exception { 151 super.doStop(); 152 OPTIONS.remove(); 153 } 154 155 @Override 156 protected void doDumpRoutes() { 157 if ("yaml".equalsIgnoreCase(getDumpRoutes())) { 158 doDumpRoutesAsYaml(); 159 } else { 160 // xml is default 161 doDumpRoutesAsXml(); 162 } 163 } 164 165 protected void doDumpRoutesAsXml() { 166 final ModelToXMLDumper dumper = PluginHelper.getModelToXMLDumper(this); 167 168 int size = getRouteDefinitions().size(); 169 if (size > 0) { 170 LOG.info("Dumping {} routes as XML", size); 171 // for XML to output nicely all routes in one XML then lets put them into <routes> 172 RoutesDefinition def = new RoutesDefinition(); 173 def.setRoutes(getRouteDefinitions()); 174 try { 175 String xml = dumper.dumpModelAsXml(this, def, true); 176 // lets separate routes with empty line 177 xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">", 178 "xmlns=\"http://camel.apache.org/schema/spring\">\n"); 179 xml = xml.replace("</route>", "</route>\n"); 180 LOG.info("\n\n{}\n", xml); 181 } catch (Exception e) { 182 LOG.warn("Error dumping routes to XML due to {}. This exception is ignored.", e.getMessage(), e); 183 } 184 } 185 186 size = getRestDefinitions().size(); 187 if (size > 0) { 188 LOG.info("Dumping {} rests as XML", size); 189 // for XML to output nicely all routes in one XML then lets put them into <routes> 190 RestsDefinition def = new RestsDefinition(); 191 def.setRests(getRestDefinitions()); 192 try { 193 String xml = dumper.dumpModelAsXml(this, def, true); 194 // lets separate rests with empty line 195 xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">", 196 "xmlns=\"http://camel.apache.org/schema/spring\">\n"); 197 xml = xml.replace("</rest>", "</rest>\n"); 198 LOG.info("\n\n{}\n", xml); 199 } catch (Exception e) { 200 LOG.warn("Error dumping rests to XML due to {}. This exception is ignored.", e.getMessage(), e); 201 } 202 } 203 204 size = getRouteTemplateDefinitions().size(); 205 if (size > 0) { 206 LOG.info("Dumping {} route templates as XML", size); 207 // for XML to output nicely all routes in one XML then lets put them into <routes> 208 RouteTemplatesDefinition def = new RouteTemplatesDefinition(); 209 def.setRouteTemplates(getRouteTemplateDefinitions()); 210 try { 211 String xml = dumper.dumpModelAsXml(this, def, true); 212 // lets separate rests with empty line 213 xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">", 214 "xmlns=\"http://camel.apache.org/schema/spring\">\n"); 215 xml = xml.replace("</routeTemplate>", "</routeTemplate>\n"); 216 LOG.info("\n\n{}\n", xml); 217 } catch (Exception e) { 218 LOG.warn("Error dumping route-templates to XML due to {}. This exception is ignored.", e.getMessage(), e); 219 } 220 } 221 } 222 223 protected void doDumpRoutesAsYaml() { 224 final ModelToYAMLDumper dumper = PluginHelper.getModelToYAMLDumper(this); 225 226 int size = getRouteDefinitions().size(); 227 if (size > 0) { 228 LOG.info("Dumping {} routes as YAML", size); 229 RoutesDefinition def = new RoutesDefinition(); 230 def.setRoutes(getRouteDefinitions()); 231 try { 232 String yaml = dumper.dumpModelAsYaml(this, def, true, false); 233 LOG.info("\n\n{}\n", yaml); 234 } catch (Exception e) { 235 LOG.warn("Error dumping routes to YAML due to {}. This exception is ignored.", e.getMessage(), e); 236 } 237 } 238 239 size = getRestDefinitions().size(); 240 if (size > 0) { 241 LOG.info("Dumping {} rests as YAML", size); 242 RestsDefinition def = new RestsDefinition(); 243 def.setRests(getRestDefinitions()); 244 try { 245 String taml = dumper.dumpModelAsYaml(this, def, true, false); 246 LOG.info("\n\n{}\n", taml); 247 } catch (Exception e) { 248 LOG.warn("Error dumping rests to YAML due to {}. This exception is ignored.", e.getMessage(), e); 249 } 250 } 251 252 size = getRouteTemplateDefinitions().size(); 253 if (size > 0) { 254 LOG.info("Dumping {} route templates as YAML", size); 255 RouteTemplatesDefinition def = new RouteTemplatesDefinition(); 256 def.setRouteTemplates(getRouteTemplateDefinitions()); 257 try { 258 String yaml = dumper.dumpModelAsYaml(this, def, true, false); 259 LOG.info("\n\n{}\n", yaml); 260 } catch (Exception e) { 261 LOG.warn("Error dumping route-templates to YAML due to {}. This exception is ignored.", e.getMessage(), e); 262 } 263 } 264 } 265 266 public static void setNoStart(boolean b) { 267 getOptions().put(OPTION_NO_START, b); 268 } 269 270 public static boolean isNoStart() { 271 return (Boolean) getOptions().getOrDefault(OPTION_NO_START, Boolean.FALSE); 272 } 273 274 public static void setDisableJmx(boolean b) { 275 getOptions().put(OPTION_DISABLE_JMX, b); 276 } 277 278 public static boolean isDisableJmx() { 279 return (Boolean) getOptions().getOrDefault(OPTION_DISABLE_JMX, Boolean.getBoolean(JmxSystemPropertyKeys.DISABLED)); 280 } 281 282 @Override 283 public String getTestExcludeRoutes() { 284 return getExcludeRoutes(); 285 } 286 287 public static String getExcludeRoutes() { 288 return (String) getOptions().get(OPTION_EXCLUDE_ROUTES); 289 } 290 291 public static void setExcludeRoutes(String s) { 292 getOptions().put(OPTION_EXCLUDE_ROUTES, s); 293 } 294 295 public static void clearOptions() { 296 OPTIONS.get().clear(); 297 } 298 299 private static Map<String, Object> getOptions() { 300 return OPTIONS.get(); 301 } 302 303 @Override 304 public void start() { 305 // for example from unit testing we want to start Camel later (manually) 306 if (isNoStart()) { 307 LOG.trace("Ignoring start() as NO_START is true"); 308 return; 309 } 310 311 if (!isStarted() && !isStarting()) { 312 StopWatch watch = new StopWatch(); 313 super.start(); 314 LOG.debug("start() took {} millis", watch.taken()); 315 } else { 316 // ignore as Camel is already started 317 LOG.trace("Ignoring start() as Camel is already started"); 318 } 319 } 320 321 @Override 322 protected PackageScanClassResolver createPackageScanClassResolver() { 323 PackageScanClassResolver resolver = super.createPackageScanClassResolver(); 324 String excluded = getExcludeRoutes(); 325 if (ObjectHelper.isNotEmpty(excluded)) { 326 Set<Class<?>> excludedClasses = new HashSet<>(); 327 for (String str : excluded.split(",")) { 328 excludedClasses.add(getClassResolver().resolveClass(str)); 329 } 330 resolver.addFilter(new InvertingPackageScanFilter(new AssignableToPackageScanFilter(excludedClasses))); 331 } 332 return resolver; 333 } 334 335 @Override 336 public void disposeModel() { 337 LOG.debug("Disposing Model on CamelContext"); 338 model = null; 339 } 340 341 @Override 342 public void addModelLifecycleStrategy(ModelLifecycleStrategy modelLifecycleStrategy) { 343 model.addModelLifecycleStrategy(modelLifecycleStrategy); 344 } 345 346 @Override 347 public List<ModelLifecycleStrategy> getModelLifecycleStrategies() { 348 return model.getModelLifecycleStrategies(); 349 } 350 351 @Override 352 public void addRouteConfiguration(RouteConfigurationDefinition routesConfiguration) { 353 model.addRouteConfiguration(routesConfiguration); 354 } 355 356 @Override 357 public void addRouteConfigurations(List<RouteConfigurationDefinition> routesConfigurations) { 358 model.addRouteConfigurations(routesConfigurations); 359 } 360 361 @Override 362 public List<RouteConfigurationDefinition> getRouteConfigurationDefinitions() { 363 return model.getRouteConfigurationDefinitions(); 364 } 365 366 @Override 367 public RouteConfigurationDefinition getRouteConfigurationDefinition(String id) { 368 return model.getRouteConfigurationDefinition(id); 369 } 370 371 @Override 372 public void removeRouteConfiguration(RouteConfigurationDefinition routeConfigurationDefinition) throws Exception { 373 model.removeRouteConfiguration(routeConfigurationDefinition); 374 } 375 376 @Override 377 public List<RouteDefinition> getRouteDefinitions() { 378 return model.getRouteDefinitions(); 379 } 380 381 @Override 382 public RouteDefinition getRouteDefinition(String id) { 383 return model.getRouteDefinition(id); 384 } 385 386 @Override 387 public void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception { 388 model.addRouteDefinitions(routeDefinitions); 389 } 390 391 @Override 392 public void addRouteDefinition(RouteDefinition routeDefinition) throws Exception { 393 model.addRouteDefinition(routeDefinition); 394 } 395 396 @Override 397 public void removeRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception { 398 if (!isLockModel()) { 399 model.removeRouteDefinitions(routeDefinitions); 400 } 401 } 402 403 @Override 404 public void removeRouteDefinition(RouteDefinition routeDefinition) throws Exception { 405 if (!isLockModel()) { 406 model.removeRouteDefinition(routeDefinition); 407 } 408 } 409 410 @Override 411 public List<RouteTemplateDefinition> getRouteTemplateDefinitions() { 412 return model.getRouteTemplateDefinitions(); 413 } 414 415 @Override 416 public RouteTemplateDefinition getRouteTemplateDefinition(String id) { 417 return model.getRouteTemplateDefinition(id); 418 } 419 420 @Override 421 public void addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception { 422 model.addRouteTemplateDefinitions(routeTemplateDefinitions); 423 } 424 425 @Override 426 public void addRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception { 427 428 model.addRouteTemplateDefinition(routeTemplateDefinition); 429 } 430 431 @Override 432 public void removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception { 433 434 if (!isLockModel()) { 435 model.removeRouteTemplateDefinitions(routeTemplateDefinitions); 436 } 437 } 438 439 @Override 440 public void removeRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception { 441 if (!isLockModel()) { 442 model.removeRouteTemplateDefinition(routeTemplateDefinition); 443 } 444 } 445 446 @Override 447 public void removeRouteTemplateDefinitions(String pattern) throws Exception { 448 if (!isLockModel()) { 449 model.removeRouteTemplateDefinitions(pattern); 450 } 451 } 452 453 @Override 454 public void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter) { 455 456 model.addRouteTemplateDefinitionConverter(templateIdPattern, converter); 457 } 458 459 @Override 460 public String addRouteFromTemplate(String routeId, String routeTemplateId, Map<String, Object> parameters) 461 throws Exception { 462 463 return model.addRouteFromTemplate(routeId, routeTemplateId, parameters); 464 } 465 466 @Override 467 public String addRouteFromTemplate(String routeId, String routeTemplateId, String prefixId, Map<String, Object> parameters) 468 throws Exception { 469 470 return model.addRouteFromTemplate(routeId, routeTemplateId, prefixId, parameters); 471 } 472 473 @Override 474 public String addRouteFromTemplate( 475 String routeId, String routeTemplateId, String prefixId, RouteTemplateContext routeTemplateContext) 476 throws Exception { 477 478 return model.addRouteFromTemplate(routeId, routeTemplateId, prefixId, routeTemplateContext); 479 } 480 481 @Override 482 public void addRouteFromTemplatedRoute(TemplatedRouteDefinition templatedRouteDefinition) 483 throws Exception { 484 485 model.addRouteFromTemplatedRoute(templatedRouteDefinition); 486 } 487 488 @Override 489 public void removeRouteTemplates(String pattern) throws Exception { 490 491 if (!isLockModel()) { 492 model.removeRouteTemplateDefinitions(pattern); 493 } 494 } 495 496 @Override 497 public List<RestDefinition> getRestDefinitions() { 498 return model.getRestDefinitions(); 499 } 500 501 @Override 502 public void addRestDefinitions(Collection<RestDefinition> restDefinitions, boolean addToRoutes) throws Exception { 503 model.addRestDefinitions(restDefinitions, addToRoutes); 504 } 505 506 @Override 507 public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) { 508 model.setDataFormats(dataFormats); 509 } 510 511 @Override 512 public Map<String, DataFormatDefinition> getDataFormats() { 513 return model.getDataFormats(); 514 } 515 516 @Override 517 public DataFormatDefinition resolveDataFormatDefinition(String name) { 518 return model.resolveDataFormatDefinition(name); 519 } 520 521 @Override 522 public ProcessorDefinition<?> getProcessorDefinition(String id) { 523 return model.getProcessorDefinition(id); 524 } 525 526 @Override 527 public <T extends ProcessorDefinition<T>> T getProcessorDefinition(String id, Class<T> type) { 528 return model.getProcessorDefinition(id, type); 529 } 530 531 @Override 532 public void setValidators(List<ValidatorDefinition> validators) { 533 model.setValidators(validators); 534 } 535 536 @Override 537 public Resilience4jConfigurationDefinition getResilience4jConfiguration(String id) { 538 return model.getResilience4jConfiguration(id); 539 } 540 541 @Override 542 public void setResilience4jConfiguration(Resilience4jConfigurationDefinition configuration) { 543 model.setResilience4jConfiguration(configuration); 544 } 545 546 @Override 547 public void setResilience4jConfigurations(List<Resilience4jConfigurationDefinition> configurations) { 548 model.setResilience4jConfigurations(configurations); 549 } 550 551 @Override 552 public void addResilience4jConfiguration(String id, Resilience4jConfigurationDefinition configuration) { 553 model.addResilience4jConfiguration(id, configuration); 554 } 555 556 @Override 557 public FaultToleranceConfigurationDefinition getFaultToleranceConfiguration(String id) { 558 return model.getFaultToleranceConfiguration(id); 559 } 560 561 @Override 562 public void setFaultToleranceConfiguration(FaultToleranceConfigurationDefinition configuration) { 563 model.setFaultToleranceConfiguration(configuration); 564 } 565 566 @Override 567 public void setFaultToleranceConfigurations(List<FaultToleranceConfigurationDefinition> configurations) { 568 model.setFaultToleranceConfigurations(configurations); 569 } 570 571 @Override 572 public void addFaultToleranceConfiguration(String id, FaultToleranceConfigurationDefinition configuration) { 573 model.addFaultToleranceConfiguration(id, configuration); 574 } 575 576 @Override 577 public List<ValidatorDefinition> getValidators() { 578 return model.getValidators(); 579 } 580 581 @Override 582 public void setTransformers(List<TransformerDefinition> transformers) { 583 model.setTransformers(transformers); 584 } 585 586 @Override 587 public List<TransformerDefinition> getTransformers() { 588 return model.getTransformers(); 589 } 590 591 @Override 592 public ServiceCallConfigurationDefinition getServiceCallConfiguration(String serviceName) { 593 return model.getServiceCallConfiguration(serviceName); 594 } 595 596 @Override 597 public void setServiceCallConfiguration(ServiceCallConfigurationDefinition configuration) { 598 model.setServiceCallConfiguration(configuration); 599 } 600 601 @Override 602 public void setServiceCallConfigurations(List<ServiceCallConfigurationDefinition> configurations) { 603 model.setServiceCallConfigurations(configurations); 604 } 605 606 @Override 607 public void addServiceCallConfiguration(String serviceName, ServiceCallConfigurationDefinition configuration) { 608 model.addServiceCallConfiguration(serviceName, configuration); 609 } 610 611 @Override 612 public void setRouteFilterPattern(String include, String exclude) { 613 model.setRouteFilterPattern(include, exclude); 614 } 615 616 @Override 617 public void setRouteFilter(Function<RouteDefinition, Boolean> filter) { 618 model.setRouteFilter(filter); 619 } 620 621 @Override 622 public Function<RouteDefinition, Boolean> getRouteFilter() { 623 return model.getRouteFilter(); 624 } 625 626 @Override 627 public ModelReifierFactory getModelReifierFactory() { 628 return model.getModelReifierFactory(); 629 } 630 631 @Override 632 public void setModelReifierFactory(ModelReifierFactory modelReifierFactory) { 633 model.setModelReifierFactory(modelReifierFactory); 634 } 635 636 @Override 637 protected void bindDataFormats() throws Exception { 638 // eager lookup data formats and bind to registry so the dataformats can 639 // be looked up and used 640 if (model != null) { 641 for (Map.Entry<String, DataFormatDefinition> e : model.getDataFormats().entrySet()) { 642 String id = e.getKey(); 643 DataFormatDefinition def = e.getValue(); 644 LOG.debug("Creating Dataformat with id: {} and definition: {}", id, def); 645 DataFormat df = model.getModelReifierFactory().createDataFormat(this, def); 646 addService(df, true); 647 getRegistry().bind(id, df); 648 } 649 } 650 } 651 652 @Override 653 protected synchronized void shutdownRouteService(RouteService routeService) throws Exception { 654 if (model != null) { 655 RouteDefinition rd = model.getRouteDefinition(routeService.getId()); 656 if (rd != null) { 657 model.getRouteDefinitions().remove(rd); 658 } 659 } 660 super.shutdownRouteService(routeService); 661 } 662 663 @Override 664 protected boolean isStreamCachingInUse() throws Exception { 665 boolean streamCachingInUse = super.isStreamCachingInUse(); 666 if (!streamCachingInUse) { 667 for (RouteDefinition route : model.getRouteDefinitions()) { 668 Boolean routeCache = CamelContextHelper.parseBoolean(this, route.getStreamCache()); 669 if (routeCache != null && routeCache) { 670 streamCachingInUse = true; 671 break; 672 } 673 } 674 } 675 return streamCachingInUse; 676 } 677 678 @Override 679 public void startRouteDefinitions() throws Exception { 680 List<RouteDefinition> routeDefinitions = model.getRouteDefinitions(); 681 if (routeDefinitions != null) { 682 // defensive copy of routes to be started as kamelets 683 // can add route definitions from existing routes 684 List<RouteDefinition> toBeStarted = new ArrayList<>(routeDefinitions); 685 startRouteDefinitions(toBeStarted); 686 } 687 } 688 689 @Override 690 public void removeRouteDefinitionsFromTemplate() throws Exception { 691 List<RouteDefinition> toBeRemoved = new ArrayList<>(); 692 for (RouteDefinition rd : model.getRouteDefinitions()) { 693 if (rd.isTemplate() != null && rd.isTemplate()) { 694 toBeRemoved.add(rd); 695 } 696 } 697 removeRouteDefinitions(toBeRemoved); 698 } 699 700 public void startRouteDefinitions(List<RouteDefinition> routeDefinitions) throws Exception { 701 // indicate we are staring the route using this thread so 702 // we are able to query this if needed 703 boolean alreadyStartingRoutes = isStartingRoutes(); 704 if (!alreadyStartingRoutes) { 705 setStartingRoutes(true); 706 } 707 708 PropertiesComponent pc = getCamelContextReference().getPropertiesComponent(); 709 // route templates supports binding beans that are local for the template only 710 // in this local mode then we need to check for side-effects (see further) 711 LocalBeanRepositoryAware localBeans = null; 712 if (getCamelContextReference().getRegistry() instanceof LocalBeanRepositoryAware) { 713 localBeans = (LocalBeanRepositoryAware) getCamelContextReference().getRegistry(); 714 } 715 try { 716 RouteDefinitionHelper.forceAssignIds(getCamelContextReference(), routeDefinitions); 717 List<RouteDefinition> routeDefinitionsToRemove = null; 718 for (RouteDefinition routeDefinition : routeDefinitions) { 719 // assign ids to the routes and validate that the id's is all unique 720 String duplicate = RouteDefinitionHelper.validateUniqueIds(routeDefinition, routeDefinitions, 721 routeDefinition.getNodePrefixId()); 722 if (duplicate != null) { 723 throw new FailedToStartRouteException( 724 routeDefinition.getId(), 725 "duplicate id detected: " + duplicate + ". Please correct ids to be unique among all your routes."); 726 } 727 728 // if the route definition was created via a route template then we need to prepare its parameters when the route is being created and started 729 if (routeDefinition.isTemplate() != null && routeDefinition.isTemplate() 730 && routeDefinition.getTemplateParameters() != null) { 731 732 // apply configurer if any present 733 if (routeDefinition.getRouteTemplateContext().getConfigurer() != null) { 734 routeDefinition.getRouteTemplateContext().getConfigurer() 735 .accept(routeDefinition.getRouteTemplateContext()); 736 } 737 738 // copy parameters/bean repository to not cause side effect 739 Map<Object, Object> params = new HashMap<>(routeDefinition.getTemplateParameters()); 740 LocalBeanRegistry bbr 741 = (LocalBeanRegistry) routeDefinition.getRouteTemplateContext().getLocalBeanRepository(); 742 LocalBeanRegistry bbrCopy = new LocalBeanRegistry(); 743 744 // make all bean in the bean repository use unique keys (need to add uuid counter) 745 // so when the route template is used again to create another route, then there is 746 // no side-effect from previously used values that Camel may use in its endpoint 747 // registry and elsewhere 748 if (bbr != null && !bbr.isEmpty()) { 749 for (Map.Entry<Object, Object> param : params.entrySet()) { 750 Object value = param.getValue(); 751 if (value instanceof String) { 752 String oldKey = (String) value; 753 boolean clash = bbr.keys().stream().anyMatch(k -> k.equals(oldKey)); 754 if (clash) { 755 String newKey = oldKey + "-" + UUID.generateUuid(); 756 LOG.debug( 757 "Route: {} re-assigning local-bean id: {} to: {} to ensure ids are globally unique", 758 routeDefinition.getId(), oldKey, newKey); 759 bbrCopy.put(newKey, bbr.remove(oldKey)); 760 param.setValue(newKey); 761 } 762 } 763 } 764 // the remainder of the local beans must also have their ids made global unique 765 for (Map.Entry<String, Map<Class<?>, Object>> entry : bbr.entrySet()) { 766 String oldKey = entry.getKey(); 767 String newKey = oldKey + "-" + UUID.generateUuid(); 768 LOG.debug( 769 "Route: {} re-assigning local-bean id: {} to: {} to ensure ids are globally unique", 770 routeDefinition.getId(), oldKey, newKey); 771 bbrCopy.put(newKey, entry.getValue()); 772 if (!params.containsKey(oldKey)) { 773 // if a bean was bound as local bean with a key and it was not defined as template parameter 774 // then store it as if it was a template parameter with same key=value which allows us 775 // to use this local bean in the route without any problem such as: 776 // to("bean:{{myBean}}") 777 // and myBean is the local bean id. 778 params.put(oldKey, newKey); 779 } 780 } 781 } 782 783 OrderedLocationProperties prop = new OrderedLocationProperties(); 784 if (routeDefinition.getTemplateDefaultParameters() != null) { 785 // need to keep track if a parameter is set as default value or end user configured value 786 params.forEach((k, v) -> { 787 Object dv = routeDefinition.getTemplateDefaultParameters().get(k); 788 prop.put(routeDefinition.getLocation(), k, v, dv); 789 }); 790 } else { 791 prop.putAll(routeDefinition.getLocation(), params); 792 } 793 pc.setLocalProperties(prop); 794 795 // we need to shadow the bean registry on the CamelContext with the local beans from the route template context 796 if (localBeans != null && bbrCopy != null) { 797 localBeans.setLocalBeanRepository(bbrCopy); 798 } 799 800 // need to reset auto assigned ids, so there is no clash when creating routes 801 ProcessorDefinitionHelper.resetAllAutoAssignedNodeIds(routeDefinition); 802 // must re-init parent when created from a template 803 RouteDefinitionHelper.initParent(routeDefinition); 804 } 805 // Check if the route is included 806 if (includedRoute(routeDefinition)) { 807 // must ensure route is prepared, before we can start it 808 if (!routeDefinition.isPrepared()) { 809 RouteDefinitionHelper.prepareRoute(getCamelContextReference(), routeDefinition); 810 routeDefinition.markPrepared(); 811 } 812 813 StartupStepRecorder recorder 814 = getCamelContextReference().getCamelContextExtension().getStartupStepRecorder(); 815 StartupStep step = recorder.beginStep(Route.class, routeDefinition.getRouteId(), "Create Route"); 816 Route route = model.getModelReifierFactory().createRoute(this, routeDefinition); 817 recorder.endStep(step); 818 819 RouteService routeService = new RouteService(route); 820 startRouteService(routeService, true); 821 } else { 822 // Add the definition to the list of definitions to remove as the route is excluded 823 if (routeDefinitionsToRemove == null) { 824 routeDefinitionsToRemove = new ArrayList<>(routeDefinitions.size()); 825 } 826 routeDefinitionsToRemove.add(routeDefinition); 827 } 828 829 // clear local after the route is created via the reifier 830 pc.setLocalProperties(null); 831 if (localBeans != null) { 832 localBeans.setLocalBeanRepository(null); 833 } 834 } 835 if (routeDefinitionsToRemove != null) { 836 // Remove all the excluded routes 837 model.removeRouteDefinitions(routeDefinitionsToRemove); 838 } 839 } finally { 840 if (!alreadyStartingRoutes) { 841 setStartingRoutes(false); 842 } 843 pc.setLocalProperties(null); 844 if (localBeans != null) { 845 localBeans.setLocalBeanRepository(null); 846 } 847 } 848 } 849 850 @Override 851 protected ExecutorServiceManager createExecutorServiceManager() { 852 return new DefaultExecutorServiceManager(this); 853 } 854 855 @Override 856 public Processor createErrorHandler(Route route, Processor processor) throws Exception { 857 return model.getModelReifierFactory().createErrorHandler(route, processor); 858 } 859 860 @Override 861 public Expression createExpression(ExpressionDefinition definition) { 862 return model.getModelReifierFactory().createExpression(this, definition); 863 } 864 865 @Override 866 public Predicate createPredicate(ExpressionDefinition definition) { 867 return model.getModelReifierFactory().createPredicate(this, definition); 868 } 869 870 @Override 871 public void registerValidator(ValidatorDefinition def) { 872 model.getValidators().add(def); 873 Validator validator = model.getModelReifierFactory().createValidator(this, def); 874 getValidatorRegistry().put(createValidatorKey(def), validator); 875 } 876 877 private static ValueHolder<String> createValidatorKey(ValidatorDefinition def) { 878 return new ValidatorKey(new DataType(def.getType())); 879 } 880 881 @Override 882 public void registerTransformer(TransformerDefinition def) { 883 model.getTransformers().add(def); 884 Transformer transformer = model.getModelReifierFactory().createTransformer(this, def); 885 getTransformerRegistry().put(createTransformerKey(def), transformer); 886 } 887 888 @Override 889 protected boolean removeRoute(String routeId, LoggingLevel loggingLevel) throws Exception { 890 // synchronize on model first to avoid deadlock with concurrent 'addRoutes' calls: 891 synchronized (model) { 892 synchronized (this) { 893 boolean removed = super.removeRoute(routeId, loggingLevel); 894 if (removed) { 895 // must also remove the route definition 896 RouteDefinition def = getRouteDefinition(routeId); 897 if (def != null) { 898 removeRouteDefinition(def); 899 } 900 } 901 return removed; 902 } 903 } 904 } 905 906 @Override 907 public boolean removeRoute(String routeId) throws Exception { 908 // synchronize on model first to avoid deadlock with concurrent 'addRoutes' calls: 909 synchronized (model) { 910 return super.removeRoute(routeId); 911 } 912 } 913 914 /** 915 * Indicates whether the route should be included according to the precondition. 916 * 917 * @param definition the definition of the route to check. 918 * @return {@code true} if the route should be included, {@code false} otherwise. 919 */ 920 private boolean includedRoute(RouteDefinition definition) { 921 return PreconditionHelper.included(definition, this); 922 } 923 924 private static ValueHolder<String> createTransformerKey(TransformerDefinition def) { 925 if (ObjectHelper.isNotEmpty(def.getScheme())) { 926 return ObjectHelper.isNotEmpty(def.getName()) 927 ? new TransformerKey(def.getScheme() + ":" + def.getName()) : new TransformerKey(def.getScheme()); 928 } 929 if (ObjectHelper.isNotEmpty(def.getName())) { 930 return new TransformerKey(def.getName()); 931 } else { 932 return new TransformerKey(new DataType(def.getFromType()), new DataType(def.getToType())); 933 } 934 } 935 936}