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