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