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.FailedToCreateRouteFromTemplateException; 035import org.apache.camel.NoSuchBeanException; 036import org.apache.camel.PropertyBindingException; 037import org.apache.camel.RouteTemplateContext; 038import org.apache.camel.model.BeanFactoryDefinition; 039import org.apache.camel.model.DataFormatDefinition; 040import org.apache.camel.model.DefaultRouteTemplateContext; 041import org.apache.camel.model.FaultToleranceConfigurationDefinition; 042import org.apache.camel.model.FromDefinition; 043import org.apache.camel.model.Model; 044import org.apache.camel.model.ModelCamelContext; 045import org.apache.camel.model.ModelLifecycleStrategy; 046import org.apache.camel.model.ProcessorDefinition; 047import org.apache.camel.model.ProcessorDefinitionHelper; 048import org.apache.camel.model.Resilience4jConfigurationDefinition; 049import org.apache.camel.model.RouteConfigurationDefinition; 050import org.apache.camel.model.RouteDefinition; 051import org.apache.camel.model.RouteDefinitionHelper; 052import org.apache.camel.model.RouteFilters; 053import org.apache.camel.model.RouteTemplateBeanDefinition; 054import org.apache.camel.model.RouteTemplateDefinition; 055import org.apache.camel.model.RouteTemplateParameterDefinition; 056import org.apache.camel.model.RoutesDefinition; 057import org.apache.camel.model.TemplatedRouteBeanDefinition; 058import org.apache.camel.model.TemplatedRouteDefinition; 059import org.apache.camel.model.TemplatedRouteParameterDefinition; 060import org.apache.camel.model.ToDefinition; 061import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition; 062import org.apache.camel.model.rest.RestDefinition; 063import org.apache.camel.model.transformer.TransformerDefinition; 064import org.apache.camel.model.validator.ValidatorDefinition; 065import org.apache.camel.spi.ExchangeFactory; 066import org.apache.camel.spi.Language; 067import org.apache.camel.spi.ModelReifierFactory; 068import org.apache.camel.spi.PropertyConfigurer; 069import org.apache.camel.spi.RouteTemplateLoaderListener; 070import org.apache.camel.spi.RouteTemplateParameterSource; 071import org.apache.camel.spi.ScriptingLanguage; 072import org.apache.camel.support.CamelContextHelper; 073import org.apache.camel.support.PatternHelper; 074import org.apache.camel.support.PropertyBindingSupport; 075import org.apache.camel.support.RouteTemplateHelper; 076import org.apache.camel.support.ScriptHelper; 077import org.apache.camel.support.service.ServiceHelper; 078import org.apache.camel.util.AntPathMatcher; 079import org.apache.camel.util.ObjectHelper; 080import org.apache.camel.util.StringHelper; 081import org.apache.camel.util.function.Suppliers; 082 083public class DefaultModel implements Model { 084 085 private final CamelContext camelContext; 086 087 private ModelReifierFactory modelReifierFactory = new DefaultModelReifierFactory(); 088 private final List<ModelLifecycleStrategy> modelLifecycleStrategies = new ArrayList<>(); 089 private final List<RouteConfigurationDefinition> routesConfigurations = new ArrayList<>(); 090 private final List<RouteDefinition> routeDefinitions = new ArrayList<>(); 091 private final List<RouteTemplateDefinition> routeTemplateDefinitions = new ArrayList<>(); 092 private final List<RestDefinition> restDefinitions = new ArrayList<>(); 093 private final Map<String, RouteTemplateDefinition.Converter> routeTemplateConverters = new ConcurrentHashMap<>(); 094 private Map<String, DataFormatDefinition> dataFormats = new HashMap<>(); 095 private List<TransformerDefinition> transformers = new ArrayList<>(); 096 private List<ValidatorDefinition> validators = new ArrayList<>(); 097 private final Map<String, ServiceCallConfigurationDefinition> serviceCallConfigurations = new ConcurrentHashMap<>(); 098 private final Map<String, Resilience4jConfigurationDefinition> resilience4jConfigurations = new ConcurrentHashMap<>(); 099 private final Map<String, FaultToleranceConfigurationDefinition> faultToleranceConfigurations = new ConcurrentHashMap<>(); 100 private Function<RouteDefinition, Boolean> routeFilter; 101 102 public DefaultModel(CamelContext camelContext) { 103 this.camelContext = camelContext; 104 } 105 106 public CamelContext getCamelContext() { 107 return camelContext; 108 } 109 110 @Override 111 public void addModelLifecycleStrategy(ModelLifecycleStrategy modelLifecycleStrategy) { 112 // avoid adding double which can happen with spring xml on spring boot 113 if (!this.modelLifecycleStrategies.contains(modelLifecycleStrategy)) { 114 this.modelLifecycleStrategies.add(modelLifecycleStrategy); 115 } 116 } 117 118 @Override 119 public List<ModelLifecycleStrategy> getModelLifecycleStrategies() { 120 return modelLifecycleStrategies; 121 } 122 123 @Override 124 public void addRouteConfiguration(RouteConfigurationDefinition routesConfiguration) { 125 // Ensure that the route configuration should be included 126 if (routesConfiguration == null || !includedRouteConfiguration(routesConfiguration)) { 127 return; 128 } 129 // only add if not already exists (route-loader may let Java DSL add route configuration twice 130 // because it extends RouteBuilder as base class) 131 if (!this.routesConfigurations.contains(routesConfiguration)) { 132 // check that there is no id clash 133 if (routesConfiguration.getId() != null) { 134 boolean clash = this.routesConfigurations.stream() 135 .anyMatch(r -> ObjectHelper.equal(r.getId(), routesConfiguration.getId())); 136 if (clash) { 137 throw new IllegalArgumentException( 138 "Route configuration already exists with id: " + routesConfiguration.getId()); 139 } 140 } 141 this.routesConfigurations.add(routesConfiguration); 142 } 143 } 144 145 @Override 146 public void addRouteConfigurations(List<RouteConfigurationDefinition> routesConfigurations) { 147 if (routesConfigurations == null || routesConfigurations.isEmpty()) { 148 return; 149 } 150 // only add if not already exists (route-loader may let Java DSL add route configuration twice 151 // because it extends RouteBuilder as base class) 152 for (RouteConfigurationDefinition rc : routesConfigurations) { 153 addRouteConfiguration(rc); 154 } 155 } 156 157 @Override 158 public List<RouteConfigurationDefinition> getRouteConfigurationDefinitions() { 159 return routesConfigurations; 160 } 161 162 @Override 163 public synchronized RouteConfigurationDefinition getRouteConfigurationDefinition(String id) { 164 for (RouteConfigurationDefinition def : routesConfigurations) { 165 if (def.idOrCreate(camelContext.getCamelContextExtension().getNodeIdFactory()).equals(id)) { 166 return def; 167 } 168 } 169 // you can have a global route configuration that has no ID assigned 170 return routesConfigurations.stream().filter(c -> c.getId() == null).findFirst().orElse(null); 171 } 172 173 @Override 174 public void removeRouteConfiguration(RouteConfigurationDefinition routeConfigurationDefinition) throws Exception { 175 RouteConfigurationDefinition toBeRemoved = getRouteConfigurationDefinition(routeConfigurationDefinition.getId()); 176 this.routesConfigurations.remove(toBeRemoved); 177 } 178 179 @Override 180 public synchronized void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception { 181 if (routeDefinitions == null || routeDefinitions.isEmpty()) { 182 return; 183 } 184 185 List<RouteDefinition> list; 186 if (routeFilter == null) { 187 list = new ArrayList<>(routeDefinitions); 188 } else { 189 list = new ArrayList<>(); 190 for (RouteDefinition r : routeDefinitions) { 191 if (routeFilter.apply(r)) { 192 list.add(r); 193 } 194 } 195 } 196 197 removeRouteDefinitions(list); 198 199 // special if rest-dsl is inlining routes 200 if (camelContext.getRestConfiguration().isInlineRoutes()) { 201 List<RouteDefinition> allRoutes = new ArrayList<>(); 202 allRoutes.addAll(list); 203 allRoutes.addAll(this.routeDefinitions); 204 205 List<RouteDefinition> toBeRemoved = new ArrayList<>(); 206 Map<String, RouteDefinition> directs = new HashMap<>(); 207 for (RouteDefinition r : allRoutes) { 208 // does the route start with direct, which is candidate for rest-dsl 209 FromDefinition from = r.getInput(); 210 if (from != null) { 211 String uri = from.getEndpointUri(); 212 if (uri != null && uri.startsWith("direct:")) { 213 directs.put(uri, r); 214 } 215 } 216 } 217 for (RouteDefinition r : allRoutes) { 218 // loop all rest routes 219 FromDefinition from = r.getInput(); 220 if (from != null) { 221 String uri = from.getEndpointUri(); 222 if (uri != null && uri.startsWith("rest:")) { 223 ToDefinition to = (ToDefinition) r.getOutputs().get(0); 224 String toUri = to.getEndpointUri(); 225 RouteDefinition toBeInlined = directs.get(toUri); 226 if (toBeInlined != null) { 227 toBeRemoved.add(toBeInlined); 228 // inline by replacing the outputs 229 r.getOutputs().clear(); 230 r.getOutputs().addAll(toBeInlined.getOutputs()); 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().getNodeIdFactory()).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().getNodeIdFactory()).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 beanFactory.getProperties().forEach(p -> props.put(p.getKey(), p.getValue())); 544 } 545 if (beanFactory.getBeanSupplier() != null) { 546 if (props.isEmpty()) { 547 // bean class is optional for supplier 548 if (beanFactory.getBeanClass() != null) { 549 routeTemplateContext.bind(beanFactory.getName(), beanFactory.getBeanClass(), beanFactory.getBeanSupplier()); 550 } else { 551 routeTemplateContext.bind(beanFactory.getName(), beanFactory.getBeanSupplier()); 552 } 553 } 554 } else if (beanFactory.getScript() != null) { 555 final String script = beanFactory.getScript(); 556 final CamelContext camelContext = routeTemplateContext.getCamelContext(); 557 final Language lan = camelContext.resolveLanguage(beanFactory.getType()); 558 final Class<?> clazz; 559 if (beanFactory.getBeanType() != null) { 560 clazz = camelContext.getClassResolver().resolveMandatoryClass(beanFactory.getBeanType()); 561 } else { 562 if (beanFactory.getBeanClass() != null) { 563 clazz = beanFactory.getBeanClass(); 564 } else { 565 clazz = Object.class; 566 } 567 } 568 final ScriptingLanguage slan = lan instanceof ScriptingLanguage ? (ScriptingLanguage) lan : null; 569 if (slan != null) { 570 // scripting language should be evaluated with route template context as binding 571 // and memorize so the script is only evaluated once and the local bean is the same 572 // if a route template refers to the local bean multiple times 573 routeTemplateContext.bind(beanFactory.getName(), clazz, Suppliers.memorize(() -> { 574 Map<String, Object> bindings = new HashMap<>(); 575 // use rtx as the short-hand name, as context would imply its CamelContext 576 bindings.put("rtc", routeTemplateContext); 577 Object local = slan.evaluate(script, bindings, clazz); 578 if (!props.isEmpty()) { 579 setPropertiesOnTarget(camelContext, local, props); 580 } 581 return local; 582 })); 583 } else { 584 // exchange based languages needs a dummy exchange to be evaluated 585 // and memorize so the script is only evaluated once and the local bean is the same 586 // if a route template refers to the local bean multiple times 587 routeTemplateContext.bind(beanFactory.getName(), clazz, Suppliers.memorize(() -> { 588 ExchangeFactory ef = camelContext.getCamelContextExtension().getExchangeFactory(); 589 Exchange dummy = ef.create(false); 590 try { 591 String text = ScriptHelper.resolveOptionalExternalScript(camelContext, dummy, script); 592 if (text != null) { 593 Expression exp = lan.createExpression(text); 594 Object local = exp.evaluate(dummy, clazz); 595 if (!props.isEmpty()) { 596 setPropertiesOnTarget(camelContext, local, props); 597 } 598 return local; 599 } else { 600 return null; 601 } 602 } finally { 603 ef.release(dummy); 604 } 605 })); 606 } 607 } else if (beanFactory.getBeanClass() != null 608 || beanFactory.getType() != null && beanFactory.getType().startsWith("#class:")) { 609 // if there is a factory method then the class/bean should be created in a different way 610 String className = null; 611 String factoryMethod = null; 612 String parameters = null; 613 if (beanFactory.getType() != null) { 614 className = beanFactory.getType().substring(7); 615 if (className.endsWith(")") && className.indexOf('(') != -1) { 616 parameters = StringHelper.after(className, "("); 617 parameters = parameters.substring(0, parameters.length() - 1); // clip last ) 618 className = StringHelper.before(className, "("); 619 } 620 if (className != null && className.indexOf('#') != -1) { 621 factoryMethod = StringHelper.after(className, "#"); 622 className = StringHelper.before(className, "#"); 623 } 624 } 625 if (className != null && (factoryMethod != null || parameters != null)) { 626 final CamelContext camelContext = routeTemplateContext.getCamelContext(); 627 final Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(className); 628 final String fqn = className; 629 final String fm = factoryMethod; 630 final String fp = parameters; 631 routeTemplateContext.bind(beanFactory.getName(), Object.class, Suppliers.memorize(() -> { 632 // resolve placeholders in parameters 633 String params = camelContext.resolvePropertyPlaceholders(fp); 634 try { 635 Object local; 636 if (fm != null) { 637 if (fp != null) { 638 // special to support factory method parameters 639 local = PropertyBindingSupport.newInstanceFactoryParameters(camelContext, clazz, fm, params); 640 } else { 641 local = camelContext.getInjector().newInstance(clazz, fm); 642 } 643 if (local == null) { 644 throw new IllegalStateException( 645 "Cannot create bean instance using factory method: " + fqn + "#" + fm); 646 } 647 } else { 648 // special to support constructor parameters 649 local = PropertyBindingSupport.newInstanceConstructorParameters(camelContext, clazz, params); 650 } 651 if (!props.isEmpty()) { 652 setPropertiesOnTarget(camelContext, local, props); 653 } 654 return local; 655 } catch (Exception e) { 656 throw new IllegalStateException( 657 "Cannot create bean: " + beanFactory.getType()); 658 } 659 })); 660 } else { 661 final CamelContext camelContext = routeTemplateContext.getCamelContext(); 662 Class<?> clazz = beanFactory.getBeanClass() != null 663 ? beanFactory.getBeanClass() : camelContext.getClassResolver().resolveMandatoryClass(className); 664 // we only have the bean class so we use that to create a new bean via the injector 665 // and memorize so the bean is only created once and the local bean is the same 666 // if a route template refers to the local bean multiple times 667 routeTemplateContext.bind(beanFactory.getName(), clazz, 668 Suppliers.memorize(() -> { 669 Object local = camelContext.getInjector().newInstance(clazz); 670 if (!props.isEmpty()) { 671 setPropertiesOnTarget(camelContext, local, props); 672 } 673 return local; 674 })); 675 } 676 } else if (beanFactory.getType() != null && beanFactory.getType().startsWith("#type:")) { 677 final CamelContext camelContext = routeTemplateContext.getCamelContext(); 678 Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(beanFactory.getType().substring(6)); 679 Set<?> found = camelContext.getRegistry().findByType(clazz); 680 if (found == null || found.isEmpty()) { 681 throw new NoSuchBeanException(null, clazz.getName()); 682 } else if (found.size() > 1) { 683 throw new NoSuchBeanException( 684 "Found " + found.size() + " beans of type: " + clazz + ". Only one bean expected."); 685 } else { 686 // do not set properties when using #type as it uses an existing shared bean 687 routeTemplateContext.bind(beanFactory.getName(), clazz, found.iterator().next()); 688 } 689 } else { 690 // invalid syntax for the local bean, so lets report an exception 691 throw new IllegalArgumentException( 692 "Route template local bean: " + beanFactory.getName() + " has invalid type syntax: " + beanFactory.getType() 693 + ". To refer to a class then prefix the value with #class such as: #class:fullyQualifiedClassName"); 694 } 695 } 696 697 /** 698 * Sets the properties to the given target. 699 * 700 * @param context the context into which the properties must be set. 701 * @param target the object to which the properties must be set. 702 * @param properties the properties to set. 703 */ 704 private static void setPropertiesOnTarget(CamelContext context, Object target, Map<String, Object> properties) { 705 ObjectHelper.notNull(context, "context"); 706 ObjectHelper.notNull(target, "target"); 707 ObjectHelper.notNull(properties, "properties"); 708 709 if (target instanceof CamelContext) { 710 throw new UnsupportedOperationException("Configuring the Camel Context is not supported"); 711 } 712 713 PropertyConfigurer configurer = null; 714 if (target instanceof Component) { 715 // the component needs to be initialized to have the configurer ready 716 ServiceHelper.initService(target); 717 configurer = ((Component) target).getComponentPropertyConfigurer(); 718 } 719 720 if (configurer == null) { 721 // see if there is a configurer for it 722 configurer = context.getCamelContextExtension() 723 .getConfigurerResolver() 724 .resolvePropertyConfigurer(target.getClass().getSimpleName(), context); 725 } 726 727 try { 728 PropertyBindingSupport.build() 729 .withMandatory(true) 730 .withRemoveParameters(false) 731 .withConfigurer(configurer) 732 .withIgnoreCase(true) 733 .withFlattenProperties(true) 734 .bind(context, target, properties); 735 } catch (PropertyBindingException e) { 736 String key = e.getOptionKey(); 737 if (key == null) { 738 String prefix = e.getOptionPrefix(); 739 if (prefix != null && !prefix.endsWith(".")) { 740 prefix = "." + prefix; 741 } 742 743 key = prefix != null 744 ? prefix + "." + e.getPropertyName() 745 : e.getPropertyName(); 746 } 747 748 // enrich the error with more precise details with option prefix and key 749 throw new PropertyBindingException( 750 e.getTarget(), 751 e.getPropertyName(), 752 e.getValue(), 753 null, 754 key, 755 e.getCause()); 756 } 757 } 758 759 @Override 760 public void addRouteFromTemplatedRoute(TemplatedRouteDefinition templatedRouteDefinition) 761 throws Exception { 762 ObjectHelper.notNull(templatedRouteDefinition, "templatedRouteDefinition"); 763 764 final RouteTemplateContext routeTemplateContext = new DefaultRouteTemplateContext(camelContext); 765 // Load the parameters into the context 766 final List<TemplatedRouteParameterDefinition> parameters = templatedRouteDefinition.getParameters(); 767 if (parameters != null) { 768 for (TemplatedRouteParameterDefinition parameterDefinition : parameters) { 769 routeTemplateContext.setParameter(parameterDefinition.getName(), parameterDefinition.getValue()); 770 } 771 } 772 // Bind the beans into the context 773 final List<TemplatedRouteBeanDefinition> beans = templatedRouteDefinition.getBeans(); 774 if (beans != null) { 775 for (TemplatedRouteBeanDefinition beanDefinition : beans) { 776 bind(beanDefinition, routeTemplateContext); 777 } 778 } 779 // Add the route 780 addRouteFromTemplate(templatedRouteDefinition.getRouteId(), templatedRouteDefinition.getRouteTemplateRef(), 781 templatedRouteDefinition.getPrefixId(), routeTemplateContext); 782 } 783 784 @Override 785 public synchronized List<RestDefinition> getRestDefinitions() { 786 return restDefinitions; 787 } 788 789 @Override 790 public synchronized void addRestDefinitions(Collection<RestDefinition> restDefinitions, boolean addToRoutes) 791 throws Exception { 792 if (restDefinitions == null || restDefinitions.isEmpty()) { 793 return; 794 } 795 796 this.restDefinitions.addAll(restDefinitions); 797 if (addToRoutes) { 798 // rests are also routes so need to add them there too 799 for (final RestDefinition restDefinition : restDefinitions) { 800 List<RouteDefinition> routeDefinitions = restDefinition.asRouteDefinition(camelContext); 801 addRouteDefinitions(routeDefinitions); 802 } 803 } 804 } 805 806 @Override 807 public ServiceCallConfigurationDefinition getServiceCallConfiguration(String serviceName) { 808 if (serviceName == null) { 809 serviceName = ""; 810 } 811 812 return serviceCallConfigurations.get(serviceName); 813 } 814 815 @Override 816 public void setServiceCallConfiguration(ServiceCallConfigurationDefinition configuration) { 817 serviceCallConfigurations.put("", configuration); 818 } 819 820 @Override 821 public void setServiceCallConfigurations(List<ServiceCallConfigurationDefinition> configurations) { 822 if (configurations != null) { 823 for (ServiceCallConfigurationDefinition configuration : configurations) { 824 serviceCallConfigurations.put(configuration.getId(), configuration); 825 } 826 } 827 } 828 829 @Override 830 public void addServiceCallConfiguration(String serviceName, ServiceCallConfigurationDefinition configuration) { 831 serviceCallConfigurations.put(serviceName, configuration); 832 } 833 834 @Override 835 public Resilience4jConfigurationDefinition getResilience4jConfiguration(String id) { 836 if (id == null) { 837 id = ""; 838 } 839 840 return resilience4jConfigurations.get(id); 841 } 842 843 @Override 844 public void setResilience4jConfiguration(Resilience4jConfigurationDefinition configuration) { 845 resilience4jConfigurations.put("", configuration); 846 } 847 848 @Override 849 public void setResilience4jConfigurations(List<Resilience4jConfigurationDefinition> configurations) { 850 if (configurations != null) { 851 for (Resilience4jConfigurationDefinition configuration : configurations) { 852 resilience4jConfigurations.put(configuration.getId(), configuration); 853 } 854 } 855 } 856 857 @Override 858 public void addResilience4jConfiguration(String id, Resilience4jConfigurationDefinition configuration) { 859 resilience4jConfigurations.put(id, configuration); 860 } 861 862 @Override 863 public FaultToleranceConfigurationDefinition getFaultToleranceConfiguration(String id) { 864 if (id == null) { 865 id = ""; 866 } 867 868 return faultToleranceConfigurations.get(id); 869 } 870 871 @Override 872 public void setFaultToleranceConfiguration(FaultToleranceConfigurationDefinition configuration) { 873 faultToleranceConfigurations.put("", configuration); 874 } 875 876 @Override 877 public void setFaultToleranceConfigurations(List<FaultToleranceConfigurationDefinition> configurations) { 878 if (configurations != null) { 879 for (FaultToleranceConfigurationDefinition configuration : configurations) { 880 faultToleranceConfigurations.put(configuration.getId(), configuration); 881 } 882 } 883 } 884 885 @Override 886 public void addFaultToleranceConfiguration(String id, FaultToleranceConfigurationDefinition configuration) { 887 faultToleranceConfigurations.put(id, configuration); 888 } 889 890 @Override 891 public DataFormatDefinition resolveDataFormatDefinition(String name) { 892 // lookup type and create the data format from it 893 DataFormatDefinition type = lookup(camelContext, name, DataFormatDefinition.class); 894 if (type == null && getDataFormats() != null) { 895 type = getDataFormats().get(name); 896 } 897 return type; 898 } 899 900 @SuppressWarnings("rawtypes") 901 @Override 902 public ProcessorDefinition<?> getProcessorDefinition(String id) { 903 for (RouteDefinition route : getRouteDefinitions()) { 904 Collection<ProcessorDefinition> col 905 = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class); 906 for (ProcessorDefinition proc : col) { 907 if (id.equals(proc.getId())) { 908 return proc; 909 } 910 } 911 } 912 return null; 913 } 914 915 @Override 916 public <T extends ProcessorDefinition<T>> T getProcessorDefinition(String id, Class<T> type) { 917 ProcessorDefinition<?> answer = getProcessorDefinition(id); 918 if (answer != null) { 919 return type.cast(answer); 920 } 921 return null; 922 } 923 924 @Override 925 public Map<String, DataFormatDefinition> getDataFormats() { 926 return dataFormats; 927 } 928 929 @Override 930 public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) { 931 this.dataFormats = dataFormats; 932 } 933 934 @Override 935 public List<TransformerDefinition> getTransformers() { 936 return transformers; 937 } 938 939 @Override 940 public void setTransformers(List<TransformerDefinition> transformers) { 941 this.transformers = transformers; 942 } 943 944 @Override 945 public List<ValidatorDefinition> getValidators() { 946 return validators; 947 } 948 949 @Override 950 public void setValidators(List<ValidatorDefinition> validators) { 951 this.validators = validators; 952 } 953 954 @Override 955 public void setRouteFilterPattern(String include, String exclude) { 956 setRouteFilter(RouteFilters.filterByPattern(include, exclude)); 957 } 958 959 @Override 960 public Function<RouteDefinition, Boolean> getRouteFilter() { 961 return routeFilter; 962 } 963 964 @Override 965 public void setRouteFilter(Function<RouteDefinition, Boolean> routeFilter) { 966 this.routeFilter = routeFilter; 967 } 968 969 @Override 970 public ModelReifierFactory getModelReifierFactory() { 971 return modelReifierFactory; 972 } 973 974 @Override 975 public void setModelReifierFactory(ModelReifierFactory modelReifierFactory) { 976 this.modelReifierFactory = modelReifierFactory; 977 } 978 979 /** 980 * Should we start newly added routes? 981 */ 982 protected boolean shouldStartRoutes() { 983 return camelContext.isStarted() && !camelContext.isStarting(); 984 } 985 986 private static <T> T lookup(CamelContext context, String ref, Class<T> type) { 987 try { 988 return context.getRegistry().lookupByNameAndType(ref, type); 989 } catch (Exception e) { 990 // need to ignore not same type and return it as null 991 return null; 992 } 993 } 994 995 /** 996 * Indicates whether the route configuration should be included according to the precondition. 997 * 998 * @param definition the definition of the route configuration to check. 999 * @return {@code true} if the route configuration should be included, {@code false} otherwise. 1000 */ 1001 private boolean includedRouteConfiguration(RouteConfigurationDefinition definition) { 1002 return PreconditionHelper.included(definition, camelContext); 1003 } 1004}