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