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.Collections; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.StringJoiner; 027import java.util.concurrent.ConcurrentHashMap; 028import java.util.function.Function; 029 030import org.apache.camel.CamelContext; 031import org.apache.camel.Exchange; 032import org.apache.camel.Expression; 033import org.apache.camel.FailedToCreateRouteFromTemplateException; 034import org.apache.camel.NoSuchBeanException; 035import org.apache.camel.RouteTemplateContext; 036import org.apache.camel.model.BeanFactoryDefinition; 037import org.apache.camel.model.DataFormatDefinition; 038import org.apache.camel.model.DefaultRouteTemplateContext; 039import org.apache.camel.model.FaultToleranceConfigurationDefinition; 040import org.apache.camel.model.FromDefinition; 041import org.apache.camel.model.Model; 042import org.apache.camel.model.ModelCamelContext; 043import org.apache.camel.model.ModelLifecycleStrategy; 044import org.apache.camel.model.ProcessorDefinition; 045import org.apache.camel.model.ProcessorDefinitionHelper; 046import org.apache.camel.model.Resilience4jConfigurationDefinition; 047import org.apache.camel.model.RouteConfigurationDefinition; 048import org.apache.camel.model.RouteDefinition; 049import org.apache.camel.model.RouteDefinitionHelper; 050import org.apache.camel.model.RouteFilters; 051import org.apache.camel.model.RouteTemplateBeanDefinition; 052import org.apache.camel.model.RouteTemplateDefinition; 053import org.apache.camel.model.RouteTemplateParameterDefinition; 054import org.apache.camel.model.RoutesDefinition; 055import org.apache.camel.model.TemplatedRouteBeanDefinition; 056import org.apache.camel.model.TemplatedRouteDefinition; 057import org.apache.camel.model.TemplatedRouteParameterDefinition; 058import org.apache.camel.model.ToDefinition; 059import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition; 060import org.apache.camel.model.rest.RestDefinition; 061import org.apache.camel.model.transformer.TransformerDefinition; 062import org.apache.camel.model.validator.ValidatorDefinition; 063import org.apache.camel.spi.ExchangeFactory; 064import org.apache.camel.spi.Language; 065import org.apache.camel.spi.ModelReifierFactory; 066import org.apache.camel.spi.NodeIdFactory; 067import org.apache.camel.spi.RouteTemplateLoaderListener; 068import org.apache.camel.spi.RouteTemplateParameterSource; 069import org.apache.camel.spi.ScriptingLanguage; 070import org.apache.camel.support.CamelContextHelper; 071import org.apache.camel.support.PatternHelper; 072import org.apache.camel.support.PropertyBindingSupport; 073import org.apache.camel.support.RouteTemplateHelper; 074import org.apache.camel.support.ScriptHelper; 075import org.apache.camel.util.AntPathMatcher; 076import org.apache.camel.util.ObjectHelper; 077import org.apache.camel.util.StringHelper; 078import org.apache.camel.util.function.Suppliers; 079 080public class DefaultModel implements Model { 081 082 private final CamelContext camelContext; 083 084 private ModelReifierFactory modelReifierFactory = new DefaultModelReifierFactory(); 085 private final List<ModelLifecycleStrategy> modelLifecycleStrategies = new ArrayList<>(); 086 private final List<RouteConfigurationDefinition> routesConfigurations = new ArrayList<>(); 087 private final List<RouteDefinition> routeDefinitions = new ArrayList<>(); 088 private final List<RouteTemplateDefinition> routeTemplateDefinitions = new ArrayList<>(); 089 private final List<RestDefinition> restDefinitions = new ArrayList<>(); 090 private final Map<String, RouteTemplateDefinition.Converter> routeTemplateConverters = new ConcurrentHashMap<>(); 091 private Map<String, DataFormatDefinition> dataFormats = new HashMap<>(); 092 private List<TransformerDefinition> transformers = new ArrayList<>(); 093 private List<ValidatorDefinition> validators = new ArrayList<>(); 094 private final Map<String, ServiceCallConfigurationDefinition> serviceCallConfigurations = new ConcurrentHashMap<>(); 095 private final Map<String, Resilience4jConfigurationDefinition> resilience4jConfigurations = new ConcurrentHashMap<>(); 096 private final Map<String, FaultToleranceConfigurationDefinition> faultToleranceConfigurations = new ConcurrentHashMap<>(); 097 private Function<RouteDefinition, Boolean> routeFilter; 098 099 public DefaultModel(CamelContext camelContext) { 100 this.camelContext = camelContext; 101 } 102 103 public CamelContext getCamelContext() { 104 return camelContext; 105 } 106 107 @Override 108 public void addModelLifecycleStrategy(ModelLifecycleStrategy modelLifecycleStrategy) { 109 // avoid adding double which can happen with spring xml on spring boot 110 if (!this.modelLifecycleStrategies.contains(modelLifecycleStrategy)) { 111 this.modelLifecycleStrategies.add(modelLifecycleStrategy); 112 } 113 } 114 115 @Override 116 public List<ModelLifecycleStrategy> getModelLifecycleStrategies() { 117 return modelLifecycleStrategies; 118 } 119 120 @Override 121 public void addRouteConfiguration(RouteConfigurationDefinition routesConfiguration) { 122 // Ensure that the route configuration should be included 123 if (routesConfiguration == null || !includedRouteConfiguration(routesConfiguration)) { 124 return; 125 } 126 // only add if not already exists (route-loader may let Java DSL add route configuration twice 127 // because it extends RouteBuilder as base class) 128 if (!this.routesConfigurations.contains(routesConfiguration)) { 129 // check that there is no id clash 130 if (routesConfiguration.getId() != null) { 131 boolean clash = this.routesConfigurations.stream() 132 .anyMatch(r -> ObjectHelper.equal(r.getId(), routesConfiguration.getId())); 133 if (clash) { 134 throw new IllegalArgumentException( 135 "Route configuration already exists with id: " + routesConfiguration.getId()); 136 } 137 } 138 this.routesConfigurations.add(routesConfiguration); 139 } 140 } 141 142 @Override 143 public void addRouteConfigurations(List<RouteConfigurationDefinition> routesConfigurations) { 144 if (routesConfigurations == null || routesConfigurations.isEmpty()) { 145 return; 146 } 147 // only add if not already exists (route-loader may let Java DSL add route configuration twice 148 // because it extends RouteBuilder as base class) 149 for (RouteConfigurationDefinition rc : routesConfigurations) { 150 addRouteConfiguration(rc); 151 } 152 } 153 154 @Override 155 public List<RouteConfigurationDefinition> getRouteConfigurationDefinitions() { 156 return routesConfigurations; 157 } 158 159 @Override 160 public synchronized RouteConfigurationDefinition getRouteConfigurationDefinition(String id) { 161 for (RouteConfigurationDefinition def : routesConfigurations) { 162 if (def.idOrCreate(camelContext.getCamelContextExtension().getContextPlugin(NodeIdFactory.class)).equals(id)) { 163 return def; 164 } 165 } 166 // you can have a global route configuration that has no ID assigned 167 return routesConfigurations.stream().filter(c -> c.getId() == null).findFirst().orElse(null); 168 } 169 170 @Override 171 public void removeRouteConfiguration(RouteConfigurationDefinition routeConfigurationDefinition) throws Exception { 172 RouteConfigurationDefinition toBeRemoved = getRouteConfigurationDefinition(routeConfigurationDefinition.getId()); 173 this.routesConfigurations.remove(toBeRemoved); 174 } 175 176 @Override 177 public synchronized void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception { 178 if (routeDefinitions == null || routeDefinitions.isEmpty()) { 179 return; 180 } 181 182 List<RouteDefinition> list; 183 if (routeFilter == null) { 184 list = new ArrayList<>(routeDefinitions); 185 } else { 186 list = new ArrayList<>(); 187 for (RouteDefinition r : routeDefinitions) { 188 if (routeFilter.apply(r)) { 189 list.add(r); 190 } 191 } 192 } 193 194 removeRouteDefinitions(list); 195 196 // special if rest-dsl is inlining routes 197 if (camelContext.getRestConfiguration().isInlineRoutes()) { 198 List<RouteDefinition> allRoutes = new ArrayList<>(); 199 allRoutes.addAll(list); 200 allRoutes.addAll(this.routeDefinitions); 201 202 List<RouteDefinition> toBeRemoved = new ArrayList<>(); 203 Map<String, RouteDefinition> directs = new HashMap<>(); 204 for (RouteDefinition r : allRoutes) { 205 // does the route start with direct, which is candidate for rest-dsl 206 FromDefinition from = r.getInput(); 207 if (from != null) { 208 String uri = from.getEndpointUri(); 209 if (uri != null && uri.startsWith("direct:")) { 210 directs.put(uri, r); 211 } 212 } 213 } 214 for (RouteDefinition r : allRoutes) { 215 // loop all rest routes 216 FromDefinition from = r.getInput(); 217 if (from != null) { 218 String uri = from.getEndpointUri(); 219 if (uri != null && uri.startsWith("rest:")) { 220 ProcessorDefinition<?> def = r.getOutputs().get(0); 221 if (def instanceof ToDefinition) { 222 ToDefinition to = (ToDefinition) def; 223 String toUri = to.getEndpointUri(); 224 RouteDefinition toBeInlined = directs.get(toUri); 225 if (toBeInlined != null) { 226 toBeRemoved.add(toBeInlined); 227 // inline by replacing the outputs 228 r.getOutputs().clear(); 229 r.getOutputs().addAll(toBeInlined.getOutputs()); 230 } 231 } 232 } 233 } 234 } 235 // remove all the routes that was inlined 236 list.removeAll(toBeRemoved); 237 this.routeDefinitions.removeAll(toBeRemoved); 238 } 239 240 for (RouteDefinition r : list) { 241 for (ModelLifecycleStrategy s : modelLifecycleStrategies) { 242 s.onAddRouteDefinition(r); 243 } 244 this.routeDefinitions.add(r); 245 } 246 247 if (shouldStartRoutes()) { 248 ((ModelCamelContext) getCamelContext()).startRouteDefinitions(list); 249 } 250 } 251 252 @Override 253 public void addRouteDefinition(RouteDefinition routeDefinition) throws Exception { 254 addRouteDefinitions(Collections.singletonList(routeDefinition)); 255 } 256 257 @Override 258 public synchronized void removeRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception { 259 for (RouteDefinition routeDefinition : routeDefinitions) { 260 removeRouteDefinition(routeDefinition); 261 } 262 } 263 264 @Override 265 public synchronized void removeRouteDefinition(RouteDefinition routeDefinition) throws Exception { 266 RouteDefinition toBeRemoved = routeDefinition; 267 String id = routeDefinition.getId(); 268 if (id != null) { 269 // remove existing route 270 camelContext.getRouteController().stopRoute(id); 271 camelContext.removeRoute(id); 272 toBeRemoved = getRouteDefinition(id); 273 } 274 for (ModelLifecycleStrategy s : modelLifecycleStrategies) { 275 s.onRemoveRouteDefinition(toBeRemoved); 276 } 277 this.routeDefinitions.remove(toBeRemoved); 278 } 279 280 @Override 281 public synchronized void removeRouteTemplateDefinitions(String pattern) throws Exception { 282 for (RouteTemplateDefinition def : new ArrayList<>(routeTemplateDefinitions)) { 283 if (PatternHelper.matchPattern(def.getId(), pattern)) { 284 removeRouteTemplateDefinition(def); 285 } 286 } 287 } 288 289 @Override 290 public synchronized List<RouteDefinition> getRouteDefinitions() { 291 return routeDefinitions; 292 } 293 294 @Override 295 public synchronized RouteDefinition getRouteDefinition(String id) { 296 for (RouteDefinition route : routeDefinitions) { 297 if (route.idOrCreate(camelContext.getCamelContextExtension().getContextPlugin(NodeIdFactory.class)).equals(id)) { 298 return route; 299 } 300 } 301 return null; 302 } 303 304 @Override 305 public List<RouteTemplateDefinition> getRouteTemplateDefinitions() { 306 return routeTemplateDefinitions; 307 } 308 309 @Override 310 public RouteTemplateDefinition getRouteTemplateDefinition(String id) { 311 for (RouteTemplateDefinition route : routeTemplateDefinitions) { 312 if (route.idOrCreate(camelContext.getCamelContextExtension().getContextPlugin(NodeIdFactory.class)).equals(id)) { 313 return route; 314 } 315 } 316 return null; 317 } 318 319 @Override 320 public void addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception { 321 if (routeTemplateDefinitions == null || routeTemplateDefinitions.isEmpty()) { 322 return; 323 } 324 325 for (RouteTemplateDefinition r : routeTemplateDefinitions) { 326 for (ModelLifecycleStrategy s : modelLifecycleStrategies) { 327 s.onAddRouteTemplateDefinition(r); 328 } 329 this.routeTemplateDefinitions.add(r); 330 } 331 } 332 333 @Override 334 public void addRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception { 335 addRouteTemplateDefinitions(Collections.singletonList(routeTemplateDefinition)); 336 } 337 338 @Override 339 public void removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception { 340 for (RouteTemplateDefinition r : routeTemplateDefinitions) { 341 removeRouteTemplateDefinition(r); 342 } 343 } 344 345 @Override 346 public void removeRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception { 347 for (ModelLifecycleStrategy s : modelLifecycleStrategies) { 348 s.onRemoveRouteTemplateDefinition(routeTemplateDefinition); 349 } 350 routeTemplateDefinitions.remove(routeTemplateDefinition); 351 } 352 353 @Override 354 public void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter) { 355 routeTemplateConverters.put(templateIdPattern, converter); 356 } 357 358 @Override 359 @Deprecated 360 public String addRouteFromTemplate(final String routeId, final String routeTemplateId, final Map<String, Object> parameters) 361 throws Exception { 362 RouteTemplateContext rtc = new DefaultRouteTemplateContext(camelContext); 363 if (parameters != null) { 364 parameters.forEach(rtc::setParameter); 365 } 366 return addRouteFromTemplate(routeId, routeTemplateId, null, rtc); 367 } 368 369 @Override 370 public String addRouteFromTemplate(String routeId, String routeTemplateId, String prefixId, Map<String, Object> parameters) 371 throws Exception { 372 RouteTemplateContext rtc = new DefaultRouteTemplateContext(camelContext); 373 if (parameters != null) { 374 parameters.forEach(rtc::setParameter); 375 } 376 return addRouteFromTemplate(routeId, routeTemplateId, prefixId, rtc); 377 } 378 379 public String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext) 380 throws Exception { 381 return addRouteFromTemplate(routeId, routeTemplateId, null, routeTemplateContext); 382 } 383 384 @Override 385 public String addRouteFromTemplate( 386 String routeId, String routeTemplateId, String prefixId, 387 RouteTemplateContext routeTemplateContext) 388 throws Exception { 389 390 RouteTemplateDefinition target = null; 391 for (RouteTemplateDefinition def : routeTemplateDefinitions) { 392 if (routeTemplateId.equals(def.getId())) { 393 target = def; 394 break; 395 } 396 } 397 if (target == null) { 398 // if the route template has a location parameter, then try to load route templates from the location 399 // and look up again 400 Object location = routeTemplateContext.getParameters().get(RouteTemplateParameterSource.LOCATION); 401 if (location != null) { 402 RouteTemplateLoaderListener listener 403 = CamelContextHelper.findSingleByType(getCamelContext(), RouteTemplateLoaderListener.class); 404 RouteTemplateHelper.loadRouteTemplateFromLocation(getCamelContext(), listener, routeTemplateId, 405 location.toString()); 406 } 407 for (RouteTemplateDefinition def : routeTemplateDefinitions) { 408 if (routeTemplateId.equals(def.getId())) { 409 target = def; 410 break; 411 } 412 } 413 } 414 if (target == null) { 415 throw new IllegalArgumentException("Cannot find RouteTemplate with id " + routeTemplateId); 416 } 417 418 // support both camelCase and kebab-case keys 419 final Map<String, Object> prop = new HashMap<>(); 420 final Map<String, Object> propDefaultValues = new HashMap<>(); 421 // include default values first from the template (and validate that we have inputs for all required parameters) 422 if (target.getTemplateParameters() != null) { 423 StringJoiner templatesBuilder = new StringJoiner(", "); 424 425 for (RouteTemplateParameterDefinition temp : target.getTemplateParameters()) { 426 if (temp.getDefaultValue() != null) { 427 addProperty(prop, temp.getName(), temp.getDefaultValue()); 428 addProperty(propDefaultValues, temp.getName(), temp.getDefaultValue()); 429 } else { 430 if (temp.isRequired() && !routeTemplateContext.hasParameter(temp.getName())) { 431 // this is a required parameter which is missing 432 templatesBuilder.add(temp.getName()); 433 } 434 } 435 } 436 if (templatesBuilder.length() > 0) { 437 throw new IllegalArgumentException( 438 "Route template " + routeTemplateId + " the following mandatory parameters must be provided: " 439 + templatesBuilder); 440 } 441 } 442 443 // then override with user parameters part 1 444 if (routeTemplateContext.getParameters() != null) { 445 routeTemplateContext.getParameters().forEach((k, v) -> addProperty(prop, k, v)); 446 } 447 // route template context should include default template parameters from the target route template 448 // so it has all parameters available 449 if (target.getTemplateParameters() != null) { 450 for (RouteTemplateParameterDefinition temp : target.getTemplateParameters()) { 451 if (!routeTemplateContext.hasParameter(temp.getName()) && temp.getDefaultValue() != null) { 452 routeTemplateContext.setParameter(temp.getName(), temp.getDefaultValue()); 453 } 454 } 455 } 456 457 RouteTemplateDefinition.Converter converter = RouteTemplateDefinition.Converter.DEFAULT_CONVERTER; 458 459 for (Map.Entry<String, RouteTemplateDefinition.Converter> entry : routeTemplateConverters.entrySet()) { 460 final String key = entry.getKey(); 461 final String templateId = target.getId(); 462 463 if ("*".equals(key) || templateId.equals(key)) { 464 converter = entry.getValue(); 465 break; 466 } else if (AntPathMatcher.INSTANCE.match(key, templateId)) { 467 converter = entry.getValue(); 468 break; 469 } else if (templateId.matches(key)) { 470 converter = entry.getValue(); 471 break; 472 } 473 } 474 475 RouteDefinition def = converter.apply(target, prop); 476 if (routeId != null) { 477 def.setId(routeId); 478 } 479 if (prefixId != null) { 480 def.setNodePrefixId(prefixId); 481 } 482 def.setTemplateParameters(prop); 483 def.setTemplateDefaultParameters(propDefaultValues); 484 def.setRouteTemplateContext(routeTemplateContext); 485 486 // setup local beans 487 if (target.getTemplateBeans() != null) { 488 addTemplateBeans(routeTemplateContext, target); 489 } 490 491 if (target.getConfigurer() != null) { 492 routeTemplateContext.setConfigurer(target.getConfigurer()); 493 } 494 495 // assign ids to the routes and validate that the id's are all unique 496 String duplicate = RouteDefinitionHelper.validateUniqueIds(def, routeDefinitions, prefixId); 497 if (duplicate != null) { 498 throw new FailedToCreateRouteFromTemplateException( 499 routeId, routeTemplateId, 500 "duplicate id detected: " + duplicate + ". Please correct ids to be unique among all your routes."); 501 } 502 503 // must use route collection to prepare the created route to 504 // ensure its created correctly from the route template 505 RoutesDefinition routeCollection = new RoutesDefinition(); 506 routeCollection.setCamelContext(camelContext); 507 routeCollection.setRoutes(getRouteDefinitions()); 508 routeCollection.prepareRoute(def); 509 510 // add route and return the id it was assigned 511 addRouteDefinition(def); 512 return def.getId(); 513 } 514 515 private static void addProperty(Map<String, Object> prop, String key, Object value) { 516 prop.put(key, value); 517 // support also camelCase and kebab-case because route templates (kamelets) 518 // can be defined using different key styles 519 key = StringHelper.dashToCamelCase(key); 520 prop.put(key, value); 521 key = StringHelper.camelCaseToDash(key); 522 prop.put(key, value); 523 } 524 525 private static void addTemplateBeans(RouteTemplateContext routeTemplateContext, RouteTemplateDefinition target) 526 throws Exception { 527 for (RouteTemplateBeanDefinition b : target.getTemplateBeans()) { 528 bind(b, routeTemplateContext); 529 } 530 } 531 532 /** 533 * Binds the bean factory to the repository (if possible). 534 * 535 * @param beanFactory the bean factory to bind. 536 * @param routeTemplateContext the context into which the bean factory should be bound. 537 * @throws Exception if an error occurs while trying to bind the bean factory 538 */ 539 private static void bind(BeanFactoryDefinition<?, ?> beanFactory, RouteTemplateContext routeTemplateContext) 540 throws Exception { 541 final Map<String, Object> props = new HashMap<>(); 542 if (beanFactory.getProperties() != null) { 543 props.putAll(beanFactory.getProperties()); 544 } 545 if (beanFactory.getPropertyDefinitions() != null) { 546 beanFactory.getPropertyDefinitions().forEach(p -> props.put(p.getKey(), p.getValue())); 547 } 548 if (beanFactory.getBeanSupplier() != null) { 549 if (props.isEmpty()) { 550 // bean class is optional for supplier 551 if (beanFactory.getBeanClass() != null) { 552 routeTemplateContext.bind(beanFactory.getName(), beanFactory.getBeanClass(), beanFactory.getBeanSupplier()); 553 } else { 554 routeTemplateContext.bind(beanFactory.getName(), beanFactory.getBeanSupplier()); 555 } 556 } 557 } else if (beanFactory.getScript() != null) { 558 final String script = beanFactory.getScript(); 559 final CamelContext camelContext = routeTemplateContext.getCamelContext(); 560 final Language lan = camelContext.resolveLanguage(beanFactory.getType()); 561 final Class<?> clazz; 562 if (beanFactory.getBeanType() != null) { 563 clazz = camelContext.getClassResolver().resolveMandatoryClass(beanFactory.getBeanType()); 564 } else { 565 if (beanFactory.getBeanClass() != null) { 566 clazz = beanFactory.getBeanClass(); 567 } else { 568 clazz = Object.class; 569 } 570 } 571 final ScriptingLanguage slan = lan instanceof ScriptingLanguage ? (ScriptingLanguage) lan : null; 572 if (slan != null) { 573 // scripting language should be evaluated with route template context as binding 574 // and memorize so the script is only evaluated once and the local bean is the same 575 // if a route template refers to the local bean multiple times 576 routeTemplateContext.bind(beanFactory.getName(), clazz, Suppliers.memorize(() -> { 577 Map<String, Object> bindings = new HashMap<>(); 578 // use rtx as the short-hand name, as context would imply its CamelContext 579 bindings.put("rtc", routeTemplateContext); 580 Object local = slan.evaluate(script, bindings, clazz); 581 if (!props.isEmpty()) { 582 PropertyBindingSupport.setPropertiesOnTarget(camelContext, local, props); 583 } 584 return local; 585 })); 586 } else { 587 // exchange based languages needs a dummy exchange to be evaluated 588 // and memorize so the script is only evaluated once and the local bean is the same 589 // if a route template refers to the local bean multiple times 590 routeTemplateContext.bind(beanFactory.getName(), clazz, Suppliers.memorize(() -> { 591 ExchangeFactory ef = camelContext.getCamelContextExtension().getExchangeFactory(); 592 Exchange dummy = ef.create(false); 593 try { 594 String text = ScriptHelper.resolveOptionalExternalScript(camelContext, dummy, script); 595 if (text != null) { 596 Expression exp = lan.createExpression(text); 597 Object local = exp.evaluate(dummy, clazz); 598 if (!props.isEmpty()) { 599 PropertyBindingSupport.setPropertiesOnTarget(camelContext, local, props); 600 } 601 return local; 602 } else { 603 return null; 604 } 605 } finally { 606 ef.release(dummy); 607 } 608 })); 609 } 610 } else if (beanFactory.getBeanClass() != null 611 || beanFactory.getType() != null && beanFactory.getType().startsWith("#class:")) { 612 // if there is a factory method then the class/bean should be created in a different way 613 String className = null; 614 String factoryMethod = null; 615 String parameters = null; 616 if (beanFactory.getType() != null) { 617 className = beanFactory.getType().substring(7); 618 if (className.endsWith(")") && className.indexOf('(') != -1) { 619 parameters = StringHelper.after(className, "("); 620 parameters = parameters.substring(0, parameters.length() - 1); // clip last ) 621 className = StringHelper.before(className, "("); 622 } 623 if (className != null && className.indexOf('#') != -1) { 624 factoryMethod = StringHelper.after(className, "#"); 625 className = StringHelper.before(className, "#"); 626 } 627 } 628 if (className != null && (factoryMethod != null || parameters != null)) { 629 final CamelContext camelContext = routeTemplateContext.getCamelContext(); 630 final Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(className); 631 final String fqn = className; 632 final String fm = factoryMethod; 633 final String fp = parameters; 634 routeTemplateContext.bind(beanFactory.getName(), Object.class, Suppliers.memorize(() -> { 635 // resolve placeholders in parameters 636 String params = camelContext.resolvePropertyPlaceholders(fp); 637 try { 638 Object local; 639 if (fm != null) { 640 if (fp != null) { 641 // special to support factory method parameters 642 local = PropertyBindingSupport.newInstanceFactoryParameters(camelContext, clazz, fm, params); 643 } else { 644 local = camelContext.getInjector().newInstance(clazz, fm); 645 } 646 if (local == null) { 647 throw new IllegalStateException( 648 "Cannot create bean instance using factory method: " + fqn + "#" + fm); 649 } 650 } else { 651 // special to support constructor parameters 652 local = PropertyBindingSupport.newInstanceConstructorParameters(camelContext, clazz, params); 653 } 654 if (!props.isEmpty()) { 655 PropertyBindingSupport.setPropertiesOnTarget(camelContext, local, props); 656 } 657 return local; 658 } catch (Exception e) { 659 throw new IllegalStateException( 660 "Cannot create bean: " + beanFactory.getType()); 661 } 662 })); 663 } else { 664 final CamelContext camelContext = routeTemplateContext.getCamelContext(); 665 Class<?> clazz = beanFactory.getBeanClass() != null 666 ? beanFactory.getBeanClass() : camelContext.getClassResolver().resolveMandatoryClass(className); 667 // we only have the bean class so we use that to create a new bean via the injector 668 // and memorize so the bean is only created once and the local bean is the same 669 // if a route template refers to the local bean multiple times 670 routeTemplateContext.bind(beanFactory.getName(), clazz, 671 Suppliers.memorize(() -> { 672 Object local = camelContext.getInjector().newInstance(clazz); 673 if (!props.isEmpty()) { 674 PropertyBindingSupport.setPropertiesOnTarget(camelContext, local, props); 675 } 676 return local; 677 })); 678 } 679 } else if (beanFactory.getType() != null && beanFactory.getType().startsWith("#type:")) { 680 final CamelContext camelContext = routeTemplateContext.getCamelContext(); 681 Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(beanFactory.getType().substring(6)); 682 Set<?> found = camelContext.getRegistry().findByType(clazz); 683 if (found == null || found.isEmpty()) { 684 throw new NoSuchBeanException(null, clazz.getName()); 685 } else if (found.size() > 1) { 686 throw new NoSuchBeanException( 687 "Found " + found.size() + " beans of type: " + clazz + ". Only one bean expected."); 688 } else { 689 // do not set properties when using #type as it uses an existing shared bean 690 routeTemplateContext.bind(beanFactory.getName(), clazz, found.iterator().next()); 691 } 692 } else { 693 // invalid syntax for the local bean, so lets report an exception 694 throw new IllegalArgumentException( 695 "Route template local bean: " + beanFactory.getName() + " has invalid type syntax: " + beanFactory.getType() 696 + ". To refer to a class then prefix the value with #class such as: #class:fullyQualifiedClassName"); 697 } 698 } 699 700 @Override 701 public void addRouteFromTemplatedRoute(TemplatedRouteDefinition templatedRouteDefinition) 702 throws Exception { 703 ObjectHelper.notNull(templatedRouteDefinition, "templatedRouteDefinition"); 704 705 final RouteTemplateContext routeTemplateContext = new DefaultRouteTemplateContext(camelContext); 706 // Load the parameters into the context 707 final List<TemplatedRouteParameterDefinition> parameters = templatedRouteDefinition.getParameters(); 708 if (parameters != null) { 709 for (TemplatedRouteParameterDefinition parameterDefinition : parameters) { 710 routeTemplateContext.setParameter(parameterDefinition.getName(), parameterDefinition.getValue()); 711 } 712 } 713 // Bind the beans into the context 714 final List<TemplatedRouteBeanDefinition> beans = templatedRouteDefinition.getBeans(); 715 if (beans != null) { 716 for (TemplatedRouteBeanDefinition beanDefinition : beans) { 717 bind(beanDefinition, routeTemplateContext); 718 } 719 } 720 // Add the route 721 addRouteFromTemplate(templatedRouteDefinition.getRouteId(), templatedRouteDefinition.getRouteTemplateRef(), 722 templatedRouteDefinition.getPrefixId(), routeTemplateContext); 723 } 724 725 @Override 726 public synchronized List<RestDefinition> getRestDefinitions() { 727 return restDefinitions; 728 } 729 730 @Override 731 public synchronized void addRestDefinitions(Collection<RestDefinition> restDefinitions, boolean addToRoutes) 732 throws Exception { 733 if (restDefinitions == null || restDefinitions.isEmpty()) { 734 return; 735 } 736 737 this.restDefinitions.addAll(restDefinitions); 738 if (addToRoutes) { 739 // rests are also routes so need to add them there too 740 for (final RestDefinition restDefinition : restDefinitions) { 741 List<RouteDefinition> routeDefinitions = restDefinition.asRouteDefinition(camelContext); 742 addRouteDefinitions(routeDefinitions); 743 } 744 } 745 } 746 747 @Override 748 public ServiceCallConfigurationDefinition getServiceCallConfiguration(String serviceName) { 749 if (serviceName == null) { 750 serviceName = ""; 751 } 752 753 return serviceCallConfigurations.get(serviceName); 754 } 755 756 @Override 757 public void setServiceCallConfiguration(ServiceCallConfigurationDefinition configuration) { 758 serviceCallConfigurations.put("", configuration); 759 } 760 761 @Override 762 public void setServiceCallConfigurations(List<ServiceCallConfigurationDefinition> configurations) { 763 if (configurations != null) { 764 for (ServiceCallConfigurationDefinition configuration : configurations) { 765 serviceCallConfigurations.put(configuration.getId(), configuration); 766 } 767 } 768 } 769 770 @Override 771 public void addServiceCallConfiguration(String serviceName, ServiceCallConfigurationDefinition configuration) { 772 serviceCallConfigurations.put(serviceName, configuration); 773 } 774 775 @Override 776 public Resilience4jConfigurationDefinition getResilience4jConfiguration(String id) { 777 if (id == null) { 778 id = ""; 779 } 780 781 return resilience4jConfigurations.get(id); 782 } 783 784 @Override 785 public void setResilience4jConfiguration(Resilience4jConfigurationDefinition configuration) { 786 resilience4jConfigurations.put("", configuration); 787 } 788 789 @Override 790 public void setResilience4jConfigurations(List<Resilience4jConfigurationDefinition> configurations) { 791 if (configurations != null) { 792 for (Resilience4jConfigurationDefinition configuration : configurations) { 793 resilience4jConfigurations.put(configuration.getId(), configuration); 794 } 795 } 796 } 797 798 @Override 799 public void addResilience4jConfiguration(String id, Resilience4jConfigurationDefinition configuration) { 800 resilience4jConfigurations.put(id, configuration); 801 } 802 803 @Override 804 public FaultToleranceConfigurationDefinition getFaultToleranceConfiguration(String id) { 805 if (id == null) { 806 id = ""; 807 } 808 809 return faultToleranceConfigurations.get(id); 810 } 811 812 @Override 813 public void setFaultToleranceConfiguration(FaultToleranceConfigurationDefinition configuration) { 814 faultToleranceConfigurations.put("", configuration); 815 } 816 817 @Override 818 public void setFaultToleranceConfigurations(List<FaultToleranceConfigurationDefinition> configurations) { 819 if (configurations != null) { 820 for (FaultToleranceConfigurationDefinition configuration : configurations) { 821 faultToleranceConfigurations.put(configuration.getId(), configuration); 822 } 823 } 824 } 825 826 @Override 827 public void addFaultToleranceConfiguration(String id, FaultToleranceConfigurationDefinition configuration) { 828 faultToleranceConfigurations.put(id, configuration); 829 } 830 831 @Override 832 public DataFormatDefinition resolveDataFormatDefinition(String name) { 833 // lookup type and create the data format from it 834 DataFormatDefinition type = lookup(camelContext, name, DataFormatDefinition.class); 835 if (type == null && getDataFormats() != null) { 836 type = getDataFormats().get(name); 837 } 838 return type; 839 } 840 841 @SuppressWarnings("rawtypes") 842 @Override 843 public ProcessorDefinition<?> getProcessorDefinition(String id) { 844 for (RouteDefinition route : getRouteDefinitions()) { 845 Collection<ProcessorDefinition> col 846 = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class); 847 for (ProcessorDefinition proc : col) { 848 if (id.equals(proc.getId())) { 849 return proc; 850 } 851 } 852 } 853 return null; 854 } 855 856 @Override 857 public <T extends ProcessorDefinition<T>> T getProcessorDefinition(String id, Class<T> type) { 858 ProcessorDefinition<?> answer = getProcessorDefinition(id); 859 if (answer != null) { 860 return type.cast(answer); 861 } 862 return null; 863 } 864 865 @Override 866 public Map<String, DataFormatDefinition> getDataFormats() { 867 return dataFormats; 868 } 869 870 @Override 871 public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) { 872 this.dataFormats = dataFormats; 873 } 874 875 @Override 876 public List<TransformerDefinition> getTransformers() { 877 return transformers; 878 } 879 880 @Override 881 public void setTransformers(List<TransformerDefinition> transformers) { 882 this.transformers = transformers; 883 } 884 885 @Override 886 public List<ValidatorDefinition> getValidators() { 887 return validators; 888 } 889 890 @Override 891 public void setValidators(List<ValidatorDefinition> validators) { 892 this.validators = validators; 893 } 894 895 @Override 896 public void setRouteFilterPattern(String include, String exclude) { 897 setRouteFilter(RouteFilters.filterByPattern(include, exclude)); 898 } 899 900 @Override 901 public Function<RouteDefinition, Boolean> getRouteFilter() { 902 return routeFilter; 903 } 904 905 @Override 906 public void setRouteFilter(Function<RouteDefinition, Boolean> routeFilter) { 907 this.routeFilter = routeFilter; 908 } 909 910 @Override 911 public ModelReifierFactory getModelReifierFactory() { 912 return modelReifierFactory; 913 } 914 915 @Override 916 public void setModelReifierFactory(ModelReifierFactory modelReifierFactory) { 917 this.modelReifierFactory = modelReifierFactory; 918 } 919 920 /** 921 * Should we start newly added routes? 922 */ 923 protected boolean shouldStartRoutes() { 924 return camelContext.isStarted() && !camelContext.isStarting(); 925 } 926 927 private static <T> T lookup(CamelContext context, String ref, Class<T> type) { 928 try { 929 return context.getRegistry().lookupByNameAndType(ref, type); 930 } catch (Exception e) { 931 // need to ignore not same type and return it as null 932 return null; 933 } 934 } 935 936 /** 937 * Indicates whether the route configuration should be included according to the precondition. 938 * 939 * @param definition the definition of the route configuration to check. 940 * @return {@code true} if the route configuration should be included, {@code false} otherwise. 941 */ 942 private boolean includedRouteConfiguration(RouteConfigurationDefinition definition) { 943 return PreconditionHelper.included(definition, camelContext); 944 } 945}