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.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.StringJoiner; 027import java.util.concurrent.ConcurrentHashMap; 028import java.util.function.Function; 029 030import org.apache.camel.CamelContext; 031import org.apache.camel.ExtendedCamelContext; 032import org.apache.camel.model.DataFormatDefinition; 033import org.apache.camel.model.FaultToleranceConfigurationDefinition; 034import org.apache.camel.model.HystrixConfigurationDefinition; 035import org.apache.camel.model.Model; 036import org.apache.camel.model.ModelCamelContext; 037import org.apache.camel.model.ModelLifecycleStrategy; 038import org.apache.camel.model.ProcessorDefinition; 039import org.apache.camel.model.ProcessorDefinitionHelper; 040import org.apache.camel.model.Resilience4jConfigurationDefinition; 041import org.apache.camel.model.RouteDefinition; 042import org.apache.camel.model.RouteFilters; 043import org.apache.camel.model.RouteTemplateDefinition; 044import org.apache.camel.model.RouteTemplateParameterDefinition; 045import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition; 046import org.apache.camel.model.rest.RestDefinition; 047import org.apache.camel.model.transformer.TransformerDefinition; 048import org.apache.camel.model.validator.ValidatorDefinition; 049import org.apache.camel.spi.ModelReifierFactory; 050import org.apache.camel.util.AntPathMatcher; 051 052public class DefaultModel implements Model { 053 054 private final CamelContext camelContext; 055 056 private ModelReifierFactory modelReifierFactory = new DefaultModelReifierFactory(); 057 private final List<ModelLifecycleStrategy> modelLifecycleStrategies = new ArrayList<>(); 058 private final List<RouteDefinition> routeDefinitions = new ArrayList<>(); 059 private final List<RouteTemplateDefinition> routeTemplateDefinitions = new ArrayList<>(); 060 private final List<RestDefinition> restDefinitions = new ArrayList<>(); 061 private final Map<String, RouteTemplateDefinition.Converter> routeTemplateConverters = new ConcurrentHashMap<>(); 062 private Map<String, DataFormatDefinition> dataFormats = new HashMap<>(); 063 private List<TransformerDefinition> transformers = new ArrayList<>(); 064 private List<ValidatorDefinition> validators = new ArrayList<>(); 065 private final Map<String, ServiceCallConfigurationDefinition> serviceCallConfigurations = new ConcurrentHashMap<>(); 066 private final Map<String, HystrixConfigurationDefinition> hystrixConfigurations = new ConcurrentHashMap<>(); 067 private final Map<String, Resilience4jConfigurationDefinition> resilience4jConfigurations = new ConcurrentHashMap<>(); 068 private final Map<String, FaultToleranceConfigurationDefinition> faultToleranceConfigurations = new ConcurrentHashMap<>(); 069 private Function<RouteDefinition, Boolean> routeFilter; 070 071 public DefaultModel(CamelContext camelContext) { 072 this.camelContext = camelContext; 073 } 074 075 public CamelContext getCamelContext() { 076 return camelContext; 077 } 078 079 @Override 080 public void addModelLifecycleStrategy(ModelLifecycleStrategy modelLifecycleStrategy) { 081 this.modelLifecycleStrategies.add(modelLifecycleStrategy); 082 } 083 084 @Override 085 public List<ModelLifecycleStrategy> getModelLifecycleStrategies() { 086 return modelLifecycleStrategies; 087 } 088 089 @Override 090 public synchronized void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception { 091 if (routeDefinitions == null || routeDefinitions.isEmpty()) { 092 return; 093 } 094 List<RouteDefinition> list = new ArrayList<>(); 095 routeDefinitions.forEach(r -> { 096 if (routeFilter == null || routeFilter.apply(r)) { 097 list.add(r); 098 } 099 }); 100 101 removeRouteDefinitions(list); 102 list.forEach(r -> { 103 modelLifecycleStrategies.forEach(s -> s.onAddRouteDefinition(r)); 104 this.routeDefinitions.add(r); 105 }); 106 if (shouldStartRoutes()) { 107 getCamelContext().adapt(ModelCamelContext.class).startRouteDefinitions(list); 108 } 109 } 110 111 @Override 112 public void addRouteDefinition(RouteDefinition routeDefinition) throws Exception { 113 addRouteDefinitions(Collections.singletonList(routeDefinition)); 114 } 115 116 @Override 117 public synchronized void removeRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception { 118 for (RouteDefinition routeDefinition : routeDefinitions) { 119 removeRouteDefinition(routeDefinition); 120 } 121 } 122 123 @Override 124 public synchronized void removeRouteDefinition(RouteDefinition routeDefinition) throws Exception { 125 RouteDefinition toBeRemoved = routeDefinition; 126 String id = routeDefinition.getId(); 127 if (id != null) { 128 // remove existing route 129 camelContext.getRouteController().stopRoute(id); 130 camelContext.removeRoute(id); 131 toBeRemoved = getRouteDefinition(id); 132 } 133 for (ModelLifecycleStrategy s : modelLifecycleStrategies) { 134 s.onRemoveRouteDefinition(toBeRemoved); 135 } 136 this.routeDefinitions.remove(toBeRemoved); 137 } 138 139 @Override 140 public synchronized List<RouteDefinition> getRouteDefinitions() { 141 return routeDefinitions; 142 } 143 144 @Override 145 public synchronized RouteDefinition getRouteDefinition(String id) { 146 for (RouteDefinition route : routeDefinitions) { 147 if (route.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory()).equals(id)) { 148 return route; 149 } 150 } 151 return null; 152 } 153 154 @Override 155 public List<RouteTemplateDefinition> getRouteTemplateDefinitions() { 156 return routeTemplateDefinitions; 157 } 158 159 @Override 160 public RouteTemplateDefinition getRouteTemplateDefinition(String id) { 161 for (RouteTemplateDefinition route : routeTemplateDefinitions) { 162 if (route.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory()).equals(id)) { 163 return route; 164 } 165 } 166 return null; 167 } 168 169 @Override 170 public void addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception { 171 if (routeTemplateDefinitions == null || routeTemplateDefinitions.isEmpty()) { 172 return; 173 } 174 routeTemplateDefinitions.forEach(r -> { 175 modelLifecycleStrategies.forEach(s -> s.onAddRouteTemplateDefinition(r)); 176 this.routeTemplateDefinitions.add(r); 177 }); 178 } 179 180 @Override 181 public void addRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception { 182 addRouteTemplateDefinitions(Collections.singletonList(routeTemplateDefinition)); 183 } 184 185 @Override 186 public void removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception { 187 for (RouteTemplateDefinition r : routeTemplateDefinitions) { 188 removeRouteTemplateDefinition(r); 189 } 190 } 191 192 @Override 193 public void removeRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception { 194 for (ModelLifecycleStrategy s : modelLifecycleStrategies) { 195 s.onRemoveRouteTemplateDefinition(routeTemplateDefinition); 196 } 197 routeTemplateDefinitions.remove(routeTemplateDefinition); 198 } 199 200 @Override 201 public void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter) { 202 routeTemplateConverters.put(templateIdPattern, converter); 203 } 204 205 @Override 206 public String addRouteFromTemplate(final String routeId, final String routeTemplateId, final Map<String, Object> parameters) 207 throws Exception { 208 RouteTemplateDefinition target = null; 209 for (RouteTemplateDefinition def : routeTemplateDefinitions) { 210 if (routeTemplateId.equals(def.getId())) { 211 target = def; 212 break; 213 } 214 } 215 if (target == null) { 216 throw new IllegalArgumentException("Cannot find RouteTemplate with id " + routeTemplateId); 217 } 218 219 final Map<String, Object> prop = new HashMap<>(); 220 // include default values first from the template (and validate that we have inputs for all required parameters) 221 if (target.getTemplateParameters() != null) { 222 StringJoiner templatesBuilder = new StringJoiner(", "); 223 224 for (RouteTemplateParameterDefinition temp : target.getTemplateParameters()) { 225 if (temp.getDefaultValue() != null) { 226 prop.put(temp.getName(), temp.getDefaultValue()); 227 } else { 228 // this is a required parameter do we have that as input 229 if (!parameters.containsKey(temp.getName())) { 230 templatesBuilder.add(temp.getName()); 231 } 232 } 233 } 234 if (templatesBuilder.length() > 0) { 235 throw new IllegalArgumentException( 236 "Route template " + routeTemplateId + " the following mandatory parameters must be provided: " 237 + templatesBuilder.toString()); 238 } 239 } 240 241 // then override with user parameters 242 if (parameters != null) { 243 prop.putAll(parameters); 244 } 245 246 RouteTemplateDefinition.Converter converter = RouteTemplateDefinition.Converter.DEFAULT_CONVERTER; 247 248 for (Map.Entry<String, RouteTemplateDefinition.Converter> entry : routeTemplateConverters.entrySet()) { 249 final String key = entry.getKey(); 250 final String templateId = target.getId(); 251 252 if ("*".equals(key) || templateId.equals(key)) { 253 converter = entry.getValue(); 254 break; 255 } else if (AntPathMatcher.INSTANCE.match(key, templateId)) { 256 converter = entry.getValue(); 257 break; 258 } else if (templateId.matches(key)) { 259 converter = entry.getValue(); 260 break; 261 } 262 } 263 264 RouteDefinition def = converter.apply(target, prop); 265 if (routeId != null) { 266 def.setId(routeId); 267 } 268 def.setTemplateParameters(prop); 269 addRouteDefinition(def); 270 return def.getId(); 271 } 272 273 @Override 274 public synchronized List<RestDefinition> getRestDefinitions() { 275 return restDefinitions; 276 } 277 278 @Override 279 public synchronized void addRestDefinitions(Collection<RestDefinition> restDefinitions, boolean addToRoutes) 280 throws Exception { 281 if (restDefinitions == null || restDefinitions.isEmpty()) { 282 return; 283 } 284 285 this.restDefinitions.addAll(restDefinitions); 286 if (addToRoutes) { 287 // rests are also routes so need to add them there too 288 for (final RestDefinition restDefinition : restDefinitions) { 289 List<RouteDefinition> routeDefinitions = restDefinition.asRouteDefinition(camelContext); 290 addRouteDefinitions(routeDefinitions); 291 } 292 } 293 } 294 295 @Override 296 public ServiceCallConfigurationDefinition getServiceCallConfiguration(String serviceName) { 297 if (serviceName == null) { 298 serviceName = ""; 299 } 300 301 return serviceCallConfigurations.get(serviceName); 302 } 303 304 @Override 305 public void setServiceCallConfiguration(ServiceCallConfigurationDefinition configuration) { 306 serviceCallConfigurations.put("", configuration); 307 } 308 309 @Override 310 public void setServiceCallConfigurations(List<ServiceCallConfigurationDefinition> configurations) { 311 if (configurations != null) { 312 for (ServiceCallConfigurationDefinition configuration : configurations) { 313 serviceCallConfigurations.put(configuration.getId(), configuration); 314 } 315 } 316 } 317 318 @Override 319 public void addServiceCallConfiguration(String serviceName, ServiceCallConfigurationDefinition configuration) { 320 serviceCallConfigurations.put(serviceName, configuration); 321 } 322 323 @Override 324 public HystrixConfigurationDefinition getHystrixConfiguration(String id) { 325 if (id == null) { 326 id = ""; 327 } 328 329 return hystrixConfigurations.get(id); 330 } 331 332 @Override 333 public void setHystrixConfiguration(HystrixConfigurationDefinition configuration) { 334 hystrixConfigurations.put("", configuration); 335 } 336 337 @Override 338 public void setHystrixConfigurations(List<HystrixConfigurationDefinition> configurations) { 339 if (configurations != null) { 340 for (HystrixConfigurationDefinition configuration : configurations) { 341 hystrixConfigurations.put(configuration.getId(), configuration); 342 } 343 } 344 } 345 346 @Override 347 public void addHystrixConfiguration(String id, HystrixConfigurationDefinition configuration) { 348 hystrixConfigurations.put(id, configuration); 349 } 350 351 @Override 352 public Resilience4jConfigurationDefinition getResilience4jConfiguration(String id) { 353 if (id == null) { 354 id = ""; 355 } 356 357 return resilience4jConfigurations.get(id); 358 } 359 360 @Override 361 public void setResilience4jConfiguration(Resilience4jConfigurationDefinition configuration) { 362 resilience4jConfigurations.put("", configuration); 363 } 364 365 @Override 366 public void setResilience4jConfigurations(List<Resilience4jConfigurationDefinition> configurations) { 367 if (configurations != null) { 368 for (Resilience4jConfigurationDefinition configuration : configurations) { 369 resilience4jConfigurations.put(configuration.getId(), configuration); 370 } 371 } 372 } 373 374 @Override 375 public void addResilience4jConfiguration(String id, Resilience4jConfigurationDefinition configuration) { 376 resilience4jConfigurations.put(id, configuration); 377 } 378 379 @Override 380 public FaultToleranceConfigurationDefinition getFaultToleranceConfiguration(String id) { 381 if (id == null) { 382 id = ""; 383 } 384 385 return faultToleranceConfigurations.get(id); 386 } 387 388 @Override 389 public void setFaultToleranceConfiguration(FaultToleranceConfigurationDefinition configuration) { 390 faultToleranceConfigurations.put("", configuration); 391 } 392 393 @Override 394 public void setFaultToleranceConfigurations(List<FaultToleranceConfigurationDefinition> configurations) { 395 if (configurations != null) { 396 for (FaultToleranceConfigurationDefinition configuration : configurations) { 397 faultToleranceConfigurations.put(configuration.getId(), configuration); 398 } 399 } 400 } 401 402 @Override 403 public void addFaultToleranceConfiguration(String id, FaultToleranceConfigurationDefinition configuration) { 404 faultToleranceConfigurations.put(id, configuration); 405 } 406 407 @Override 408 public DataFormatDefinition resolveDataFormatDefinition(String name) { 409 // lookup type and create the data format from it 410 DataFormatDefinition type = lookup(camelContext, name, DataFormatDefinition.class); 411 if (type == null && getDataFormats() != null) { 412 type = getDataFormats().get(name); 413 } 414 return type; 415 } 416 417 @SuppressWarnings("rawtypes") 418 @Override 419 public ProcessorDefinition<?> getProcessorDefinition(String id) { 420 for (RouteDefinition route : getRouteDefinitions()) { 421 Iterator<ProcessorDefinition> it 422 = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class); 423 while (it.hasNext()) { 424 ProcessorDefinition<?> proc = it.next(); 425 if (id.equals(proc.getId())) { 426 return proc; 427 } 428 } 429 } 430 return null; 431 } 432 433 @Override 434 public <T extends ProcessorDefinition<T>> T getProcessorDefinition(String id, Class<T> type) { 435 ProcessorDefinition<?> answer = getProcessorDefinition(id); 436 if (answer != null) { 437 return type.cast(answer); 438 } 439 return null; 440 } 441 442 @Override 443 public Map<String, DataFormatDefinition> getDataFormats() { 444 return dataFormats; 445 } 446 447 @Override 448 public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) { 449 this.dataFormats = dataFormats; 450 } 451 452 @Override 453 public List<TransformerDefinition> getTransformers() { 454 return transformers; 455 } 456 457 @Override 458 public void setTransformers(List<TransformerDefinition> transformers) { 459 this.transformers = transformers; 460 } 461 462 @Override 463 public List<ValidatorDefinition> getValidators() { 464 return validators; 465 } 466 467 @Override 468 public void setValidators(List<ValidatorDefinition> validators) { 469 this.validators = validators; 470 } 471 472 @Override 473 public void setRouteFilterPattern(String include, String exclude) { 474 setRouteFilter(RouteFilters.filterByPattern(include, exclude)); 475 } 476 477 @Override 478 public Function<RouteDefinition, Boolean> getRouteFilter() { 479 return routeFilter; 480 } 481 482 @Override 483 public void setRouteFilter(Function<RouteDefinition, Boolean> routeFilter) { 484 this.routeFilter = routeFilter; 485 } 486 487 @Override 488 public ModelReifierFactory getModelReifierFactory() { 489 return modelReifierFactory; 490 } 491 492 @Override 493 public void setModelReifierFactory(ModelReifierFactory modelReifierFactory) { 494 this.modelReifierFactory = modelReifierFactory; 495 } 496 497 /** 498 * Should we start newly added routes? 499 */ 500 protected boolean shouldStartRoutes() { 501 return camelContext.isStarted() && !camelContext.isStarting(); 502 } 503 504 private static <T> T lookup(CamelContext context, String ref, Class<T> type) { 505 try { 506 return context.getRegistry().lookupByNameAndType(ref, type); 507 } catch (Exception e) { 508 // need to ignore not same type and return it as null 509 return null; 510 } 511 } 512 513}