com.insys.cfclient.nozzle.InfluxDBSender.java Source code

Java tutorial

Introduction

Here is the source code for com.insys.cfclient.nozzle.InfluxDBSender.java

Source

/*******************************************************************************
 * Copyright 2017 ECS Team, Inc.
 *
 * 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 com.insys.cfclient.nozzle;

import com.insys.cfclient.config.NozzleProperties;
import com.insys.cfclient.destination.MetricsDestination;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.backoff.UniformRandomBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Sends a batch of messages to InfluxDB with retry logic.
 */
@RequiredArgsConstructor
@Slf4j
@Service
public class InfluxDBSender {
    private RestTemplate httpClient = new RestTemplate();
    private URI uri;
    private BackOffPolicy backOffPolicy;

    private final NozzleProperties properties;
    private final MetricsDestination influxDbDestination;

    @PostConstruct
    public void postConstruct() throws Exception {
        if (properties.isSkipSslValidation()) {
            TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

            SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                    .loadTrustMaterial(null, acceptingTrustStrategy).build();

            SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

            CloseableHttpClient apacheHttpClient = HttpClients.custom().setSSLSocketFactory(csf).build();

            HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();

            requestFactory.setHttpClient(apacheHttpClient);

            this.httpClient.setRequestFactory(requestFactory);
        }
    }

    @Async
    public void sendBatch(List<String> messages) {
        log.debug("ENTER sendBatch");
        httpClient.setErrorHandler(new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
                return clientHttpResponse.getRawStatusCode() > 399;
            }

            @Override
            public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {

            }
        });

        RetryTemplate retryable = new RetryTemplate();
        retryable.setBackOffPolicy(getBackOffPolicy());
        retryable.setRetryPolicy(new SimpleRetryPolicy(properties.getMaxRetries(),
                Collections.singletonMap(ResourceAccessException.class, true)));

        final AtomicInteger counter = new AtomicInteger(0);
        retryable.execute(retryContext -> {
            int count = counter.incrementAndGet();
            log.trace("Attempt {} to deliver this batch", count);
            final StringBuilder builder = new StringBuilder();
            messages.forEach(s -> builder.append(s).append("\n"));

            String body = builder.toString();

            RequestEntity<String> entity = new RequestEntity<>(body, HttpMethod.POST, getUri());

            ResponseEntity<String> response;

            response = httpClient.exchange(entity, String.class);

            if (response.getStatusCode() != HttpStatus.NO_CONTENT) {
                log.error("Failed to write logs to InfluxDB! Expected error code 204, got {}",
                        response.getStatusCodeValue());

                log.trace("Request Body: {}", body);
                log.trace("Response Body: {}", response.getBody());

            } else {
                log.debug("batch sent successfully!");
            }

            log.debug("EXIT sendBatch");

            return null;
        }, recoveryContext -> {
            log.trace("Failed after {} attempts!", counter.get());
            return null;
        });
    }

    private URI getUri() {
        if (uri == null) {
            uri = URI.create(
                    String.format("%s/write?db=%s", influxDbDestination.getInfluxDbHost(), properties.getDbName()));
        }

        return uri;
    }

    private BackOffPolicy getBackOffPolicy() {
        if (backOffPolicy == null) {
            log.info("Using backoff policy {}", properties.getBackoffPolicy().name());
            switch (properties.getBackoffPolicy()) {
            case linear:
                backOffPolicy = new FixedBackOffPolicy();
                ((FixedBackOffPolicy) backOffPolicy).setBackOffPeriod(properties.getMinBackoff());
                break;
            case random:
                backOffPolicy = new UniformRandomBackOffPolicy();
                ((UniformRandomBackOffPolicy) backOffPolicy).setMinBackOffPeriod(properties.getMinBackoff());
                ((UniformRandomBackOffPolicy) backOffPolicy).setMaxBackOffPeriod(properties.getMaxBackoff());
                break;
            case exponential:
            default:
                backOffPolicy = new ExponentialBackOffPolicy();
                ((ExponentialBackOffPolicy) backOffPolicy).setInitialInterval(properties.getMinBackoff());
                ((ExponentialBackOffPolicy) backOffPolicy).setMaxInterval(properties.getMaxBackoff());
                break;
            }
        }

        return backOffPolicy;
    }
}