be.ordina.msdashboard.aggregators.health.HealthIndicatorsAggregator.java Source code

Java tutorial

Introduction

Here is the source code for be.ordina.msdashboard.aggregators.health.HealthIndicatorsAggregator.java

Source

/*
 * Copyright 2012-2016 the original author or authors.
 *
 * Licensed 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 be.ordina.msdashboard.aggregators.health;

import be.ordina.msdashboard.aggregators.NodeAggregator;
import be.ordina.msdashboard.events.NodeEvent;
import be.ordina.msdashboard.events.SystemEvent;
import be.ordina.msdashboard.model.Node;
import be.ordina.msdashboard.uriresolvers.UriResolver;
import io.netty.buffer.ByteBuf;
import io.reactivex.netty.RxNetty;
import io.reactivex.netty.protocol.http.AbstractHttpContentHolder;
import io.reactivex.netty.protocol.http.client.HttpClientRequest;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.json.JacksonJsonParser;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ApplicationEventPublisher;
import rx.Observable;
import rx.functions.Action1;
import rx.schedulers.Schedulers;

import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Map.Entry;
import java.util.Objects;

import static be.ordina.msdashboard.constants.Constants.*;

/**
 * @author Andreas Evers
 */
public class HealthIndicatorsAggregator implements NodeAggregator {

    protected static final String ZUUL_ID = "zuul";

    private static final Logger logger = LoggerFactory.getLogger(HealthIndicatorsAggregator.class);

    private DiscoveryClient discoveryClient;
    private UriResolver uriResolver;
    private HealthProperties properties;
    private ApplicationEventPublisher publisher;

    public HealthIndicatorsAggregator(final DiscoveryClient discoveryClient, final UriResolver uriResolver,
            final HealthProperties properties, final ApplicationEventPublisher publisher) {
        this.discoveryClient = discoveryClient;
        this.uriResolver = uriResolver;
        this.properties = properties;
        this.publisher = publisher;
    }

    //TODO: Caching
    //@Cacheable(value = Constants.HEALTH_CACHE_NAME, keyGenerator = "simpleKeyGenerator")
    @Override
    public Observable<Node> aggregateNodes() {
        Observable<Observable<Node>> observableObservable = getServiceIdsFromDiscoveryClient()
                .map(id -> new ImmutablePair<String, String>(id,
                        uriResolver.resolveHealthCheckUrl(discoveryClient.getInstances(id).get(0))))
                .doOnNext(pair -> logger.info("Creating health observable: " + pair))
                .map(pair -> getHealthNodesFromService(pair.getLeft(), pair.getRight()))
                .doOnNext(el -> logger.debug("Unmerged health observable: " + el))
                .doOnCompleted(() -> logger.info("Completed getting all health observables"));
        return Observable.merge(observableObservable)
                .doOnNext(el -> logger.debug("Merged health node: " + el.getId()))
                .doOnCompleted(() -> logger.info("Completed merging all health observables"));
    }

    private Observable<String> getServiceIdsFromDiscoveryClient() {
        logger.info("Discovering services for health");
        return Observable.from(discoveryClient.getServices()).subscribeOn(Schedulers.io()).doOnError(e -> {
            String error = "Error retrieving services: " + e.getMessage();
            logger.error(error);
            publisher.publishEvent(new SystemEvent(error, e));
        }).onErrorResumeNext(Observable.empty()).doOnNext(s -> logger.debug("Service discovered: " + s))
                .map(id -> id.toLowerCase()).filter(id -> !id.equals(ZUUL_ID));
    }

    //TODO: Add hook for properties on GET (e.g. globalId)
    private Observable<Node> getHealthNodesFromService(String serviceId, String url) {
        HttpClientRequest<ByteBuf> request = HttpClientRequest.createGet(url);
        for (Entry<String, String> header : properties.getRequestHeaders().entrySet()) {
            request.withHeader(header.getKey(), header.getValue());
        }
        return RxNetty.createHttpRequest(request).doOnError(el -> {
            String error = MessageFormat.format("Error retrieving healthnodes in url {} with headers {}: {}",
                    request.getUri(), request.getHeaders().entries(), el);
            logger.error(error);
            publisher.publishEvent(new NodeEvent(serviceId, error));
        }).onErrorResumeNext(Observable.empty()).filter(r -> {
            if (r.getStatus().code() < 400) {
                return true;
            } else {
                String warning = "Exception " + r.getStatus() + " for call " + url + " with headers "
                        + r.getHeaders().entries();
                logger.warn(warning);
                publisher.publishEvent(new NodeEvent(serviceId, warning));
                return false;
            }
        }).flatMap(AbstractHttpContentHolder::getContent).map(data -> data.toString(Charset.defaultCharset()))
                .map(response -> {
                    JacksonJsonParser jsonParser = new JacksonJsonParser();
                    return jsonParser.parseMap(response);
                }).map((source) -> HealthToNodeConverter.convertToNodes(serviceId, source)).flatMap(el -> el)
                .filter(node -> !HYSTRIX.equals(node.getId()) && !DISK_SPACE.equals(node.getId())
                        && !DISCOVERY.equals(node.getId()) && !CONFIGSERVER.equals(node.getId()))
                //TODO: .map(node -> toolBoxDependenciesModifier.modify(node))
                .doOnNext(el -> logger.info("Health node {} discovered in url: {}", el.getId(), url)).doOnCompleted(
                        () -> logger.info("Completed emission of a health node observable from url: " + url));
    }
}