001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.impl;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026import java.util.concurrent.ConcurrentHashMap;
027import java.util.function.Function;
028
029import org.apache.camel.CamelContext;
030import org.apache.camel.ExtendedCamelContext;
031import org.apache.camel.FailedToStartRouteException;
032import org.apache.camel.Route;
033import org.apache.camel.impl.engine.AbstractCamelContext;
034import org.apache.camel.impl.engine.DefaultRouteContext;
035import org.apache.camel.model.DataFormatDefinition;
036import org.apache.camel.model.HystrixConfigurationDefinition;
037import org.apache.camel.model.Model;
038import org.apache.camel.model.ProcessorDefinition;
039import org.apache.camel.model.ProcessorDefinitionHelper;
040import org.apache.camel.model.Resilience4jConfigurationDefinition;
041import org.apache.camel.model.RouteDefinition;
042import org.apache.camel.model.RouteDefinitionHelper;
043import org.apache.camel.model.RouteFilters;
044import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
045import org.apache.camel.model.rest.RestDefinition;
046import org.apache.camel.model.transformer.TransformerDefinition;
047import org.apache.camel.model.validator.ValidatorDefinition;
048import org.apache.camel.reifier.RouteReifier;
049import org.apache.camel.spi.RouteContext;
050
051public class DefaultModel implements Model {
052
053    private final CamelContext camelContext;
054
055    private final List<RouteDefinition> routeDefinitions = new ArrayList<>();
056    private final List<RestDefinition> restDefinitions = new ArrayList<>();
057    private Map<String, DataFormatDefinition> dataFormats = new HashMap<>();
058    private List<TransformerDefinition> transformers = new ArrayList<>();
059    private List<ValidatorDefinition> validators = new ArrayList<>();
060    private Map<String, ServiceCallConfigurationDefinition> serviceCallConfigurations = new ConcurrentHashMap<>();
061    private Map<String, HystrixConfigurationDefinition> hystrixConfigurations = new ConcurrentHashMap<>();
062    private Map<String, Resilience4jConfigurationDefinition> resilience4jConfigurations = new ConcurrentHashMap<>();
063    private Function<RouteDefinition, Boolean> routeFilter;
064
065    public DefaultModel(CamelContext camelContext) {
066        this.camelContext = camelContext;
067    }
068
069    public CamelContext getCamelContext() {
070        return camelContext;
071    }
072
073    @Override
074    public synchronized void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
075        if (routeDefinitions == null || routeDefinitions.isEmpty()) {
076            return;
077        }
078        List<RouteDefinition> list = new ArrayList<>();
079        routeDefinitions.forEach(r -> {
080            if (routeFilter == null || routeFilter.apply(r)) {
081                list.add(r);
082            }
083        });
084
085        removeRouteDefinitions(list);
086        this.routeDefinitions.addAll(list);
087        if (shouldStartRoutes()) {
088            startRouteDefinitions(list);
089        }
090    }
091
092    @Override
093    public void addRouteDefinition(RouteDefinition routeDefinition) throws Exception {
094        addRouteDefinitions(Collections.singletonList(routeDefinition));
095    }
096
097    @Override
098    public synchronized void removeRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
099        for (RouteDefinition routeDefinition : routeDefinitions) {
100            removeRouteDefinition(routeDefinition);
101        }
102    }
103
104    @Override
105    public synchronized void removeRouteDefinition(RouteDefinition routeDefinition) throws Exception {
106        RouteDefinition toBeRemoved = routeDefinition;
107        String id = routeDefinition.getId();
108        if (id != null) {
109            // remove existing route
110            camelContext.getRouteController().stopRoute(id);
111            camelContext.removeRoute(id);
112            toBeRemoved = getRouteDefinition(id);
113        }
114        this.routeDefinitions.remove(toBeRemoved);
115    }
116
117    @Override
118    public synchronized List<RouteDefinition> getRouteDefinitions() {
119        return routeDefinitions;
120    }
121
122    @Override
123    public synchronized RouteDefinition getRouteDefinition(String id) {
124        for (RouteDefinition route : routeDefinitions) {
125            if (route.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory()).equals(id)) {
126                return route;
127            }
128        }
129        return null;
130    }
131
132    @Override
133    public synchronized List<RestDefinition> getRestDefinitions() {
134        return restDefinitions;
135    }
136
137    @Override
138    public synchronized void addRestDefinitions(Collection<RestDefinition> restDefinitions, boolean addToRoutes) throws Exception {
139        if (restDefinitions == null || restDefinitions.isEmpty()) {
140            return;
141        }
142
143        this.restDefinitions.addAll(restDefinitions);
144        if (addToRoutes) {
145            // rests are also routes so need to add them there too
146            for (final RestDefinition restDefinition : restDefinitions) {
147                List<RouteDefinition> routeDefinitions = restDefinition.asRouteDefinition(camelContext);
148                addRouteDefinitions(routeDefinitions);
149            }
150        }
151    }
152
153    @Override
154    public ServiceCallConfigurationDefinition getServiceCallConfiguration(String serviceName) {
155        if (serviceName == null) {
156            serviceName = "";
157        }
158
159        return serviceCallConfigurations.get(serviceName);
160    }
161
162    @Override
163    public void setServiceCallConfiguration(ServiceCallConfigurationDefinition configuration) {
164        serviceCallConfigurations.put("", configuration);
165    }
166
167    @Override
168    public void setServiceCallConfigurations(List<ServiceCallConfigurationDefinition> configurations) {
169        if (configurations != null) {
170            for (ServiceCallConfigurationDefinition configuration : configurations) {
171                serviceCallConfigurations.put(configuration.getId(), configuration);
172            }
173        }
174    }
175
176    @Override
177    public void addServiceCallConfiguration(String serviceName, ServiceCallConfigurationDefinition configuration) {
178        serviceCallConfigurations.put(serviceName, configuration);
179    }
180
181    @Override
182    public HystrixConfigurationDefinition getHystrixConfiguration(String id) {
183        if (id == null) {
184            id = "";
185        }
186
187        return hystrixConfigurations.get(id);
188    }
189
190    @Override
191    public void setHystrixConfiguration(HystrixConfigurationDefinition configuration) {
192        hystrixConfigurations.put("", configuration);
193    }
194
195    @Override
196    public void setHystrixConfigurations(List<HystrixConfigurationDefinition> configurations) {
197        if (configurations != null) {
198            for (HystrixConfigurationDefinition configuration : configurations) {
199                hystrixConfigurations.put(configuration.getId(), configuration);
200            }
201        }
202    }
203
204    @Override
205    public void addHystrixConfiguration(String id, HystrixConfigurationDefinition configuration) {
206        hystrixConfigurations.put(id, configuration);
207    }
208
209    @Override
210    public Resilience4jConfigurationDefinition getResilience4jConfiguration(String id) {
211        if (id == null) {
212            id = "";
213        }
214
215        return resilience4jConfigurations.get(id);
216    }
217
218    @Override
219    public void setResilience4jConfiguration(Resilience4jConfigurationDefinition configuration) {
220        resilience4jConfigurations.put("", configuration);
221    }
222
223    @Override
224    public void setResilience4jConfigurations(List<Resilience4jConfigurationDefinition> configurations) {
225        if (configurations != null) {
226            for (Resilience4jConfigurationDefinition configuration : configurations) {
227                resilience4jConfigurations.put(configuration.getId(), configuration);
228            }
229        }
230    }
231
232    @Override
233    public void addResilience4jConfiguration(String id, Resilience4jConfigurationDefinition configuration) {
234        resilience4jConfigurations.put(id, configuration);
235    }
236
237    @Override
238    public DataFormatDefinition resolveDataFormatDefinition(String name) {
239        // lookup type and create the data format from it
240        DataFormatDefinition type = lookup(camelContext, name, DataFormatDefinition.class);
241        if (type == null && getDataFormats() != null) {
242            type = getDataFormats().get(name);
243        }
244        return type;
245    }
246
247    @Override
248    public ProcessorDefinition getProcessorDefinition(String id) {
249        for (RouteDefinition route : getRouteDefinitions()) {
250            Iterator<ProcessorDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class);
251            while (it.hasNext()) {
252                ProcessorDefinition proc = it.next();
253                if (id.equals(proc.getId())) {
254                    return proc;
255                }
256            }
257        }
258        return null;
259    }
260
261    @Override
262    public <T extends ProcessorDefinition> T getProcessorDefinition(String id, Class<T> type) {
263        ProcessorDefinition answer = getProcessorDefinition(id);
264        if (answer != null) {
265            return type.cast(answer);
266        }
267        return null;
268    }
269
270    @Override
271    public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) {
272        this.dataFormats = dataFormats;
273    }
274
275    @Override
276    public Map<String, DataFormatDefinition> getDataFormats() {
277        return dataFormats;
278    }
279
280    @Override
281    public void setTransformers(List<TransformerDefinition> transformers) {
282        this.transformers = transformers;
283    }
284
285    @Override
286    public List<TransformerDefinition> getTransformers() {
287        return transformers;
288    }
289
290    @Override
291    public void setValidators(List<ValidatorDefinition> validators) {
292        this.validators = validators;
293    }
294
295    @Override
296    public List<ValidatorDefinition> getValidators() {
297        return validators;
298    }
299
300    @Override
301    public void startRouteDefinitions() throws Exception {
302        startRouteDefinitions(routeDefinitions);
303    }
304
305    @Override
306    public void setRouteFilterPattern(String include, String exclude) {
307        setRouteFilter(RouteFilters.filterByPattern(include, exclude));
308    }
309
310    @Override
311    public Function<RouteDefinition, Boolean> getRouteFilter() {
312        return routeFilter;
313    }
314
315    @Override
316    public void setRouteFilter(Function<RouteDefinition, Boolean> routeFilter) {
317        this.routeFilter = routeFilter;
318    }
319
320    protected void startRouteDefinitions(Collection<RouteDefinition> list) throws Exception {
321        if (list != null) {
322            for (RouteDefinition route : list) {
323                startRoute(route);
324            }
325        }
326    }
327
328    public void startRoute(RouteDefinition routeDefinition) throws Exception {
329        prepare(routeDefinition);
330        start(routeDefinition);
331    }
332
333    protected void prepare(RouteDefinition routeDefinition) throws Exception {
334        // assign ids to the routes and validate that the id's is all unique
335        RouteDefinitionHelper.forceAssignIds(camelContext, routeDefinitions);
336        String duplicate = RouteDefinitionHelper.validateUniqueIds(routeDefinition, routeDefinitions);
337        if (duplicate != null) {
338            throw new FailedToStartRouteException(routeDefinition.getId(), "duplicate id detected: " + duplicate + ". Please correct ids to be unique among all your routes.");
339        }
340
341        // must ensure route is prepared, before we can start it
342        if (!routeDefinition.isPrepared()) {
343            RouteDefinitionHelper.prepareRoute(camelContext, routeDefinition);
344            routeDefinition.markPrepared();
345        }
346    }
347
348    protected void start(RouteDefinition routeDefinition) throws Exception {
349        // indicate we are staring the route using this thread so
350        // we are able to query this if needed
351        AbstractCamelContext mcc = camelContext.adapt(AbstractCamelContext.class);
352        mcc.setStartingRoutes(true);
353        try {
354            String id = routeDefinition.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory());
355            RouteContext routeContext = new DefaultRouteContext(camelContext, routeDefinition, id);
356            Route route = new RouteReifier(routeContext, routeDefinition).createRoute();
357            RouteService routeService = new RouteService(route);
358            mcc.startRouteService(routeService, true);
359        } finally {
360            // we are done staring routes
361            mcc.setStartingRoutes(false);
362        }
363    }
364
365    /**
366     * Should we start newly added routes?
367     */
368    protected boolean shouldStartRoutes() {
369        return camelContext.isStarted() && !camelContext.isStarting();
370    }
371
372    protected static <T> T lookup(CamelContext context, String ref, Class<T> type) {
373        try {
374            return context.getRegistry().lookupByNameAndType(ref, type);
375        } catch (Exception e) {
376            // need to ignore not same type and return it as null
377            return null;
378        }
379    }
380
381}