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.health;
018
019import java.util.Collection;
020import java.util.Collections;
021import java.util.Comparator;
022import java.util.Map;
023import java.util.Optional;
024import java.util.function.Function;
025import java.util.stream.Collectors;
026
027import org.apache.camel.CamelContext;
028import org.apache.camel.util.ObjectHelper;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032public final class HealthCheckHelper {
033    private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckHelper.class);
034
035    private HealthCheckHelper() {
036    }
037
038    /**
039     * Get the group of the given check or an empty string if the group is not set.
040     *
041     * @param check the health check
042     * @return the {@link HealthCheck#getGroup()} or an empty string if it is <code>null</code>
043     */
044    public static String getGroup(HealthCheck check) {
045        return ObjectHelper.supplyIfEmpty(check.getGroup(), () -> "");
046    }
047
048    /**
049     * Invokes the checks and returns a collection of results.
050     */
051    public static Collection<HealthCheck.Result> invoke(CamelContext camelContext) {
052        return invoke(camelContext, check -> Collections.emptyMap(), check -> false);
053    }
054
055    /**
056     * Invokes the checks and returns a collection of results.
057     */
058    public static Collection<HealthCheck.Result> invoke(
059            CamelContext camelContext,
060            Function<HealthCheck, Map<String, Object>> optionsSupplier) {
061
062        return invoke(camelContext, optionsSupplier, check -> false);
063    }
064
065    /**
066     * Invokes the checks and returns a collection of results.
067     */
068    public static Collection<HealthCheck.Result> invoke(
069            CamelContext camelContext,
070            HealthCheckFilter filter) {
071
072        return invoke(camelContext, check -> Collections.emptyMap(), filter);
073    }
074
075    /**
076     * Invokes the checks and returns a collection of results.
077     *
078     * @param camelContext the camel context.
079     * @param optionsSupplier a supplier for options.
080     * @param filter filter to exclude some checks.
081     */
082    public static Collection<HealthCheck.Result> invoke(
083            CamelContext camelContext,
084            Function<HealthCheck, Map<String, Object>> optionsSupplier,
085            HealthCheckFilter filter) {
086
087        final HealthCheckRegistry registry = HealthCheckRegistry.get(camelContext);
088        final HealthCheckService service = camelContext.hasService(HealthCheckService.class);
089
090        if (service != null) {
091            // If a health check service is defined retrieve the current status
092            // of the checks hold by the service.
093            return service.getResults().stream()
094                .filter(result -> !filter.test(result.getCheck()))
095                .collect(Collectors.toList());
096        } else if (registry != null) {
097            // If no health check service is defined, this endpoint invokes the
098            // check one by one.
099            return registry.stream()
100                .collect(Collectors.groupingBy(HealthCheckHelper::getGroup))
101                .entrySet().stream()
102                    .map(Map.Entry::getValue)
103                    .flatMap(Collection::stream)
104                    .filter(check -> !filter.test(check))
105                    .sorted(Comparator.comparingInt(HealthCheck::getOrder))
106                    .map(check -> check.call(optionsSupplier.apply(check)))
107                    .collect(Collectors.toList());
108        } else {
109            LOGGER.debug("No health check source found");
110        }
111
112        return Collections.emptyList();
113    }
114
115    /**
116     * Query the status of a check by id. Note that this may result in an effective
117     * invocation of the {@link HealthCheck}, i.e. when no {@link HealthCheckService}
118     * is available.
119     *
120     * @param camelContext the camel context.
121     * @param id the check id.
122     * @param options the check options.
123     * @return an optional {@link HealthCheck.Result}.
124     */
125    public static Optional<HealthCheck.Result> query(CamelContext camelContext, String id, Map<String, Object> options) {
126        final HealthCheckRegistry registry = HealthCheckRegistry.get(camelContext);
127        final HealthCheckService service = camelContext.hasService(HealthCheckService.class);
128
129        if (service != null) {
130            return service.getResults().stream()
131                .filter(result -> ObjectHelper.equal(result.getCheck().getId(), id))
132                .findFirst();
133        } else if (registry != null) {
134            return registry.getCheck(id).map(check -> check.call(options));
135        } else {
136            LOGGER.debug("No health check source found");
137        }
138
139        return Optional.empty();
140    }
141
142    /**
143     * Invoke a check by id.
144     *
145     * @param camelContext the camel context.
146     * @param id the check id.
147     * @param options the check options.
148     * @return an optional {@link HealthCheck.Result}.
149     */
150    public static Optional<HealthCheck.Result> invoke(CamelContext camelContext, String id, Map<String, Object> options) {
151        final HealthCheckRegistry registry = HealthCheckRegistry.get(camelContext);
152        final HealthCheckService service = camelContext.hasService(HealthCheckService.class);
153
154        if (service != null) {
155            return service.call(id, options);
156        } else if (registry != null) {
157            return registry.getCheck(id).map(check -> check.call(options));
158        } else {
159            LOGGER.debug("No health check source found");
160        }
161
162        return Optional.empty();
163    }
164}