All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.impl.engine.RouteService Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.impl.engine;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Channel;
import org.apache.camel.Consumer;
import org.apache.camel.Endpoint;
import org.apache.camel.EndpointAware;
import org.apache.camel.FailedToStartRouteException;
import org.apache.camel.Processor;
import org.apache.camel.Route;
import org.apache.camel.RouteAware;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.Service;
import org.apache.camel.StartupStep;
import org.apache.camel.spi.IdAware;
import org.apache.camel.spi.InternalProcessor;
import org.apache.camel.spi.LifecycleStrategy;
import org.apache.camel.spi.RouteIdAware;
import org.apache.camel.spi.RoutePolicy;
import org.apache.camel.spi.StartupStepRecorder;
import org.apache.camel.support.ChildServiceSupport;
import org.apache.camel.support.EventHelper;
import org.apache.camel.support.service.ServiceHelper;
import org.slf4j.MDC;

import static org.apache.camel.spi.UnitOfWork.MDC_CAMEL_CONTEXT_ID;
import static org.apache.camel.spi.UnitOfWork.MDC_ROUTE_ID;

/**
 * Represents the runtime objects for a given route so that it can be stopped independently of other routes
 */
public class RouteService extends ChildServiceSupport {

    private final CamelContext camelContext;
    private final StartupStepRecorder startupStepRecorder;
    private final Route route;
    private boolean removingRoutes;
    private Consumer input;
    private final Lock lock = new ReentrantLock();
    private final AtomicBoolean setUpDone = new AtomicBoolean();
    private final AtomicBoolean warmUpDone = new AtomicBoolean();
    private final AtomicBoolean endpointDone = new AtomicBoolean();

    public RouteService(Route route) {
        this.route = route;
        this.camelContext = this.route.getCamelContext();
        this.startupStepRecorder = this.camelContext.getCamelContextExtension().getStartupStepRecorder();
    }

    public String getId() {
        return route.getId();
    }

    public CamelContext getCamelContext() {
        return camelContext;
    }

    public Route getRoute() {
        return route;
    }

    /**
     * Gather all the endpoints this route service uses
     * 

* This implementation finds the endpoints by searching all the child services for {@link EndpointAware} processors * which uses an endpoint. */ public Set gatherEndpoints() { Set answer = new LinkedHashSet<>(); Set services = gatherChildServices(); for (Service service : services) { if (service instanceof EndpointAware endpointAware) { Endpoint endpoint = endpointAware.getEndpoint(); if (endpoint != null) { answer.add(endpoint); } } } return answer; } public Consumer getInput() { return input; } public boolean isRemovingRoutes() { return removingRoutes; } public void setRemovingRoutes(boolean removingRoutes) { this.removingRoutes = removingRoutes; } public void warmUp() throws FailedToStartRouteException { try { doWarmUp(); } catch (Exception e) { throw new FailedToStartRouteException(getId(), e.getLocalizedMessage(), e); } } public void setUp() throws FailedToStartRouteException { if (setUpDone.compareAndSet(false, true)) { try { doSetup(); } catch (Exception e) { throw new FailedToStartRouteException(getId(), e.getLocalizedMessage(), e); } } } public boolean isAutoStartup() { if (!getCamelContext().isAutoStartup()) { return false; } return getRoute().isAutoStartup(); } protected void doSetup() throws Exception { lock.lock(); try { // to setup we initialize the services ServiceHelper.initService(route.getEndpoint()); try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { // ensure services are initialized first route.initializeServices(); List services = route.getServices(); // split into consumers and child services as we need to start the consumers // afterwards to avoid them being active while the others start List list = new ArrayList<>(); for (Service service : services) { // inject the route if (service instanceof RouteAware routeAware) { routeAware.setRoute(route); } if (service instanceof RouteIdAware routeIdAware) { routeIdAware.setRouteId(route.getId()); } // inject camel context CamelContextAware.trySetCamelContext(service, camelContext); if (service instanceof Consumer consumer) { this.input = consumer; } else { list.add(service); } } initChildServices(list); } } finally { lock.unlock(); } } protected void doWarmUp() throws Exception { lock.lock(); try { if (endpointDone.compareAndSet(false, true)) { // endpoints should only be started once as they can be reused on other routes // and whatnot, thus their lifecycle is to start once, and only to stop when Camel shutdown // ensure endpoint is started first (before the route services, such as the consumer) ServiceHelper.startService(route.getEndpoint()); } if (warmUpDone.compareAndSet(false, true)) { try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { // warm up the route first route.warmUp(); startChildServices(route, childServices); // fire event EventHelper.notifyRouteAdded(camelContext, route); } // ensure lifecycle strategy is invoked which among others enlist the route in JMX for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { strategy.onRoutesAdd(Collections.singletonList(route)); } // add routes to camel context camelContext.getCamelContextExtension().addRoute(route); // add the routes to the inflight registry so they are pre-installed camelContext.getInflightRepository().addRoute(route.getId()); } } finally { lock.unlock(); } } @Override protected void doStart() { try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { // fire event EventHelper.notifyRouteStarting(camelContext, route); } try { // ensure we are warmed up warmUp(); } catch (FailedToStartRouteException e) { throw RuntimeCamelException.wrapRuntimeException(e); } try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { // start the route itself ServiceHelper.startService(route); // invoke callbacks on route policy routePolicyCallback(RoutePolicy::onStart); // fire event EventHelper.notifyRouteStarted(camelContext, route); } } @Override protected void doStop() { try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { // fire event EventHelper.notifyRouteStopping(camelContext, route); } // if we are stopping CamelContext then we are shutting down boolean isShutdownCamelContext = camelContext.isStopping(); if (isShutdownCamelContext || isRemovingRoutes()) { // need to call onRoutesRemove when the CamelContext is shutting down or Route is shutdown for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { strategy.onRoutesRemove(Collections.singletonList(route)); } } try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { // gather list of services to stop Set services = gatherChildServices(); // stop services stopChildServices(route, services, isShutdownCamelContext); // stop the route itself if (isShutdownCamelContext) { ServiceHelper.stopAndShutdownServices(route); } else { ServiceHelper.stopService(route); } // invoke callbacks on route policy routePolicyCallback(RoutePolicy::onStop); // fire event EventHelper.notifyRouteStopped(camelContext, route); } if (isRemovingRoutes()) { camelContext.getCamelContextExtension().removeRoute(route); } // need to redo if we start again after being stopped input = null; childServices = null; warmUpDone.set(false); setUpDone.set(false); endpointDone.set(false); setUpDone.set(false); warmUpDone.set(false); } @Override protected void doShutdown() { try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { // gather list of services to shutdown Set services = gatherChildServices(); // shutdown services stopChildServices(route, services, true); // shutdown the route itself ServiceHelper.stopAndShutdownServices(route); // endpoints should only be stopped when Camel is shutting down // see more details in the warmUp method ServiceHelper.stopAndShutdownServices(route.getEndpoint()); // invoke callbacks on route policy routePolicyCallback(RoutePolicy::onRemove); // fire event EventHelper.notifyRouteRemoved(camelContext, route); } // need to call onRoutesRemove when the CamelContext is shutting down or Route is shutdown for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { strategy.onRoutesRemove(Collections.singletonList(route)); } // remove the routes from the inflight registry camelContext.getInflightRepository().removeRoute(route.getId()); // remove the routes from the collections camelContext.getCamelContextExtension().removeRoute(route); // clear inputs on shutdown input = null; childServices = null; warmUpDone.set(false); setUpDone.set(false); endpointDone.set(false); } @Override protected void doSuspend() { // suspend and resume logic is provided by DefaultCamelContext which leverages ShutdownStrategy // to safely suspend and resume try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { routePolicyCallback(RoutePolicy::onSuspend); } } @Override protected void doResume() { // suspend and resume logic is provided by DefaultCamelContext which leverages ShutdownStrategy // to safely suspend and resume try (MDCHelper mdcHelper = new MDCHelper(route.getId())) { routePolicyCallback(RoutePolicy::onResume); } } private void routePolicyCallback(java.util.function.BiConsumer callback) { if (route.getRoutePolicyList() != null) { for (RoutePolicy routePolicy : route.getRoutePolicyList()) { callback.accept(routePolicy, route); } } } private StartupStep beginStep(Service service, String description) { Class type = service instanceof Processor ? Processor.class : Service.class; description = description + " " + service.getClass().getSimpleName(); String id = null; if (service instanceof IdAware idAware) { id = idAware.getId(); } return startupStepRecorder.beginStep(type, id, description); } protected void initChildServices(List services) { for (Service service : services) { StartupStep step = null; // skip internal services / route pipeline (starting point for route) boolean shouldRecord = !(service instanceof InternalProcessor || "RoutePipeline".equals(service.getClass().getSimpleName())); if (shouldRecord) { step = beginStep(service, "Init"); } ServiceHelper.initService(service); if (step != null) { startupStepRecorder.endStep(step); } // add and remember as child service addChildService(service); } } protected void startChildServices(Route route, List services) { for (Service service : services) { StartupStep step = null; // skip internal services / route pipeline (starting point for route) boolean shouldRecord = !(service instanceof InternalProcessor || "RoutePipeline".equals(service.getClass().getSimpleName())); if (shouldRecord) { step = beginStep(service, "Start"); } for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { strategy.onServiceAdd(camelContext, service, route); } ServiceHelper.startService(service); if (step != null) { startupStepRecorder.endStep(step); } } } protected void stopChildServices(Route route, Set services, boolean shutdown) { for (Service service : services) { for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { strategy.onServiceRemove(camelContext, service, route); } if (shutdown) { ServiceHelper.stopAndShutdownService(service); } else { ServiceHelper.stopService(service); } removeChildService(service); } } /** * Gather all child services */ private Set gatherChildServices() { List services = new ArrayList<>(route.getServices()); // also get route scoped services doGetRouteServices(services); Set list = new LinkedHashSet<>(); for (Service service : services) { list.addAll(ServiceHelper.getChildServices(service)); } // also get route scoped error handler (which must be done last) doGetErrorHandler(list); return list; } /** * Gather the route scoped error handler from the given route */ private void doGetErrorHandler(Set services) { // only include error handlers if they are route scoped List extra = new ArrayList<>(); for (Service service : services) { if (service instanceof Channel channel) { Processor eh = channel.getErrorHandler(); if (eh instanceof Service s) { extra.add(s); } } } if (!extra.isEmpty()) { services.addAll(extra); } } /** * Gather all other kind of route services from the given route, except error handler */ protected void doGetRouteServices(List services) { for (Processor proc : getRoute().getOnExceptions()) { if (proc instanceof Service service) { services.add(service); } } for (Processor proc : getRoute().getOnCompletions()) { if (proc instanceof Service service) { services.add(service); } } } class MDCHelper implements AutoCloseable { final Map originalContextMap; MDCHelper(String routeId) { if (getCamelContext().isUseMDCLogging()) { originalContextMap = MDC.getCopyOfContextMap(); MDC.put(MDC_CAMEL_CONTEXT_ID, getCamelContext().getName()); MDC.put(MDC_ROUTE_ID, routeId); } else { originalContextMap = null; } } @Override public void close() { if (getCamelContext().isUseMDCLogging()) { if (originalContextMap != null) { MDC.setContextMap(originalContextMap); } else { MDC.clear(); } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy