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