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.Set;
026import java.util.function.Function;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.Expression;
030import org.apache.camel.ExtendedCamelContext;
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.builder.AdviceWith;
041import org.apache.camel.builder.AdviceWithRouteBuilder;
042import org.apache.camel.impl.engine.DefaultExecutorServiceManager;
043import org.apache.camel.impl.engine.RouteService;
044import org.apache.camel.impl.engine.SimpleCamelContext;
045import org.apache.camel.impl.engine.TransformerKey;
046import org.apache.camel.impl.engine.ValidatorKey;
047import org.apache.camel.impl.scan.AssignableToPackageScanFilter;
048import org.apache.camel.impl.scan.InvertingPackageScanFilter;
049import org.apache.camel.model.DataFormatDefinition;
050import org.apache.camel.model.FaultToleranceConfigurationDefinition;
051import org.apache.camel.model.Model;
052import org.apache.camel.model.ModelCamelContext;
053import org.apache.camel.model.ModelLifecycleStrategy;
054import org.apache.camel.model.ProcessorDefinition;
055import org.apache.camel.model.ProcessorDefinitionHelper;
056import org.apache.camel.model.Resilience4jConfigurationDefinition;
057import org.apache.camel.model.RouteConfigurationDefinition;
058import org.apache.camel.model.RouteDefinition;
059import org.apache.camel.model.RouteDefinitionHelper;
060import org.apache.camel.model.RouteTemplateDefinition;
061import org.apache.camel.model.RouteTemplatesDefinition;
062import org.apache.camel.model.RoutesDefinition;
063import org.apache.camel.model.TemplatedRouteDefinition;
064import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
065import org.apache.camel.model.language.ExpressionDefinition;
066import org.apache.camel.model.rest.RestDefinition;
067import org.apache.camel.model.rest.RestsDefinition;
068import org.apache.camel.model.transformer.TransformerDefinition;
069import org.apache.camel.model.validator.ValidatorDefinition;
070import org.apache.camel.spi.BeanRepository;
071import org.apache.camel.spi.DataFormat;
072import org.apache.camel.spi.DataType;
073import org.apache.camel.spi.ExecutorServiceManager;
074import org.apache.camel.spi.LocalBeanRepositoryAware;
075import org.apache.camel.spi.ModelReifierFactory;
076import org.apache.camel.spi.ModelToXMLDumper;
077import org.apache.camel.spi.PackageScanClassResolver;
078import org.apache.camel.spi.PropertiesComponent;
079import org.apache.camel.spi.Registry;
080import org.apache.camel.spi.StartupStepRecorder;
081import org.apache.camel.spi.Transformer;
082import org.apache.camel.spi.UuidGenerator;
083import org.apache.camel.spi.Validator;
084import org.apache.camel.support.CamelContextHelper;
085import org.apache.camel.support.DefaultRegistry;
086import org.apache.camel.support.LocalBeanRegistry;
087import org.apache.camel.support.SimpleUuidGenerator;
088import org.apache.camel.util.ObjectHelper;
089import org.apache.camel.util.OrderedLocationProperties;
090import org.apache.camel.util.StopWatch;
091import org.apache.camel.util.StringHelper;
092import org.apache.camel.util.concurrent.NamedThreadLocal;
093import org.slf4j.Logger;
094import org.slf4j.LoggerFactory;
095
096/**
097 * Represents the context used to configure routes and the policies to use.
098 */
099public class DefaultCamelContext extends SimpleCamelContext implements ModelCamelContext {
100
101    // global options that can be set on CamelContext as part of concurrent testing
102    // which means options should be isolated via thread-locals and not a static instance
103    // use a HashMap to store only JDK classes in the thread-local so there will not be any Camel classes leaking
104    private static final ThreadLocal<Map<String, Object>> OPTIONS = new NamedThreadLocal<>("CamelContextOptions", HashMap::new);
105    private static final String OPTION_NO_START = "OptionNoStart";
106    private static final String OPTION_DISABLE_JMX = "OptionDisableJMX";
107    private static final String OPTION_EXCLUDE_ROUTES = "OptionExcludeRoutes";
108
109    private static final Logger LOG = LoggerFactory.getLogger(DefaultCamelContext.class);
110    private static final UuidGenerator UUID = new SimpleUuidGenerator();
111
112    private Model model = new DefaultModel(this);
113
114    /**
115     * Creates the {@link ModelCamelContext} using {@link org.apache.camel.support.DefaultRegistry} as registry.
116     * <p/>
117     * Use one of the other constructors to force use an explicit registry.
118     */
119    public DefaultCamelContext() {
120        this(true);
121    }
122
123    /**
124     * Creates the {@link CamelContext} using the given {@link BeanRepository} as first-choice repository, and the
125     * {@link org.apache.camel.support.SimpleRegistry} as fallback, via the {@link DefaultRegistry} implementation.
126     *
127     * @param repository the bean repository.
128     */
129    public DefaultCamelContext(BeanRepository repository) {
130        this(new DefaultRegistry(repository));
131    }
132
133    /**
134     * Creates the {@link ModelCamelContext} using the given registry
135     *
136     * @param registry the registry
137     */
138    public DefaultCamelContext(Registry registry) {
139        this();
140        setRegistry(registry);
141    }
142
143    public DefaultCamelContext(boolean init) {
144        super(init);
145        if (isDisableJmx()) {
146            disableJMX();
147        }
148    }
149
150    @Override
151    protected void doStop() throws Exception {
152        super.doStop();
153        OPTIONS.remove();
154    }
155
156    @Override
157    protected void doDumpRoutes() {
158        ModelToXMLDumper dumper = getModelToXMLDumper();
159
160        int size = getRouteDefinitions().size();
161        if (size > 0) {
162            LOG.info("Dumping {} routes as XML", size);
163            // for XML to output nicely all routes in one XML then lets put them into <routes>
164            RoutesDefinition def = new RoutesDefinition();
165            def.setRoutes(getRouteDefinitions());
166            try {
167                String xml = dumper.dumpModelAsXml(this, def, true, true);
168                // lets separate routes with empty line
169                xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">",
170                        "xmlns=\"http://camel.apache.org/schema/spring\">\n");
171                xml = xml.replace("</route>", "</route>\n");
172                LOG.info("\n\n{}\n", xml);
173            } catch (Exception e) {
174                LOG.warn("Error dumping routes to XML due to {}. This exception is ignored.", e.getMessage(), e);
175            }
176        }
177
178        size = getRestDefinitions().size();
179        if (size > 0) {
180            LOG.info("Dumping {} rests as XML", size);
181            // for XML to output nicely all routes in one XML then lets put them into <routes>
182            RestsDefinition def = new RestsDefinition();
183            def.setRests(getRestDefinitions());
184            try {
185                String xml = dumper.dumpModelAsXml(this, def, true, true);
186                // lets separate rests with empty line
187                xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">",
188                        "xmlns=\"http://camel.apache.org/schema/spring\">\n");
189                xml = xml.replace("</rest>", "</rest>\n");
190                LOG.info("\n\n{}\n", xml);
191            } catch (Exception e) {
192                LOG.warn("Error dumping rests to XML due to {}. This exception is ignored.", e.getMessage(), e);
193            }
194        }
195
196        size = getRouteTemplateDefinitions().size();
197        if (size > 0) {
198            LOG.info("Dumping {} route templates as XML", size);
199            // for XML to output nicely all routes in one XML then lets put them into <routes>
200            RouteTemplatesDefinition def = new RouteTemplatesDefinition();
201            def.setRouteTemplates(getRouteTemplateDefinitions());
202            try {
203                String xml = dumper.dumpModelAsXml(this, def, true, true);
204                // lets separate rests with empty line
205                xml = StringHelper.replaceFirst(xml, "xmlns=\"http://camel.apache.org/schema/spring\">",
206                        "xmlns=\"http://camel.apache.org/schema/spring\">\n");
207                xml = xml.replace("</routeTemplate>", "</routeTemplate>\n");
208                LOG.info("\n\n{}\n", xml);
209            } catch (Exception e) {
210                LOG.warn("Error dumping route-templates to XML due to {}. This exception is ignored.", e.getMessage(), e);
211            }
212        }
213    }
214
215    public static void setNoStart(boolean b) {
216        getOptions().put(OPTION_NO_START, b);
217    }
218
219    public static boolean isNoStart() {
220        return (Boolean) getOptions().getOrDefault(OPTION_NO_START, Boolean.FALSE);
221    }
222
223    public static void setDisableJmx(boolean b) {
224        getOptions().put(OPTION_DISABLE_JMX, b);
225    }
226
227    public static boolean isDisableJmx() {
228        return (Boolean) getOptions().getOrDefault(OPTION_DISABLE_JMX, Boolean.getBoolean(JmxSystemPropertyKeys.DISABLED));
229    }
230
231    @Override
232    public String getTestExcludeRoutes() {
233        return getExcludeRoutes();
234    }
235
236    public static String getExcludeRoutes() {
237        return (String) getOptions().get(OPTION_EXCLUDE_ROUTES);
238    }
239
240    public static void setExcludeRoutes(String s) {
241        getOptions().put(OPTION_EXCLUDE_ROUTES, s);
242    }
243
244    public static void clearOptions() {
245        OPTIONS.get().clear();
246    }
247
248    private static Map<String, Object> getOptions() {
249        return OPTIONS.get();
250    }
251
252    @Override
253    public void start() {
254        // for example from unit testing we want to start Camel later (manually)
255        if (isNoStart()) {
256            LOG.trace("Ignoring start() as NO_START is true");
257            return;
258        }
259
260        if (!isStarted() && !isStarting()) {
261            StopWatch watch = new StopWatch();
262            super.start();
263            LOG.debug("start() took {} millis", watch.taken());
264        } else {
265            // ignore as Camel is already started
266            LOG.trace("Ignoring start() as Camel is already started");
267        }
268    }
269
270    @Override
271    protected PackageScanClassResolver createPackageScanClassResolver() {
272        PackageScanClassResolver resolver = super.createPackageScanClassResolver();
273        String excluded = getExcludeRoutes();
274        if (ObjectHelper.isNotEmpty(excluded)) {
275            Set<Class<?>> excludedClasses = new HashSet<>();
276            for (String str : excluded.split(",")) {
277                excludedClasses.add(getClassResolver().resolveClass(str));
278            }
279            resolver.addFilter(new InvertingPackageScanFilter(new AssignableToPackageScanFilter(excludedClasses)));
280        }
281        return resolver;
282    }
283
284    @Override
285    public void disposeModel() {
286        LOG.debug("Disposing Model on CamelContext");
287        model = null;
288    }
289
290    @Override
291    public void addModelLifecycleStrategy(ModelLifecycleStrategy modelLifecycleStrategy) {
292        if (model == null && isLightweight()) {
293            throw new IllegalStateException("Access to model not supported in lightweight mode");
294        }
295        model.addModelLifecycleStrategy(modelLifecycleStrategy);
296    }
297
298    @Override
299    public List<ModelLifecycleStrategy> getModelLifecycleStrategies() {
300        if (model == null && isLightweight()) {
301            throw new IllegalStateException("Access to model not supported in lightweight mode");
302        }
303        return model.getModelLifecycleStrategies();
304    }
305
306    @Override
307    public void addRouteConfiguration(RouteConfigurationDefinition routesConfiguration) {
308        if (model == null && isLightweight()) {
309            throw new IllegalStateException("Access to model not supported in lightweight mode");
310        }
311        model.addRouteConfiguration(routesConfiguration);
312    }
313
314    @Override
315    public void addRouteConfigurations(List<RouteConfigurationDefinition> routesConfigurations) {
316        if (model == null && isLightweight()) {
317            throw new IllegalStateException("Access to model not supported in lightweight mode");
318        }
319        model.addRouteConfigurations(routesConfigurations);
320    }
321
322    @Override
323    public List<RouteConfigurationDefinition> getRouteConfigurationDefinitions() {
324        if (model == null && isLightweight()) {
325            throw new IllegalStateException("Access to model not supported in lightweight mode");
326        }
327        return model.getRouteConfigurationDefinitions();
328    }
329
330    @Override
331    public RouteConfigurationDefinition getRouteConfigurationDefinition(String id) {
332        if (model == null && isLightweight()) {
333            throw new IllegalStateException("Access to model not supported in lightweight mode");
334        }
335        return model.getRouteConfigurationDefinition(id);
336    }
337
338    @Override
339    public void removeRouteConfiguration(RouteConfigurationDefinition routeConfigurationDefinition) throws Exception {
340        if (model == null && isLightweight()) {
341            throw new IllegalStateException("Access to model not supported in lightweight mode");
342        }
343        model.removeRouteConfiguration(routeConfigurationDefinition);
344    }
345
346    @Override
347    public List<RouteDefinition> getRouteDefinitions() {
348        if (model == null && isLightweight()) {
349            throw new IllegalStateException("Access to model not supported in lightweight mode");
350        }
351        return model.getRouteDefinitions();
352    }
353
354    @Override
355    public RouteDefinition getRouteDefinition(String id) {
356        if (model == null && isLightweight()) {
357            throw new IllegalStateException("Access to model not supported in lightweight mode");
358        }
359        return model.getRouteDefinition(id);
360    }
361
362    @Override
363    public void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
364        if (model == null && isLightweight()) {
365            throw new IllegalStateException("Access to model not supported in lightweight mode");
366        }
367        model.addRouteDefinitions(routeDefinitions);
368    }
369
370    @Override
371    public void addRouteDefinition(RouteDefinition routeDefinition) throws Exception {
372        if (model == null && isLightweight()) {
373            throw new IllegalStateException("Access to model not supported in lightweight mode");
374        }
375        model.addRouteDefinition(routeDefinition);
376    }
377
378    @Override
379    public void removeRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
380        if (model == null && isLightweight()) {
381            throw new IllegalStateException("Access to model not supported in lightweight mode");
382        }
383        if (!isLockModel()) {
384            model.removeRouteDefinitions(routeDefinitions);
385        }
386    }
387
388    @Override
389    public void removeRouteDefinition(RouteDefinition routeDefinition) throws Exception {
390        if (model == null && isLightweight()) {
391            throw new IllegalStateException("Access to model not supported in lightweight mode");
392        }
393        if (!isLockModel()) {
394            model.removeRouteDefinition(routeDefinition);
395        }
396    }
397
398    @Override
399    public List<RouteTemplateDefinition> getRouteTemplateDefinitions() {
400        if (model == null && isLightweight()) {
401            throw new IllegalStateException("Access to model not supported in lightweight mode");
402        }
403        return model.getRouteTemplateDefinitions();
404    }
405
406    @Override
407    public RouteTemplateDefinition getRouteTemplateDefinition(String id) {
408        if (model == null && isLightweight()) {
409            throw new IllegalStateException("Access to model not supported in lightweight mode");
410        }
411        return model.getRouteTemplateDefinition(id);
412    }
413
414    @Override
415    public void addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception {
416        if (model == null && isLightweight()) {
417            throw new IllegalStateException("Access to model not supported in lightweight mode");
418        }
419        model.addRouteTemplateDefinitions(routeTemplateDefinitions);
420    }
421
422    @Override
423    public void addRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception {
424        if (model == null && isLightweight()) {
425            throw new IllegalStateException("Access to model not supported in lightweight mode");
426        }
427        model.addRouteTemplateDefinition(routeTemplateDefinition);
428    }
429
430    @Override
431    public void removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception {
432        if (model == null && isLightweight()) {
433            throw new IllegalStateException("Access to model not supported in lightweight mode");
434        }
435        if (!isLockModel()) {
436            model.removeRouteTemplateDefinitions(routeTemplateDefinitions);
437        }
438    }
439
440    @Override
441    public void removeRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception {
442        if (model == null && isLightweight()) {
443            throw new IllegalStateException("Access to model not supported in lightweight mode");
444        }
445        if (!isLockModel()) {
446            model.removeRouteTemplateDefinition(routeTemplateDefinition);
447        }
448    }
449
450    @Override
451    public void removeRouteTemplateDefinitions(String pattern) throws Exception {
452        if (model == null && isLightweight()) {
453            throw new IllegalStateException("Access to model not supported in lightweight mode");
454        }
455        if (!isLockModel()) {
456            model.removeRouteTemplateDefinitions(pattern);
457        }
458    }
459
460    @Override
461    public void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter) {
462        if (model == null && isLightweight()) {
463            throw new IllegalStateException("Access to model not supported in lightweight mode");
464        }
465        model.addRouteTemplateDefinitionConverter(templateIdPattern, converter);
466    }
467
468    @Override
469    public String addRouteFromTemplate(String routeId, String routeTemplateId, Map<String, Object> parameters)
470            throws Exception {
471        if (model == null && isLightweight()) {
472            throw new IllegalStateException("Access to model not supported in lightweight mode");
473        }
474        return model.addRouteFromTemplate(routeId, routeTemplateId, parameters);
475    }
476
477    @Override
478    public String addRouteFromTemplate(String routeId, String routeTemplateId, String prefixId, Map<String, Object> parameters)
479            throws Exception {
480        if (model == null && isLightweight()) {
481            throw new IllegalStateException("Access to model not supported in lightweight mode");
482        }
483        return model.addRouteFromTemplate(routeId, routeTemplateId, prefixId, parameters);
484    }
485
486    @Override
487    public String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
488            throws Exception {
489        if (model == null && isLightweight()) {
490            throw new IllegalStateException("Access to model not supported in lightweight mode");
491        }
492        return model.addRouteFromTemplate(routeId, routeTemplateId, routeTemplateContext);
493    }
494
495    @Override
496    public String addRouteFromTemplate(
497            String routeId, String routeTemplateId, String prefixId, RouteTemplateContext routeTemplateContext)
498            throws Exception {
499        if (model == null && isLightweight()) {
500            throw new IllegalStateException("Access to model not supported in lightweight mode");
501        }
502        return model.addRouteFromTemplate(routeId, routeTemplateId, prefixId, routeTemplateContext);
503    }
504
505    @Override
506    public void addRouteFromTemplatedRoute(TemplatedRouteDefinition templatedRouteDefinition)
507            throws Exception {
508        if (model == null && isLightweight()) {
509            throw new IllegalStateException("Access to model not supported in lightweight mode");
510        }
511        model.addRouteFromTemplatedRoute(templatedRouteDefinition);
512    }
513
514    @Override
515    public void removeRouteTemplates(String pattern) throws Exception {
516        if (model == null && isLightweight()) {
517            throw new IllegalStateException("Access to model not supported in lightweight mode");
518        }
519        if (!isLockModel()) {
520            model.removeRouteTemplateDefinitions(pattern);
521        }
522    }
523
524    @Override
525    public List<RestDefinition> getRestDefinitions() {
526        if (model == null && isLightweight()) {
527            throw new IllegalStateException("Access to model not supported in lightweight mode");
528        }
529        return model.getRestDefinitions();
530    }
531
532    @Override
533    public void addRestDefinitions(Collection<RestDefinition> restDefinitions, boolean addToRoutes) throws Exception {
534        if (model == null && isLightweight()) {
535            throw new IllegalStateException("Access to model not supported in lightweight mode");
536        }
537        model.addRestDefinitions(restDefinitions, addToRoutes);
538    }
539
540    @Override
541    public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) {
542        if (model == null && isLightweight()) {
543            throw new IllegalStateException("Access to model not supported in lightweight mode");
544        }
545        model.setDataFormats(dataFormats);
546    }
547
548    @Override
549    public Map<String, DataFormatDefinition> getDataFormats() {
550        if (model == null && isLightweight()) {
551            throw new IllegalStateException("Access to model not supported in lightweight mode");
552        }
553        return model.getDataFormats();
554    }
555
556    @Override
557    public DataFormatDefinition resolveDataFormatDefinition(String name) {
558        if (model == null && isLightweight()) {
559            throw new IllegalStateException("Access to model not supported in lightweight mode");
560        }
561        return model.resolveDataFormatDefinition(name);
562    }
563
564    @Override
565    public ProcessorDefinition<?> getProcessorDefinition(String id) {
566        if (model == null && isLightweight()) {
567            throw new IllegalStateException("Access to model not supported in lightweight mode");
568        }
569        return model.getProcessorDefinition(id);
570    }
571
572    @Override
573    public <T extends ProcessorDefinition<T>> T getProcessorDefinition(String id, Class<T> type) {
574        if (model == null && isLightweight()) {
575            throw new IllegalStateException("Access to model not supported in lightweight mode");
576        }
577        return model.getProcessorDefinition(id, type);
578    }
579
580    @Override
581    public void setValidators(List<ValidatorDefinition> validators) {
582        if (model == null && isLightweight()) {
583            throw new IllegalStateException("Access to model not supported in lightweight mode");
584        }
585        model.setValidators(validators);
586    }
587
588    @Override
589    public Resilience4jConfigurationDefinition getResilience4jConfiguration(String id) {
590        if (model == null && isLightweight()) {
591            throw new IllegalStateException("Access to model not supported in lightweight mode");
592        }
593        return model.getResilience4jConfiguration(id);
594    }
595
596    @Override
597    public void setResilience4jConfiguration(Resilience4jConfigurationDefinition configuration) {
598        if (model == null && isLightweight()) {
599            throw new IllegalStateException("Access to model not supported in lightweight mode");
600        }
601        model.setResilience4jConfiguration(configuration);
602    }
603
604    @Override
605    public void setResilience4jConfigurations(List<Resilience4jConfigurationDefinition> configurations) {
606        if (model == null && isLightweight()) {
607            throw new IllegalStateException("Access to model not supported in lightweight mode");
608        }
609        model.setResilience4jConfigurations(configurations);
610    }
611
612    @Override
613    public void addResilience4jConfiguration(String id, Resilience4jConfigurationDefinition configuration) {
614        if (model == null && isLightweight()) {
615            throw new IllegalStateException("Access to model not supported in lightweight mode");
616        }
617        model.addResilience4jConfiguration(id, configuration);
618    }
619
620    @Override
621    public FaultToleranceConfigurationDefinition getFaultToleranceConfiguration(String id) {
622        if (model == null && isLightweight()) {
623            throw new IllegalStateException("Access to model not supported in lightweight mode");
624        }
625        return model.getFaultToleranceConfiguration(id);
626    }
627
628    @Override
629    public void setFaultToleranceConfiguration(FaultToleranceConfigurationDefinition configuration) {
630        if (model == null && isLightweight()) {
631            throw new IllegalStateException("Access to model not supported in lightweight mode");
632        }
633        model.setFaultToleranceConfiguration(configuration);
634    }
635
636    @Override
637    public void setFaultToleranceConfigurations(List<FaultToleranceConfigurationDefinition> configurations) {
638        if (model == null && isLightweight()) {
639            throw new IllegalStateException("Access to model not supported in lightweight mode");
640        }
641        model.setFaultToleranceConfigurations(configurations);
642    }
643
644    @Override
645    public void addFaultToleranceConfiguration(String id, FaultToleranceConfigurationDefinition configuration) {
646        if (model == null && isLightweight()) {
647            throw new IllegalStateException("Access to model not supported in lightweight mode");
648        }
649        model.addFaultToleranceConfiguration(id, configuration);
650    }
651
652    @Override
653    public List<ValidatorDefinition> getValidators() {
654        if (model == null && isLightweight()) {
655            throw new IllegalStateException("Access to model not supported in lightweight mode");
656        }
657        return model.getValidators();
658    }
659
660    @Override
661    public void setTransformers(List<TransformerDefinition> transformers) {
662        if (model == null && isLightweight()) {
663            throw new IllegalStateException("Access to model not supported in lightweight mode");
664        }
665        model.setTransformers(transformers);
666    }
667
668    @Override
669    public List<TransformerDefinition> getTransformers() {
670        if (model == null && isLightweight()) {
671            throw new IllegalStateException("Access to model not supported in lightweight mode");
672        }
673        return model.getTransformers();
674    }
675
676    @Override
677    public ServiceCallConfigurationDefinition getServiceCallConfiguration(String serviceName) {
678        if (model == null && isLightweight()) {
679            throw new IllegalStateException("Access to model not supported in lightweight mode");
680        }
681        return model.getServiceCallConfiguration(serviceName);
682    }
683
684    @Override
685    public void setServiceCallConfiguration(ServiceCallConfigurationDefinition configuration) {
686        if (model == null && isLightweight()) {
687            throw new IllegalStateException("Access to model not supported in lightweight mode");
688        }
689        model.setServiceCallConfiguration(configuration);
690    }
691
692    @Override
693    public void setServiceCallConfigurations(List<ServiceCallConfigurationDefinition> configurations) {
694        if (model == null && isLightweight()) {
695            throw new IllegalStateException("Access to model not supported in lightweight mode");
696        }
697        model.setServiceCallConfigurations(configurations);
698    }
699
700    @Override
701    public void addServiceCallConfiguration(String serviceName, ServiceCallConfigurationDefinition configuration) {
702        if (model == null && isLightweight()) {
703            throw new IllegalStateException("Access to model not supported in lightweight mode");
704        }
705        model.addServiceCallConfiguration(serviceName, configuration);
706    }
707
708    @Override
709    public void setRouteFilterPattern(String include, String exclude) {
710        if (model == null && isLightweight()) {
711            throw new IllegalStateException("Access to model not supported in lightweight mode");
712        }
713        model.setRouteFilterPattern(include, exclude);
714    }
715
716    @Override
717    public void setRouteFilter(Function<RouteDefinition, Boolean> filter) {
718        if (model == null && isLightweight()) {
719            throw new IllegalStateException("Access to model not supported in lightweight mode");
720        }
721        model.setRouteFilter(filter);
722    }
723
724    @Override
725    public Function<RouteDefinition, Boolean> getRouteFilter() {
726        if (model == null && isLightweight()) {
727            throw new IllegalStateException("Access to model not supported in lightweight mode");
728        }
729        return model.getRouteFilter();
730    }
731
732    @Override
733    public ModelReifierFactory getModelReifierFactory() {
734        if (model == null && isLightweight()) {
735            throw new IllegalStateException("Access to model not supported in lightweight mode");
736        }
737        return model.getModelReifierFactory();
738    }
739
740    @Override
741    public void setModelReifierFactory(ModelReifierFactory modelReifierFactory) {
742        if (model == null && isLightweight()) {
743            throw new IllegalStateException("Access to model not supported in lightweight mode");
744        }
745        model.setModelReifierFactory(modelReifierFactory);
746    }
747
748    @Override
749    protected void bindDataFormats() throws Exception {
750        // eager lookup data formats and bind to registry so the dataformats can
751        // be looked up and used
752        if (model != null) {
753            for (Map.Entry<String, DataFormatDefinition> e : model.getDataFormats().entrySet()) {
754                String id = e.getKey();
755                DataFormatDefinition def = e.getValue();
756                LOG.debug("Creating Dataformat with id: {} and definition: {}", id, def);
757                DataFormat df = model.getModelReifierFactory().createDataFormat(this, def);
758                addService(df, true);
759                getRegistry().bind(id, df);
760            }
761        }
762    }
763
764    @Override
765    protected synchronized void shutdownRouteService(RouteService routeService) throws Exception {
766        if (model != null) {
767            RouteDefinition rd = model.getRouteDefinition(routeService.getId());
768            if (rd != null) {
769                model.getRouteDefinitions().remove(rd);
770            }
771        }
772        super.shutdownRouteService(routeService);
773    }
774
775    @Override
776    protected boolean isStreamCachingInUse() throws Exception {
777        boolean streamCachingInUse = super.isStreamCachingInUse();
778        if (!streamCachingInUse) {
779            for (RouteDefinition route : model.getRouteDefinitions()) {
780                Boolean routeCache = CamelContextHelper.parseBoolean(this, route.getStreamCache());
781                if (routeCache != null && routeCache) {
782                    streamCachingInUse = true;
783                    break;
784                }
785            }
786        }
787        return streamCachingInUse;
788    }
789
790    @Override
791    public void startRouteDefinitions() throws Exception {
792        if (model == null && isLightweight()) {
793            throw new IllegalStateException("Access to model not supported in lightweight mode");
794        }
795        List<RouteDefinition> routeDefinitions = model.getRouteDefinitions();
796        if (routeDefinitions != null) {
797            // defensive copy of routes to be started as kamelets
798            // can add route definitions from existing routes
799            List<RouteDefinition> toBeStarted = new ArrayList<>(routeDefinitions);
800            startRouteDefinitions(toBeStarted);
801        }
802    }
803
804    @Override
805    public void removeRouteDefinitionsFromTemplate() throws Exception {
806        if (model == null && isLightweight()) {
807            throw new IllegalStateException("Access to model not supported in lightweight mode");
808        }
809        List<RouteDefinition> toBeRemoved = new ArrayList<>();
810        for (RouteDefinition rd : model.getRouteDefinitions()) {
811            if (rd.isTemplate() != null && rd.isTemplate()) {
812                toBeRemoved.add(rd);
813            }
814        }
815        removeRouteDefinitions(toBeRemoved);
816    }
817
818    public void startRouteDefinitions(List<RouteDefinition> routeDefinitions) throws Exception {
819        if (model == null && isLightweight()) {
820            throw new IllegalStateException("Access to model not supported in lightweight mode");
821        }
822
823        // indicate we are staring the route using this thread so
824        // we are able to query this if needed
825        boolean alreadyStartingRoutes = isStartingRoutes();
826        if (!alreadyStartingRoutes) {
827            setStartingRoutes(true);
828        }
829
830        PropertiesComponent pc = getCamelContextReference().getPropertiesComponent();
831        // route templates supports binding beans that are local for the template only
832        // in this local mode then we need to check for side-effects (see further)
833        LocalBeanRepositoryAware localBeans = null;
834        if (getCamelContextReference().getRegistry() instanceof LocalBeanRepositoryAware) {
835            localBeans = (LocalBeanRepositoryAware) getCamelContextReference().getRegistry();
836        }
837        try {
838            RouteDefinitionHelper.forceAssignIds(getCamelContextReference(), routeDefinitions);
839            List<RouteDefinition> routeDefinitionsToRemove = null;
840            for (RouteDefinition routeDefinition : routeDefinitions) {
841                // assign ids to the routes and validate that the id's is all unique
842                String duplicate = RouteDefinitionHelper.validateUniqueIds(routeDefinition, routeDefinitions,
843                        routeDefinition.getNodePrefixId());
844                if (duplicate != null) {
845                    throw new FailedToStartRouteException(
846                            routeDefinition.getId(),
847                            "duplicate id detected: " + duplicate + ". Please correct ids to be unique among all your routes.");
848                }
849
850                // 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
851                if (routeDefinition.isTemplate() != null && routeDefinition.isTemplate()
852                        && routeDefinition.getTemplateParameters() != null) {
853
854                    // apply configurer if any present
855                    if (routeDefinition.getRouteTemplateContext().getConfigurer() != null) {
856                        routeDefinition.getRouteTemplateContext().getConfigurer()
857                                .accept(routeDefinition.getRouteTemplateContext());
858                    }
859
860                    // copy parameters/bean repository to not cause side effect
861                    Map<Object, Object> params = new HashMap<>(routeDefinition.getTemplateParameters());
862                    LocalBeanRegistry bbr
863                            = (LocalBeanRegistry) routeDefinition.getRouteTemplateContext().getLocalBeanRepository();
864                    LocalBeanRegistry bbrCopy = new LocalBeanRegistry();
865
866                    // make all bean in the bean repository use unique keys (need to add uuid counter)
867                    // so when the route template is used again to create another route, then there is
868                    // no side-effect from previously used values that Camel may use in its endpoint
869                    // registry and elsewhere
870                    if (bbr != null && !bbr.isEmpty()) {
871                        for (Map.Entry<Object, Object> param : params.entrySet()) {
872                            Object value = param.getValue();
873                            if (value instanceof String) {
874                                String oldKey = (String) value;
875                                boolean clash = bbr.keys().stream().anyMatch(k -> k.equals(oldKey));
876                                if (clash) {
877                                    String newKey = oldKey + "-" + UUID.generateUuid();
878                                    LOG.debug(
879                                            "Route: {} re-assigning local-bean id: {} to: {} to ensure ids are globally unique",
880                                            routeDefinition.getId(), oldKey, newKey);
881                                    bbrCopy.put(newKey, bbr.remove(oldKey));
882                                    param.setValue(newKey);
883                                }
884                            }
885                        }
886                        // the remainder of the local beans must also have their ids made global unique
887                        for (String oldKey : bbr.keySet()) {
888                            String newKey = oldKey + "-" + UUID.generateUuid();
889                            LOG.debug(
890                                    "Route: {} re-assigning local-bean id: {} to: {} to ensure ids are globally unique",
891                                    routeDefinition.getId(), oldKey, newKey);
892                            bbrCopy.put(newKey, bbr.get(oldKey));
893                            if (!params.containsKey(oldKey)) {
894                                // if a bean was bound as local bean with a key and it was not defined as template parameter
895                                // then store it as if it was a template parameter with same key=value which allows us
896                                // to use this local bean in the route without any problem such as:
897                                //   to("bean:{{myBean}}")
898                                // and myBean is the local bean id.
899                                params.put(oldKey, newKey);
900                            }
901                        }
902                    }
903
904                    OrderedLocationProperties prop = new OrderedLocationProperties();
905                    if (routeDefinition.getTemplateDefaultParameters() != null) {
906                        // need to keep track if a parameter is set as default value or end user configured value
907                        params.forEach((k, v) -> {
908                            Object dv = routeDefinition.getTemplateDefaultParameters().get(k);
909                            prop.put(routeDefinition.getLocation(), k, v, dv);
910                        });
911                    } else {
912                        prop.putAll(routeDefinition.getLocation(), params);
913                    }
914                    pc.setLocalProperties(prop);
915
916                    // we need to shadow the bean registry on the CamelContext with the local beans from the route template context
917                    if (localBeans != null && bbrCopy != null) {
918                        localBeans.setLocalBeanRepository(bbrCopy);
919                    }
920
921                    // need to reset auto assigned ids, so there is no clash when creating routes
922                    ProcessorDefinitionHelper.resetAllAutoAssignedNodeIds(routeDefinition);
923                    // must re-init parent when created from a template
924                    RouteDefinitionHelper.initParent(routeDefinition);
925                }
926                // Check if the route is included
927                if (includedRoute(routeDefinition)) {
928                    // must ensure route is prepared, before we can start it
929                    if (!routeDefinition.isPrepared()) {
930                        RouteDefinitionHelper.prepareRoute(getCamelContextReference(), routeDefinition);
931                        routeDefinition.markPrepared();
932                    }
933
934                    StartupStepRecorder recorder
935                            = getCamelContextReference().adapt(ExtendedCamelContext.class).getStartupStepRecorder();
936                    StartupStep step = recorder.beginStep(Route.class, routeDefinition.getRouteId(), "Create Route");
937                    Route route = model.getModelReifierFactory().createRoute(this, routeDefinition);
938                    recorder.endStep(step);
939
940                    RouteService routeService = new RouteService(route);
941                    startRouteService(routeService, true);
942                } else {
943                    // Add the definition to the list of definitions to remove as the route is excluded
944                    if (routeDefinitionsToRemove == null) {
945                        routeDefinitionsToRemove = new ArrayList<>(routeDefinitions.size());
946                    }
947                    routeDefinitionsToRemove.add(routeDefinition);
948                }
949
950                // clear local after the route is created via the reifier
951                pc.setLocalProperties(null);
952                if (localBeans != null) {
953                    localBeans.setLocalBeanRepository(null);
954                }
955            }
956            if (routeDefinitionsToRemove != null) {
957                // Remove all the excluded routes
958                model.removeRouteDefinitions(routeDefinitionsToRemove);
959            }
960        } finally {
961            if (!alreadyStartingRoutes) {
962                setStartingRoutes(false);
963            }
964            pc.setLocalProperties(null);
965            if (localBeans != null) {
966                localBeans.setLocalBeanRepository(null);
967            }
968        }
969    }
970
971    @Override
972    protected ExecutorServiceManager createExecutorServiceManager() {
973        return new DefaultExecutorServiceManager(this);
974    }
975
976    @Override
977    public Processor createErrorHandler(Route route, Processor processor) throws Exception {
978        if (model == null && isLightweight()) {
979            throw new IllegalStateException("Access to model not supported in lightweight mode");
980        }
981        return model.getModelReifierFactory().createErrorHandler(route, processor);
982    }
983
984    @Override
985    public Expression createExpression(ExpressionDefinition definition) {
986        if (model == null && isLightweight()) {
987            throw new IllegalStateException("Access to model not supported in lightweight mode");
988        }
989        return model.getModelReifierFactory().createExpression(this, definition);
990    }
991
992    @Override
993    public Predicate createPredicate(ExpressionDefinition definition) {
994        if (model == null && isLightweight()) {
995            throw new IllegalStateException("Access to model not supported in lightweight mode");
996        }
997        return model.getModelReifierFactory().createPredicate(this, definition);
998    }
999
1000    @Override
1001    public RouteDefinition adviceWith(RouteDefinition definition, AdviceWithRouteBuilder builder) throws Exception {
1002        return AdviceWith.adviceWith(definition, this, builder);
1003    }
1004
1005    @Override
1006    public void registerValidator(ValidatorDefinition def) {
1007        if (model == null && isLightweight()) {
1008            throw new IllegalStateException("Access to model not supported in lightweight mode");
1009        }
1010        model.getValidators().add(def);
1011        Validator validator = model.getModelReifierFactory().createValidator(this, def);
1012        getValidatorRegistry().put(createValidatorKey(def), validator);
1013    }
1014
1015    private static ValueHolder<String> createValidatorKey(ValidatorDefinition def) {
1016        return new ValidatorKey(new DataType(def.getType()));
1017    }
1018
1019    @Override
1020    public void registerTransformer(TransformerDefinition def) {
1021        if (model == null && isLightweight()) {
1022            throw new IllegalStateException("Access to model not supported in lightweight mode");
1023        }
1024        model.getTransformers().add(def);
1025        Transformer transformer = model.getModelReifierFactory().createTransformer(this, def);
1026        getTransformerRegistry().put(createTransformerKey(def), transformer);
1027    }
1028
1029    @Override
1030    protected boolean removeRoute(String routeId, LoggingLevel loggingLevel) throws Exception {
1031        // synchronize on model first to avoid deadlock with concurrent 'addRoutes' calls:
1032        synchronized (model) {
1033            synchronized (this) {
1034                boolean removed = super.removeRoute(routeId, loggingLevel);
1035                if (removed) {
1036                    // must also remove the route definition
1037                    RouteDefinition def = getRouteDefinition(routeId);
1038                    if (def != null) {
1039                        removeRouteDefinition(def);
1040                    }
1041                }
1042                return removed;
1043            }
1044        }
1045    }
1046
1047    @Override
1048    public boolean removeRoute(String routeId) throws Exception {
1049        // synchronize on model first to avoid deadlock with concurrent 'addRoutes' calls:
1050        synchronized (model) {
1051            return super.removeRoute(routeId);
1052        }
1053    }
1054
1055    /**
1056     * Indicates whether the route should be included according to the precondition.
1057     *
1058     * @param  definition the definition of the route to check.
1059     * @return            {@code true} if the route should be included, {@code false} otherwise.
1060     */
1061    private boolean includedRoute(RouteDefinition definition) {
1062        return PreconditionHelper.included(definition, this);
1063    }
1064
1065    private static ValueHolder<String> createTransformerKey(TransformerDefinition def) {
1066        return ObjectHelper.isNotEmpty(def.getScheme())
1067                ? new TransformerKey(def.getScheme())
1068                : new TransformerKey(new DataType(def.getFromType()), new DataType(def.getToType()));
1069    }
1070
1071}