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