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.health;
018
019import java.util.Collection;
020import java.util.Collections;
021import java.util.HashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.concurrent.ConcurrentMap;
027import java.util.concurrent.CopyOnWriteArrayList;
028import java.util.stream.Stream;
029
030import org.apache.camel.CamelContext;
031import org.apache.camel.CamelContextAware;
032import org.apache.camel.DeferredContextBinding;
033import org.apache.camel.Route;
034import org.apache.camel.api.management.mbean.ManagedRouteMBean;
035import org.apache.camel.health.HealthCheck;
036import org.apache.camel.health.HealthCheckRepository;
037
038@DeferredContextBinding
039public class RoutesHealthCheckRepository implements CamelContextAware, HealthCheckRepository {
040    private final ConcurrentMap<Route, HealthCheck> checks;
041    private Set<String> blacklist;
042    private List<PerformanceCounterEvaluator<ManagedRouteMBean>> evaluators;
043    private ConcurrentMap<String, Collection<PerformanceCounterEvaluator<ManagedRouteMBean>>> evaluatorMap;
044    private volatile CamelContext context;
045
046    public RoutesHealthCheckRepository() {
047        this.checks = new ConcurrentHashMap<>();
048    }
049
050    @Override
051    public void setCamelContext(CamelContext camelContext) {
052        this.context = camelContext;
053    }
054
055    @Override
056    public CamelContext getCamelContext() {
057        return context;
058    }
059
060    public void setBlacklistedRoutes(Collection<String> blacklistedRoutes) {
061        blacklistedRoutes.forEach(this::addBlacklistedRoute);
062    }
063
064    public void addBlacklistedRoute(String routeId) {
065        if (this.blacklist == null) {
066            this.blacklist = new HashSet<>();
067        }
068
069        this.blacklist.add(routeId);
070    }
071
072    public void setEvaluators(Collection<PerformanceCounterEvaluator<ManagedRouteMBean>> evaluators) {
073        evaluators.forEach(this::addEvaluator);
074    }
075
076    public void addEvaluator(PerformanceCounterEvaluator<ManagedRouteMBean> evaluator) {
077        if (this.evaluators == null) {
078            this.evaluators = new CopyOnWriteArrayList<>();
079        }
080
081        this.evaluators.add(evaluator);
082    }
083
084    public void setRoutesEvaluators(Map<String, Collection<PerformanceCounterEvaluator<ManagedRouteMBean>>> evaluators) {
085        evaluators.forEach(this::setRouteEvaluators);
086    }
087
088    public void setRouteEvaluators(String routeId, Collection<PerformanceCounterEvaluator<ManagedRouteMBean>> evaluators) {
089        evaluators.forEach(evaluator -> addRouteEvaluator(routeId, evaluator));
090    }
091
092    public void addRouteEvaluator(String routeId, PerformanceCounterEvaluator<ManagedRouteMBean> evaluator) {
093        if (this.evaluatorMap == null) {
094            this.evaluatorMap = new ConcurrentHashMap<>();
095        }
096
097        this.evaluatorMap.computeIfAbsent(routeId, id -> new CopyOnWriteArrayList<>()).add(evaluator);
098    }
099
100    public Stream<PerformanceCounterEvaluator<ManagedRouteMBean>> evaluators() {
101        return this.evaluators != null
102            ? this.evaluators.stream()
103            : Stream.empty();
104    }
105
106    public Stream<PerformanceCounterEvaluator<ManagedRouteMBean>> evaluators(String routeId) {
107        return this.evaluatorMap != null
108            ? evaluatorMap.getOrDefault(routeId, Collections.emptyList()).stream()
109            : Stream.empty();
110    }
111
112    @Override
113    public Stream<HealthCheck> stream() {
114        // This is not really efficient as getRoutes() creates a copy of the routes
115        // array for each invocation. It would be nice to have more stream oriented
116        // operation on CamelContext i.e.
117        //
118        // interface CamelContext {
119        //
120        //     Stream<Route> routes();
121        //
122        //     void forEachRoute(Consumer<Route> consumer);
123        // }
124        //
125        return this.context != null
126            ? this.context.getRoutes()
127                .stream()
128                .filter(route -> route.getId() != null)
129                .filter(route -> isNotBlacklisted(route))
130                .map(this::toRouteHealthCheck)
131            : Stream.empty();
132    }
133
134    // *****************************
135    // Helpers
136    // *****************************
137
138    private boolean isNotBlacklisted(Route route) {
139        return this.blacklist != null
140            ? !this.blacklist.contains(route.getId())
141            : true;
142    }
143
144    private HealthCheck toRouteHealthCheck(Route route) {
145        return checks.computeIfAbsent(
146            route,
147            r -> {
148                HealthCheck check = new RouteHealthCheck(
149                    route,
150                    evaluatorMap != null
151                        ? evaluatorMap.getOrDefault(r.getId(), evaluators)
152                        : evaluators
153                );
154
155                check.getConfiguration().setEnabled(true);
156
157                return check;
158            }
159        );
160    }
161}