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.List;
023import java.util.Map;
024import java.util.concurrent.atomic.AtomicInteger;
025
026import org.apache.camel.CamelContext;
027import org.apache.camel.Endpoint;
028import org.apache.camel.NoSuchEndpointException;
029import org.apache.camel.Processor;
030import org.apache.camel.Route;
031import org.apache.camel.RuntimeCamelException;
032import org.apache.camel.ShutdownRoute;
033import org.apache.camel.ShutdownRunningTask;
034import org.apache.camel.model.FromDefinition;
035import org.apache.camel.model.ProcessorDefinition;
036import org.apache.camel.model.PropertyDefinition;
037import org.apache.camel.model.RouteDefinition;
038import org.apache.camel.processor.CamelInternalProcessor;
039import org.apache.camel.processor.ContractAdvice;
040import org.apache.camel.processor.Pipeline;
041import org.apache.camel.spi.Contract;
042import org.apache.camel.spi.InterceptStrategy;
043import org.apache.camel.spi.RouteContext;
044import org.apache.camel.spi.RouteController;
045import org.apache.camel.spi.RouteError;
046import org.apache.camel.spi.RoutePolicy;
047import org.apache.camel.util.CamelContextHelper;
048import org.apache.camel.util.ObjectHelper;
049
050/**
051 * The context used to activate new routing rules
052 *
053 * @version 
054 */
055public class DefaultRouteContext implements RouteContext {
056    private final Map<ProcessorDefinition<?>, AtomicInteger> nodeIndex = new HashMap<>();
057    private final RouteDefinition route;
058    private FromDefinition from;
059    private final Collection<Route> routes;
060    private Endpoint endpoint;
061    private final List<Processor> eventDrivenProcessors = new ArrayList<>();
062    private CamelContext camelContext;
063    private List<InterceptStrategy> interceptStrategies = new ArrayList<>();
064    private InterceptStrategy managedInterceptStrategy;
065    private boolean routeAdded;
066    private Boolean trace;
067    private Boolean messageHistory;
068    private Boolean logMask;
069    private Boolean logExhaustedMessageBody;
070    private Boolean streamCache;
071    private Boolean handleFault;
072    private Long delay;
073    private Boolean autoStartup = Boolean.TRUE;
074    private List<RoutePolicy> routePolicyList = new ArrayList<>();
075    private ShutdownRoute shutdownRoute;
076    private ShutdownRunningTask shutdownRunningTask;
077    private RouteError routeError;
078    private RouteController routeController;
079
080    public DefaultRouteContext(CamelContext camelContext, RouteDefinition route, FromDefinition from, Collection<Route> routes) {
081        this.camelContext = camelContext;
082        this.route = route;
083        this.from = from;
084        this.routes = routes;
085    }
086
087    /**
088     * Only used for lazy construction from inside ExpressionType
089     */
090    public DefaultRouteContext(CamelContext camelContext) {
091        this.camelContext = camelContext;
092        this.routes = new ArrayList<>();
093        this.route = new RouteDefinition("temporary");
094    }
095
096    public Endpoint getEndpoint() {
097        if (endpoint == null) {
098            endpoint = from.resolveEndpoint(this);
099        }
100        return endpoint;
101    }
102
103    public FromDefinition getFrom() {
104        return from;
105    }
106
107    public RouteDefinition getRoute() {
108        return route;
109    }
110
111    public CamelContext getCamelContext() {
112        return camelContext;
113    }
114
115    public Endpoint resolveEndpoint(String uri) {
116        return route.resolveEndpoint(getCamelContext(), uri);
117    }
118
119    public Endpoint resolveEndpoint(String uri, String ref) {
120        Endpoint endpoint = null;
121        if (uri != null) {
122            endpoint = resolveEndpoint(uri);
123            if (endpoint == null) {
124                throw new NoSuchEndpointException(uri);
125            }
126        }
127        if (ref != null) {
128            endpoint = lookup(ref, Endpoint.class);
129            if (endpoint == null) {
130                throw new NoSuchEndpointException("ref:" + ref, "check your camel registry with id " + ref);
131            }
132            // Check the endpoint has the right CamelContext 
133            if (!this.getCamelContext().equals(endpoint.getCamelContext())) {
134                throw new NoSuchEndpointException("ref:" + ref, "make sure the endpoint has the same camel context as the route does.");
135            }
136            try {
137                // need add the endpoint into service
138                getCamelContext().addService(endpoint);
139            } catch (Exception ex) {
140                throw new RuntimeCamelException(ex);
141            }
142        }
143        if (endpoint == null) {
144            throw new IllegalArgumentException("Either 'uri' or 'ref' must be specified on: " + this);
145        } else {
146            return endpoint;
147        }
148    }
149
150    public <T> T lookup(String name, Class<T> type) {
151        return getCamelContext().getRegistry().lookupByNameAndType(name, type);
152    }
153
154    public <T> Map<String, T> lookupByType(Class<T> type) {
155        return getCamelContext().getRegistry().findByTypeWithName(type);
156    }
157
158    @Override
159    public <T> T mandatoryLookup(String name, Class<T> type) {
160        return CamelContextHelper.mandatoryLookup(getCamelContext(), name, type);
161    }
162
163    public void commit() {
164        // now lets turn all of the event driven consumer processors into a single route
165        if (!eventDrivenProcessors.isEmpty()) {
166            Processor target = Pipeline.newInstance(getCamelContext(), eventDrivenProcessors);
167
168            // force creating the route id so its known ahead of the route is started
169            String routeId = route.idOrCreate(getCamelContext().getNodeIdFactory());
170
171            // and wrap it in a unit of work so the UoW is on the top, so the entire route will be in the same UoW
172            CamelInternalProcessor internal = new CamelInternalProcessor(target);
173            internal.addAdvice(new CamelInternalProcessor.UnitOfWorkProcessorAdvice(this));
174
175            // and then optionally add route policy processor if a custom policy is set
176            List<RoutePolicy> routePolicyList = getRoutePolicyList();
177            if (routePolicyList != null && !routePolicyList.isEmpty()) {
178                for (RoutePolicy policy : routePolicyList) {
179                    // add policy as service if we have not already done that (eg possible if two routes have the same service)
180                    // this ensures Camel can control the lifecycle of the policy
181                    if (!camelContext.hasService(policy)) {
182                        try {
183                            camelContext.addService(policy);
184                        } catch (Exception e) {
185                            throw ObjectHelper.wrapRuntimeCamelException(e);
186                        }
187                    }
188                }
189
190                internal.addAdvice(new CamelInternalProcessor.RoutePolicyAdvice(routePolicyList));
191            }
192
193            // wrap in route inflight processor to track number of inflight exchanges for the route
194            internal.addAdvice(new CamelInternalProcessor.RouteInflightRepositoryAdvice(camelContext.getInflightRepository(), routeId));
195
196            // wrap in JMX instrumentation processor that is used for performance stats
197            internal.addAdvice(new CamelInternalProcessor.InstrumentationAdvice("route"));
198
199            // wrap in route lifecycle
200            internal.addAdvice(new CamelInternalProcessor.RouteLifecycleAdvice());
201
202            // wrap in REST binding
203            if (route.getRestBindingDefinition() != null) {
204                try {
205                    internal.addAdvice(route.getRestBindingDefinition().createRestBindingAdvice(this));
206                } catch (Exception e) {
207                    throw ObjectHelper.wrapRuntimeCamelException(e);
208                }
209            }
210
211            // wrap in contract
212            if (route.getInputType() != null || route.getOutputType() != null) {
213                Contract contract = new Contract();
214                if (route.getInputType() != null) {
215                    contract.setInputType(route.getInputType().getUrn());
216                    contract.setValidateInput(route.getInputType().isValidate());
217                }
218                if (route.getOutputType() != null) {
219                    contract.setOutputType(route.getOutputType().getUrn());
220                    contract.setValidateOutput(route.getOutputType().isValidate());
221                }
222                internal.addAdvice(new ContractAdvice(contract));
223                // make sure to enable data type as its in use when using input/output types on routes
224                camelContext.setUseDataType(true);
225            }
226
227            // and create the route that wraps the UoW
228            Route edcr = new EventDrivenConsumerRoute(this, getEndpoint(), internal);
229            edcr.getProperties().put(Route.ID_PROPERTY, routeId);
230            edcr.getProperties().put(Route.PARENT_PROPERTY, Integer.toHexString(route.hashCode()));
231            edcr.getProperties().put(Route.DESCRIPTION_PROPERTY, route.getDescriptionText());
232            if (route.getGroup() != null) {
233                edcr.getProperties().put(Route.GROUP_PROPERTY, route.getGroup());
234            }
235            String rest = "false";
236            if (route.isRest() != null && route.isRest()) {
237                rest = "true";
238            }
239            edcr.getProperties().put(Route.REST_PROPERTY, rest);
240
241            List<PropertyDefinition> properties = route.getRouteProperties();
242            if (properties != null) {
243                final String[] reservedProperties = new String[] {
244                    Route.ID_PROPERTY,
245                    Route.PARENT_PROPERTY,
246                    Route.GROUP_PROPERTY,
247                    Route.REST_PROPERTY,
248                    Route.DESCRIPTION_PROPERTY
249                };
250
251                for (PropertyDefinition prop : properties) {
252                    try {
253                        final String key = CamelContextHelper.parseText(camelContext, prop.getKey());
254                        final String val = CamelContextHelper.parseText(camelContext, prop.getValue());
255
256                        for (String property : reservedProperties) {
257                            if (property.equalsIgnoreCase(key)) {
258                                throw new IllegalArgumentException("Cannot set route property " + property + " as it is a reserved property");
259                            }
260                        }
261
262                        edcr.getProperties().put(key, val);
263                    } catch (Exception e) {
264                        throw ObjectHelper.wrapRuntimeCamelException(e);
265                    }
266                }
267            }
268
269            // after the route is created then set the route on the policy processor so we get hold of it
270            CamelInternalProcessor.RoutePolicyAdvice task = internal.getAdvice(CamelInternalProcessor.RoutePolicyAdvice.class);
271            if (task != null) {
272                task.setRoute(edcr);
273            }
274            CamelInternalProcessor.RouteLifecycleAdvice task2 = internal.getAdvice(CamelInternalProcessor.RouteLifecycleAdvice.class);
275            if (task2 != null) {
276                task2.setRoute(edcr);
277            }
278
279            // invoke init on route policy
280            if (routePolicyList != null && !routePolicyList.isEmpty()) {
281                for (RoutePolicy policy : routePolicyList) {
282                    policy.onInit(edcr);
283                }
284            }
285
286            routes.add(edcr);
287        }
288    }
289
290    public void addEventDrivenProcessor(Processor processor) {
291        eventDrivenProcessors.add(processor);
292    }
293
294    public List<InterceptStrategy> getInterceptStrategies() {
295        return interceptStrategies;
296    }
297
298    public void setInterceptStrategies(List<InterceptStrategy> interceptStrategies) {
299        this.interceptStrategies = interceptStrategies;
300    }
301
302    public void addInterceptStrategy(InterceptStrategy interceptStrategy) {
303        getInterceptStrategies().add(interceptStrategy);
304    }
305
306    public void setManagedInterceptStrategy(InterceptStrategy interceptStrategy) {
307        this.managedInterceptStrategy = interceptStrategy;
308    }
309
310    public InterceptStrategy getManagedInterceptStrategy() {
311        return managedInterceptStrategy;
312    }
313
314    public boolean isRouteAdded() {
315        return routeAdded;
316    }
317
318    public void setIsRouteAdded(boolean routeAdded) {
319        this.routeAdded = routeAdded;
320    }
321
322    public void setTracing(Boolean tracing) {
323        this.trace = tracing;
324    }
325
326    public Boolean isTracing() {
327        if (trace != null) {
328            return trace;
329        } else {
330            // fallback to the option from camel context
331            return getCamelContext().isTracing();
332        }
333    }
334
335    public void setMessageHistory(Boolean messageHistory) {
336        this.messageHistory = messageHistory;
337    }
338
339    public Boolean isMessageHistory() {
340        if (messageHistory != null) {
341            return messageHistory;
342        } else {
343            // fallback to the option from camel context
344            return getCamelContext().isMessageHistory();
345        }
346    }
347
348    public void setLogMask(Boolean logMask) {
349        this.logMask = logMask;
350    }
351
352    public Boolean isLogMask() {
353        if (logMask != null) {
354            return logMask;
355        } else {
356            // fallback to the option from camel context
357            return getCamelContext().isLogMask();
358        }
359    }
360
361    public void setLogExhaustedMessageBody(Boolean logExhaustedMessageBody) {
362        this.logExhaustedMessageBody = logExhaustedMessageBody;
363    }
364
365    public Boolean isLogExhaustedMessageBody() {
366        if (logExhaustedMessageBody != null) {
367            return logExhaustedMessageBody;
368        } else {
369            // fallback to the option from camel context
370            return getCamelContext().isLogExhaustedMessageBody();
371        }
372    }
373
374    public void setStreamCaching(Boolean cache) {
375        this.streamCache = cache;
376    }
377
378    public Boolean isStreamCaching() {
379        if (streamCache != null) {
380            return streamCache;
381        } else {
382            // fallback to the option from camel context
383            return getCamelContext().isStreamCaching();
384        }
385    }
386
387    public void setHandleFault(Boolean handleFault) {
388        this.handleFault = handleFault;
389    }
390
391    public Boolean isHandleFault() {
392        if (handleFault != null) {
393            return handleFault;
394        } else {
395            // fallback to the option from camel context
396            return getCamelContext().isHandleFault();
397        }
398    }
399
400    public void setDelayer(Long delay) {
401        this.delay = delay;
402    }
403
404    public Long getDelayer() {
405        if (delay != null) {
406            return delay;
407        } else {
408            // fallback to the option from camel context
409            return getCamelContext().getDelayer();
410        }
411    }
412
413    public void setAutoStartup(Boolean autoStartup) {
414        this.autoStartup = autoStartup;
415    }
416
417    public Boolean isAutoStartup() {
418        if (autoStartup != null) {
419            return autoStartup;
420        }
421        // default to true
422        return true;
423    }
424
425    public void setShutdownRoute(ShutdownRoute shutdownRoute) {
426        this.shutdownRoute = shutdownRoute;
427    }
428
429    public void setAllowUseOriginalMessage(Boolean allowUseOriginalMessage) {
430        // can only be configured on CamelContext
431        getCamelContext().setAllowUseOriginalMessage(allowUseOriginalMessage);
432    }
433
434    public Boolean isAllowUseOriginalMessage() {
435        // can only be configured on CamelContext
436        return getCamelContext().isAllowUseOriginalMessage();
437    }
438
439    public ShutdownRoute getShutdownRoute() {
440        if (shutdownRoute != null) {
441            return shutdownRoute;
442        } else {
443            // fallback to the option from camel context
444            return getCamelContext().getShutdownRoute();
445        }
446    }
447
448    public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
449        this.shutdownRunningTask = shutdownRunningTask;
450    }
451
452    public ShutdownRunningTask getShutdownRunningTask() {
453        if (shutdownRunningTask != null) {
454            return shutdownRunningTask;
455        } else {
456            // fallback to the option from camel context
457            return getCamelContext().getShutdownRunningTask();
458        }
459    }
460    
461    public int getAndIncrement(ProcessorDefinition<?> node) {
462        AtomicInteger count = nodeIndex.get(node);
463        if (count == null) {
464            count = new AtomicInteger();
465            nodeIndex.put(node, count);
466        }
467        return count.getAndIncrement();
468    }
469
470    public void setRoutePolicyList(List<RoutePolicy> routePolicyList) {
471        this.routePolicyList = routePolicyList;
472    }
473
474    public List<RoutePolicy> getRoutePolicyList() {
475        return routePolicyList;
476    }
477
478    @Override
479    public RouteError getLastError() {
480        return routeError;
481    }
482
483    @Override
484    public void setLastError(RouteError routeError) {
485        this.routeError = routeError;
486    }
487
488    @Override
489    public RouteController getRouteController() {
490        return routeController;
491    }
492
493    @Override
494    public void setRouteController(RouteController routeController) {
495        this.routeController = routeController;
496    }
497}