001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.impl;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026import java.util.StringJoiner;
027import java.util.concurrent.ConcurrentHashMap;
028import java.util.function.Function;
029
030import org.apache.camel.CamelContext;
031import org.apache.camel.Component;
032import org.apache.camel.Exchange;
033import org.apache.camel.Expression;
034import org.apache.camel.ExtendedCamelContext;
035import org.apache.camel.FailedToCreateRouteFromTemplateException;
036import org.apache.camel.NoSuchBeanException;
037import org.apache.camel.PropertyBindingException;
038import org.apache.camel.RouteTemplateContext;
039import org.apache.camel.model.DataFormatDefinition;
040import org.apache.camel.model.DefaultRouteTemplateContext;
041import org.apache.camel.model.FaultToleranceConfigurationDefinition;
042import org.apache.camel.model.HystrixConfigurationDefinition;
043import org.apache.camel.model.Model;
044import org.apache.camel.model.ModelCamelContext;
045import org.apache.camel.model.ModelLifecycleStrategy;
046import org.apache.camel.model.ProcessorDefinition;
047import org.apache.camel.model.ProcessorDefinitionHelper;
048import org.apache.camel.model.Resilience4jConfigurationDefinition;
049import org.apache.camel.model.RouteDefinition;
050import org.apache.camel.model.RouteDefinitionHelper;
051import org.apache.camel.model.RouteFilters;
052import org.apache.camel.model.RouteTemplateBeanDefinition;
053import org.apache.camel.model.RouteTemplateDefinition;
054import org.apache.camel.model.RouteTemplateParameterDefinition;
055import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
056import org.apache.camel.model.rest.RestDefinition;
057import org.apache.camel.model.transformer.TransformerDefinition;
058import org.apache.camel.model.validator.ValidatorDefinition;
059import org.apache.camel.spi.ExchangeFactory;
060import org.apache.camel.spi.Language;
061import org.apache.camel.spi.ModelReifierFactory;
062import org.apache.camel.spi.PropertyConfigurer;
063import org.apache.camel.spi.RouteTemplateLoaderListener;
064import org.apache.camel.spi.RouteTemplateParameterSource;
065import org.apache.camel.spi.ScriptingLanguage;
066import org.apache.camel.support.CamelContextHelper;
067import org.apache.camel.support.PropertyBindingSupport;
068import org.apache.camel.support.RouteTemplateHelper;
069import org.apache.camel.support.ScriptHelper;
070import org.apache.camel.support.service.ServiceHelper;
071import org.apache.camel.util.AntPathMatcher;
072import org.apache.camel.util.ObjectHelper;
073import org.apache.camel.util.StringHelper;
074import org.apache.camel.util.function.Suppliers;
075
076public class DefaultModel implements Model {
077
078    private final CamelContext camelContext;
079
080    private ModelReifierFactory modelReifierFactory = new DefaultModelReifierFactory();
081    private final List<ModelLifecycleStrategy> modelLifecycleStrategies = new ArrayList<>();
082    private final List<RouteDefinition> routeDefinitions = new ArrayList<>();
083    private final List<RouteTemplateDefinition> routeTemplateDefinitions = new ArrayList<>();
084    private final List<RestDefinition> restDefinitions = new ArrayList<>();
085    private final Map<String, RouteTemplateDefinition.Converter> routeTemplateConverters = new ConcurrentHashMap<>();
086    private Map<String, DataFormatDefinition> dataFormats = new HashMap<>();
087    private List<TransformerDefinition> transformers = new ArrayList<>();
088    private List<ValidatorDefinition> validators = new ArrayList<>();
089    private final Map<String, ServiceCallConfigurationDefinition> serviceCallConfigurations = new ConcurrentHashMap<>();
090    private final Map<String, HystrixConfigurationDefinition> hystrixConfigurations = new ConcurrentHashMap<>();
091    private final Map<String, Resilience4jConfigurationDefinition> resilience4jConfigurations = new ConcurrentHashMap<>();
092    private final Map<String, FaultToleranceConfigurationDefinition> faultToleranceConfigurations = new ConcurrentHashMap<>();
093    private Function<RouteDefinition, Boolean> routeFilter;
094
095    public DefaultModel(CamelContext camelContext) {
096        this.camelContext = camelContext;
097    }
098
099    public CamelContext getCamelContext() {
100        return camelContext;
101    }
102
103    @Override
104    public void addModelLifecycleStrategy(ModelLifecycleStrategy modelLifecycleStrategy) {
105        // avoid adding double which can happen with spring xml on spring boot
106        if (!this.modelLifecycleStrategies.contains(modelLifecycleStrategy)) {
107            this.modelLifecycleStrategies.add(modelLifecycleStrategy);
108        }
109    }
110
111    @Override
112    public List<ModelLifecycleStrategy> getModelLifecycleStrategies() {
113        return modelLifecycleStrategies;
114    }
115
116    @Override
117    public synchronized void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
118        if (routeDefinitions == null || routeDefinitions.isEmpty()) {
119            return;
120        }
121
122        List<RouteDefinition> list;
123        if (routeFilter == null) {
124            list = new ArrayList<>(routeDefinitions);
125        } else {
126            list = new ArrayList<>();
127            for (RouteDefinition r : routeDefinitions) {
128                if (routeFilter.apply(r)) {
129                    list.add(r);
130                }
131            }
132        }
133
134        removeRouteDefinitions(list);
135
136        for (RouteDefinition r : list) {
137            for (ModelLifecycleStrategy s : modelLifecycleStrategies) {
138                s.onAddRouteDefinition(r);
139            }
140            this.routeDefinitions.add(r);
141        }
142
143        if (shouldStartRoutes()) {
144            getCamelContext().adapt(ModelCamelContext.class).startRouteDefinitions(list);
145        }
146    }
147
148    @Override
149    public void addRouteDefinition(RouteDefinition routeDefinition) throws Exception {
150        addRouteDefinitions(Collections.singletonList(routeDefinition));
151    }
152
153    @Override
154    public synchronized void removeRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
155        for (RouteDefinition routeDefinition : routeDefinitions) {
156            removeRouteDefinition(routeDefinition);
157        }
158    }
159
160    @Override
161    public synchronized void removeRouteDefinition(RouteDefinition routeDefinition) throws Exception {
162        RouteDefinition toBeRemoved = routeDefinition;
163        String id = routeDefinition.getId();
164        if (id != null) {
165            // remove existing route
166            camelContext.getRouteController().stopRoute(id);
167            camelContext.removeRoute(id);
168            toBeRemoved = getRouteDefinition(id);
169        }
170        for (ModelLifecycleStrategy s : modelLifecycleStrategies) {
171            s.onRemoveRouteDefinition(toBeRemoved);
172        }
173        this.routeDefinitions.remove(toBeRemoved);
174    }
175
176    @Override
177    public synchronized List<RouteDefinition> getRouteDefinitions() {
178        return routeDefinitions;
179    }
180
181    @Override
182    public synchronized RouteDefinition getRouteDefinition(String id) {
183        for (RouteDefinition route : routeDefinitions) {
184            if (route.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory()).equals(id)) {
185                return route;
186            }
187        }
188        return null;
189    }
190
191    @Override
192    public List<RouteTemplateDefinition> getRouteTemplateDefinitions() {
193        return routeTemplateDefinitions;
194    }
195
196    @Override
197    public RouteTemplateDefinition getRouteTemplateDefinition(String id) {
198        for (RouteTemplateDefinition route : routeTemplateDefinitions) {
199            if (route.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory()).equals(id)) {
200                return route;
201            }
202        }
203        return null;
204    }
205
206    @Override
207    public void addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception {
208        if (routeTemplateDefinitions == null || routeTemplateDefinitions.isEmpty()) {
209            return;
210        }
211
212        for (RouteTemplateDefinition r : routeTemplateDefinitions) {
213            for (ModelLifecycleStrategy s : modelLifecycleStrategies) {
214                s.onAddRouteTemplateDefinition(r);
215            }
216            this.routeTemplateDefinitions.add(r);
217        }
218    }
219
220    @Override
221    public void addRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception {
222        addRouteTemplateDefinitions(Collections.singletonList(routeTemplateDefinition));
223    }
224
225    @Override
226    public void removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> routeTemplateDefinitions) throws Exception {
227        for (RouteTemplateDefinition r : routeTemplateDefinitions) {
228            removeRouteTemplateDefinition(r);
229        }
230    }
231
232    @Override
233    public void removeRouteTemplateDefinition(RouteTemplateDefinition routeTemplateDefinition) throws Exception {
234        for (ModelLifecycleStrategy s : modelLifecycleStrategies) {
235            s.onRemoveRouteTemplateDefinition(routeTemplateDefinition);
236        }
237        routeTemplateDefinitions.remove(routeTemplateDefinition);
238    }
239
240    @Override
241    public void addRouteTemplateDefinitionConverter(String templateIdPattern, RouteTemplateDefinition.Converter converter) {
242        routeTemplateConverters.put(templateIdPattern, converter);
243    }
244
245    @Override
246    @Deprecated
247    public String addRouteFromTemplate(final String routeId, final String routeTemplateId, final Map<String, Object> parameters)
248            throws Exception {
249        RouteTemplateContext rtc = new DefaultRouteTemplateContext(camelContext);
250        if (parameters != null) {
251            parameters.forEach(rtc::setParameter);
252        }
253        return addRouteFromTemplate(routeId, routeTemplateId, rtc);
254    }
255
256    @Override
257    public String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
258            throws Exception {
259        RouteTemplateDefinition target = null;
260        for (RouteTemplateDefinition def : routeTemplateDefinitions) {
261            if (routeTemplateId.equals(def.getId())) {
262                target = def;
263                break;
264            }
265        }
266        if (target == null) {
267            // if the route template has a location parameter, then try to load route templates from the location
268            // and look up again
269            Object location = routeTemplateContext.getParameters().get(RouteTemplateParameterSource.LOCATION);
270            if (location != null) {
271                RouteTemplateLoaderListener listener
272                        = CamelContextHelper.findByType(getCamelContext(), RouteTemplateLoaderListener.class);
273                RouteTemplateHelper.loadRouteTemplateFromLocation(getCamelContext(), listener, routeTemplateId,
274                        location.toString());
275            }
276            for (RouteTemplateDefinition def : routeTemplateDefinitions) {
277                if (routeTemplateId.equals(def.getId())) {
278                    target = def;
279                    break;
280                }
281            }
282        }
283        if (target == null) {
284            throw new IllegalArgumentException("Cannot find RouteTemplate with id " + routeTemplateId);
285        }
286
287        final Map<String, Object> prop = new HashMap<>();
288        // include default values first from the template (and validate that we have inputs for all required parameters)
289        if (target.getTemplateParameters() != null) {
290            StringJoiner templatesBuilder = new StringJoiner(", ");
291
292            for (RouteTemplateParameterDefinition temp : target.getTemplateParameters()) {
293                if (temp.getDefaultValue() != null) {
294                    prop.put(temp.getName(), temp.getDefaultValue());
295                } else {
296                    if (temp.isRequired() && !routeTemplateContext.getParameters().containsKey(temp.getName())) {
297                        // this is a required parameter which is missing
298                        templatesBuilder.add(temp.getName());
299                    }
300                }
301            }
302            if (templatesBuilder.length() > 0) {
303                throw new IllegalArgumentException(
304                        "Route template " + routeTemplateId + " the following mandatory parameters must be provided: "
305                                                   + templatesBuilder);
306            }
307        }
308
309        // then override with user parameters part 1
310        if (routeTemplateContext.getParameters() != null) {
311            prop.putAll(routeTemplateContext.getParameters());
312        }
313        // route template context should include default template parameters from the target route template
314        // so it has all parameters available
315        if (target.getTemplateParameters() != null) {
316            for (RouteTemplateParameterDefinition temp : target.getTemplateParameters()) {
317                if (!routeTemplateContext.getParameters().containsKey(temp.getName()) && temp.getDefaultValue() != null) {
318                    routeTemplateContext.setParameter(temp.getName(), temp.getDefaultValue());
319                }
320            }
321        }
322
323        RouteTemplateDefinition.Converter converter = RouteTemplateDefinition.Converter.DEFAULT_CONVERTER;
324
325        for (Map.Entry<String, RouteTemplateDefinition.Converter> entry : routeTemplateConverters.entrySet()) {
326            final String key = entry.getKey();
327            final String templateId = target.getId();
328
329            if ("*".equals(key) || templateId.equals(key)) {
330                converter = entry.getValue();
331                break;
332            } else if (AntPathMatcher.INSTANCE.match(key, templateId)) {
333                converter = entry.getValue();
334                break;
335            } else if (templateId.matches(key)) {
336                converter = entry.getValue();
337                break;
338            }
339        }
340
341        RouteDefinition def = converter.apply(target, prop);
342        if (routeId != null) {
343            def.setId(routeId);
344        }
345        def.setTemplateParameters(prop);
346        def.setRouteTemplateContext(routeTemplateContext);
347
348        // setup local beans
349        if (target.getTemplateBeans() != null) {
350            addTemplateBeans(routeTemplateContext, target);
351        }
352
353        if (target.getConfigurer() != null) {
354            routeTemplateContext.setConfigurer(target.getConfigurer());
355        }
356
357        // assign ids to the routes and validate that the id's are all unique
358        String duplicate = RouteDefinitionHelper.validateUniqueIds(def, routeDefinitions);
359        if (duplicate != null) {
360            throw new FailedToCreateRouteFromTemplateException(
361                    routeId, routeTemplateId,
362                    "duplicate id detected: " + duplicate + ". Please correct ids to be unique among all your routes.");
363        }
364        addRouteDefinition(def);
365        return def.getId();
366    }
367
368    private void addTemplateBeans(RouteTemplateContext routeTemplateContext, RouteTemplateDefinition target) throws Exception {
369        for (RouteTemplateBeanDefinition b : target.getTemplateBeans()) {
370            final Map<String, Object> props = new HashMap<>();
371            if (b.getProperties() != null) {
372                b.getProperties().forEach(p -> props.put(p.getKey(), p.getValue()));
373            }
374            if (b.getBeanSupplier() != null) {
375                if (props.isEmpty()) {
376                    // bean class is optional for supplier
377                    if (b.getBeanClass() != null) {
378                        routeTemplateContext.bind(b.getName(), b.getBeanClass(), b.getBeanSupplier());
379                    } else {
380                        routeTemplateContext.bind(b.getName(), b.getBeanSupplier());
381                    }
382                }
383            } else if (b.getScript() != null) {
384                final String script = b.getScript().getScript();
385                final Language lan = camelContext.resolveLanguage(b.getType());
386                final Class<?> clazz = b.getBeanType() != null
387                        ? camelContext.getClassResolver().resolveMandatoryClass(b.getBeanType())
388                        : b.getBeanClass() != null ? b.getBeanClass() : Object.class;
389                final ScriptingLanguage slan = lan instanceof ScriptingLanguage ? (ScriptingLanguage) lan : null;
390                if (slan != null) {
391                    // scripting language should be evaluated with route template context as binding
392                    // and memorize so the script is only evaluated once and the local bean is the same
393                    // if a route template refers to the local bean multiple times
394                    routeTemplateContext.bind(b.getName(), clazz, Suppliers.memorize(() -> {
395                        Map<String, Object> bindings = new HashMap<>();
396                        // use rtx as the short-hand name, as context would imply its CamelContext
397                        bindings.put("rtc", routeTemplateContext);
398                        Object local = slan.evaluate(script, bindings, clazz);
399                        if (!props.isEmpty()) {
400                            setPropertiesOnTarget(camelContext, local, props);
401                        }
402                        return local;
403                    }));
404                } else {
405                    // exchange based languages needs a dummy exchange to be evaluated
406                    // and memorize so the script is only evaluated once and the local bean is the same
407                    // if a route template refers to the local bean multiple times
408                    routeTemplateContext.bind(b.getName(), clazz, Suppliers.memorize(() -> {
409                        ExchangeFactory ef = camelContext.adapt(ExtendedCamelContext.class).getExchangeFactory();
410                        Exchange dummy = ef.create(false);
411                        try {
412                            String text = ScriptHelper.resolveOptionalExternalScript(camelContext, dummy, script);
413                            if (text != null) {
414                                Expression exp = lan.createExpression(text);
415                                Object local = exp.evaluate(dummy, clazz);
416                                if (!props.isEmpty()) {
417                                    setPropertiesOnTarget(camelContext, local, props);
418                                }
419                                return local;
420                            } else {
421                                return null;
422                            }
423                        } finally {
424                            ef.release(dummy);
425                        }
426                    }));
427                }
428            } else if (b.getBeanClass() != null || b.getType() != null && b.getType().startsWith("#class:")) {
429                // if there is a factory method then the class/bean should be created in a different way
430                String className = null;
431                String factoryMethod = null;
432                String parameters = null;
433                if (b.getType() != null) {
434                    className = b.getType().substring(7);
435                    if (className.endsWith(")") && className.indexOf('(') != -1) {
436                        parameters = StringHelper.after(className, "(");
437                        parameters = parameters.substring(0, parameters.length() - 1); // clip last )
438                        className = StringHelper.before(className, "(");
439                    }
440                    if (className != null && className.indexOf('#') != -1) {
441                        factoryMethod = StringHelper.after(className, "#");
442                        className = StringHelper.before(className, "#");
443                    }
444                }
445                if (className != null && (factoryMethod != null || parameters != null)) {
446                    final Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(className);
447                    final String fqn = className;
448                    final String fm = factoryMethod;
449                    final String fp = parameters;
450                    routeTemplateContext.bind(b.getName(), Object.class, Suppliers.memorize(() -> {
451                        try {
452                            Object local;
453                            if (fm != null) {
454                                if (fp != null) {
455                                    // special to support factory method parameters
456                                    local = PropertyBindingSupport.newInstanceFactoryParameters(camelContext, clazz, fm, fp);
457                                } else {
458                                    local = camelContext.getInjector().newInstance(clazz, fm);
459                                }
460                                if (local == null) {
461                                    throw new IllegalStateException(
462                                            "Cannot create bean instance using factory method: " + fqn + "#" + fm);
463                                }
464                            } else {
465                                // special to support constructor parameters
466                                local = PropertyBindingSupport.newInstanceConstructorParameters(camelContext, clazz, fp);
467                            }
468                            if (!props.isEmpty()) {
469                                setPropertiesOnTarget(camelContext, local, props);
470                            }
471                            return local;
472                        } catch (Exception e) {
473                            throw new IllegalStateException(
474                                    "Cannot create bean: " + b.getType());
475                        }
476                    }));
477                } else {
478                    Class<?> clazz = b.getBeanClass() != null
479                            ? b.getBeanClass() : camelContext.getClassResolver().resolveMandatoryClass(className);
480                    // we only have the bean class so we use that to create a new bean via the injector
481                    // and memorize so the bean is only created once and the local bean is the same
482                    // if a route template refers to the local bean multiple times
483                    routeTemplateContext.bind(b.getName(), clazz,
484                            Suppliers.memorize(() -> {
485                                Object local = camelContext.getInjector().newInstance(clazz);
486                                if (!props.isEmpty()) {
487                                    setPropertiesOnTarget(camelContext, local, props);
488                                }
489                                return local;
490                            }));
491                }
492            } else if (b.getType() != null && b.getType().startsWith("#type:")) {
493                Class<?> clazz = camelContext.getClassResolver().resolveMandatoryClass(b.getType().substring(6));
494                Set<?> found = getCamelContext().getRegistry().findByType(clazz);
495                if (found == null || found.isEmpty()) {
496                    throw new NoSuchBeanException(null, clazz.getName());
497                } else if (found.size() > 1) {
498                    throw new NoSuchBeanException(
499                            "Found " + found.size() + " beans of type: " + clazz + ". Only one bean expected.");
500                } else {
501                    // do not set properties when using #type as it uses an existing shared bean
502                    routeTemplateContext.bind(b.getName(), clazz, found.iterator().next());
503                }
504            } else {
505                // invalid syntax for the local bean, so lets report an exception
506                throw new IllegalArgumentException(
507                        "Route template local bean: " + b.getName() + " has invalid type syntax: " + b.getType()
508                                                   + ". To refer to a class then prefix the value with #class such as: #class:fullyQualifiedClassName");
509            }
510        }
511    }
512
513    @Override
514    public synchronized List<RestDefinition> getRestDefinitions() {
515        return restDefinitions;
516    }
517
518    @Override
519    public synchronized void addRestDefinitions(Collection<RestDefinition> restDefinitions, boolean addToRoutes)
520            throws Exception {
521        if (restDefinitions == null || restDefinitions.isEmpty()) {
522            return;
523        }
524
525        this.restDefinitions.addAll(restDefinitions);
526        if (addToRoutes) {
527            // rests are also routes so need to add them there too
528            for (final RestDefinition restDefinition : restDefinitions) {
529                List<RouteDefinition> routeDefinitions = restDefinition.asRouteDefinition(camelContext);
530                addRouteDefinitions(routeDefinitions);
531            }
532        }
533    }
534
535    @Override
536    public ServiceCallConfigurationDefinition getServiceCallConfiguration(String serviceName) {
537        if (serviceName == null) {
538            serviceName = "";
539        }
540
541        return serviceCallConfigurations.get(serviceName);
542    }
543
544    @Override
545    public void setServiceCallConfiguration(ServiceCallConfigurationDefinition configuration) {
546        serviceCallConfigurations.put("", configuration);
547    }
548
549    @Override
550    public void setServiceCallConfigurations(List<ServiceCallConfigurationDefinition> configurations) {
551        if (configurations != null) {
552            for (ServiceCallConfigurationDefinition configuration : configurations) {
553                serviceCallConfigurations.put(configuration.getId(), configuration);
554            }
555        }
556    }
557
558    @Override
559    public void addServiceCallConfiguration(String serviceName, ServiceCallConfigurationDefinition configuration) {
560        serviceCallConfigurations.put(serviceName, configuration);
561    }
562
563    @Override
564    public HystrixConfigurationDefinition getHystrixConfiguration(String id) {
565        if (id == null) {
566            id = "";
567        }
568
569        return hystrixConfigurations.get(id);
570    }
571
572    @Override
573    public void setHystrixConfiguration(HystrixConfigurationDefinition configuration) {
574        hystrixConfigurations.put("", configuration);
575    }
576
577    @Override
578    public void setHystrixConfigurations(List<HystrixConfigurationDefinition> configurations) {
579        if (configurations != null) {
580            for (HystrixConfigurationDefinition configuration : configurations) {
581                hystrixConfigurations.put(configuration.getId(), configuration);
582            }
583        }
584    }
585
586    @Override
587    public void addHystrixConfiguration(String id, HystrixConfigurationDefinition configuration) {
588        hystrixConfigurations.put(id, configuration);
589    }
590
591    @Override
592    public Resilience4jConfigurationDefinition getResilience4jConfiguration(String id) {
593        if (id == null) {
594            id = "";
595        }
596
597        return resilience4jConfigurations.get(id);
598    }
599
600    @Override
601    public void setResilience4jConfiguration(Resilience4jConfigurationDefinition configuration) {
602        resilience4jConfigurations.put("", configuration);
603    }
604
605    @Override
606    public void setResilience4jConfigurations(List<Resilience4jConfigurationDefinition> configurations) {
607        if (configurations != null) {
608            for (Resilience4jConfigurationDefinition configuration : configurations) {
609                resilience4jConfigurations.put(configuration.getId(), configuration);
610            }
611        }
612    }
613
614    @Override
615    public void addResilience4jConfiguration(String id, Resilience4jConfigurationDefinition configuration) {
616        resilience4jConfigurations.put(id, configuration);
617    }
618
619    @Override
620    public FaultToleranceConfigurationDefinition getFaultToleranceConfiguration(String id) {
621        if (id == null) {
622            id = "";
623        }
624
625        return faultToleranceConfigurations.get(id);
626    }
627
628    @Override
629    public void setFaultToleranceConfiguration(FaultToleranceConfigurationDefinition configuration) {
630        faultToleranceConfigurations.put("", configuration);
631    }
632
633    @Override
634    public void setFaultToleranceConfigurations(List<FaultToleranceConfigurationDefinition> configurations) {
635        if (configurations != null) {
636            for (FaultToleranceConfigurationDefinition configuration : configurations) {
637                faultToleranceConfigurations.put(configuration.getId(), configuration);
638            }
639        }
640    }
641
642    @Override
643    public void addFaultToleranceConfiguration(String id, FaultToleranceConfigurationDefinition configuration) {
644        faultToleranceConfigurations.put(id, configuration);
645    }
646
647    @Override
648    public DataFormatDefinition resolveDataFormatDefinition(String name) {
649        // lookup type and create the data format from it
650        DataFormatDefinition type = lookup(camelContext, name, DataFormatDefinition.class);
651        if (type == null && getDataFormats() != null) {
652            type = getDataFormats().get(name);
653        }
654        return type;
655    }
656
657    @SuppressWarnings("rawtypes")
658    @Override
659    public ProcessorDefinition<?> getProcessorDefinition(String id) {
660        for (RouteDefinition route : getRouteDefinitions()) {
661            Collection<ProcessorDefinition> col
662                    = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class);
663            for (ProcessorDefinition proc : col) {
664                if (id.equals(proc.getId())) {
665                    return proc;
666                }
667            }
668        }
669        return null;
670    }
671
672    @Override
673    public <T extends ProcessorDefinition<T>> T getProcessorDefinition(String id, Class<T> type) {
674        ProcessorDefinition<?> answer = getProcessorDefinition(id);
675        if (answer != null) {
676            return type.cast(answer);
677        }
678        return null;
679    }
680
681    @Override
682    public Map<String, DataFormatDefinition> getDataFormats() {
683        return dataFormats;
684    }
685
686    @Override
687    public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) {
688        this.dataFormats = dataFormats;
689    }
690
691    @Override
692    public List<TransformerDefinition> getTransformers() {
693        return transformers;
694    }
695
696    @Override
697    public void setTransformers(List<TransformerDefinition> transformers) {
698        this.transformers = transformers;
699    }
700
701    @Override
702    public List<ValidatorDefinition> getValidators() {
703        return validators;
704    }
705
706    @Override
707    public void setValidators(List<ValidatorDefinition> validators) {
708        this.validators = validators;
709    }
710
711    @Override
712    public void setRouteFilterPattern(String include, String exclude) {
713        setRouteFilter(RouteFilters.filterByPattern(include, exclude));
714    }
715
716    @Override
717    public Function<RouteDefinition, Boolean> getRouteFilter() {
718        return routeFilter;
719    }
720
721    @Override
722    public void setRouteFilter(Function<RouteDefinition, Boolean> routeFilter) {
723        this.routeFilter = routeFilter;
724    }
725
726    @Override
727    public ModelReifierFactory getModelReifierFactory() {
728        return modelReifierFactory;
729    }
730
731    @Override
732    public void setModelReifierFactory(ModelReifierFactory modelReifierFactory) {
733        this.modelReifierFactory = modelReifierFactory;
734    }
735
736    /**
737     * Should we start newly added routes?
738     */
739    protected boolean shouldStartRoutes() {
740        return camelContext.isStarted() && !camelContext.isStarting();
741    }
742
743    private static <T> T lookup(CamelContext context, String ref, Class<T> type) {
744        try {
745            return context.getRegistry().lookupByNameAndType(ref, type);
746        } catch (Exception e) {
747            // need to ignore not same type and return it as null
748            return null;
749        }
750    }
751
752    private static void setPropertiesOnTarget(CamelContext context, Object target, Map<String, Object> properties) {
753        ObjectHelper.notNull(context, "context");
754        ObjectHelper.notNull(target, "target");
755        ObjectHelper.notNull(properties, "properties");
756
757        if (target instanceof CamelContext) {
758            throw new UnsupportedOperationException("Configuring the Camel Context is not supported");
759        }
760
761        PropertyConfigurer configurer = null;
762        if (target instanceof Component) {
763            // the component needs to be initialized to have the configurer ready
764            ServiceHelper.initService(target);
765            configurer = ((Component) target).getComponentPropertyConfigurer();
766        }
767
768        if (configurer == null) {
769            // see if there is a configurer for it
770            configurer = context.adapt(ExtendedCamelContext.class)
771                    .getConfigurerResolver()
772                    .resolvePropertyConfigurer(target.getClass().getSimpleName(), context);
773        }
774
775        try {
776            PropertyBindingSupport.build()
777                    .withMandatory(true)
778                    .withRemoveParameters(false)
779                    .withConfigurer(configurer)
780                    .withIgnoreCase(true)
781                    .withFlattenProperties(true)
782                    .bind(context, target, properties);
783        } catch (PropertyBindingException e) {
784            String key = e.getOptionKey();
785            if (key == null) {
786                String prefix = e.getOptionPrefix();
787                if (prefix != null && !prefix.endsWith(".")) {
788                    prefix = "." + prefix;
789                }
790
791                key = prefix != null
792                        ? prefix + "." + e.getPropertyName()
793                        : e.getPropertyName();
794            }
795
796            // enrich the error with more precise details with option prefix and key
797            throw new PropertyBindingException(
798                    e.getTarget(),
799                    e.getPropertyName(),
800                    e.getValue(),
801                    null,
802                    key,
803                    e.getCause());
804        }
805    }
806
807}