org.kaaproject.kaa.server.appenders.rest.appender.RestLogAppender.java Source code

Java tutorial

Introduction

Here is the source code for org.kaaproject.kaa.server.appenders.rest.appender.RestLogAppender.java

Source

/*
 * Copyright 2014-2016 CyberVision, 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 org.kaaproject.kaa.server.appenders.rest.appender;

import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.kaaproject.kaa.common.dto.logs.LogAppenderDto;
import org.kaaproject.kaa.common.dto.logs.LogEventDto;
import org.kaaproject.kaa.server.appenders.rest.config.gen.MethodType;
import org.kaaproject.kaa.server.appenders.rest.config.gen.RequestType;
import org.kaaproject.kaa.server.appenders.rest.config.gen.RestConfig;
import org.kaaproject.kaa.server.common.log.shared.appender.AbstractLogAppender;
import org.kaaproject.kaa.server.common.log.shared.appender.LogDeliveryCallback;
import org.kaaproject.kaa.server.common.log.shared.appender.LogEventPack;
import org.kaaproject.kaa.server.common.log.shared.avro.gen.RecordHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class RestLogAppender extends AbstractLogAppender<RestConfig> {

    private static final Logger LOG = LoggerFactory.getLogger(RestLogAppender.class);

    private ExecutorService executor;
    private CloseableHttpClient client;
    private HttpHost target;
    private URI targetUri;
    private RestConfig configuration;
    private boolean closed = false;

    public RestLogAppender() {
        super(RestConfig.class);
    }

    @Override
    protected void initFromConfiguration(LogAppenderDto appender, RestConfig configuration) {
        this.configuration = configuration;
        this.executor = Executors.newFixedThreadPool(configuration.getConnectionPoolSize());
        target = new HttpHost(configuration.getHost(), configuration.getPort(),
                configuration.getSsl() ? "https" : "http");
        HttpClientBuilder builder = HttpClients.custom();
        if (configuration.getUsername() != null && configuration.getPassword() != null) {
            LOG.info("Adding basic auth credentials provider");
            CredentialsProvider credsProvider = new BasicCredentialsProvider();
            credsProvider.setCredentials(new AuthScope(target.getHostName(), target.getPort()),
                    new UsernamePasswordCredentials(configuration.getUsername(), configuration.getPassword()));
            builder.setDefaultCredentialsProvider(credsProvider);
        }
        if (!configuration.getVerifySslCert()) {
            LOG.info("Adding trustful ssl context");
            SSLContextBuilder sslBuilder = new SSLContextBuilder();
            try {
                sslBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslBuilder.build());
                builder.setSSLSocketFactory(sslsf);
            } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException ex) {
                LOG.error("Failed to init socket factory {}", ex.getMessage(), ex);
            }
        }
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setDefaultMaxPerRoute(configuration.getConnectionPoolSize());
        cm.setMaxTotal(configuration.getConnectionPoolSize());
        builder.setConnectionManager(cm);
        this.client = builder.build();
    }

    @Override
    public void doAppend(LogEventPack logEventPack, RecordHeader header, final LogDeliveryCallback listener) {
        if (closed) {
            LOG.warn("Attempt to append data to already stopped appender");
            listener.onInternalError();
        }
        if (targetUri == null) {
            try {
                targetUri = new URIBuilder().setHost(target.getHostName()).setPort(target.getPort())
                        .setScheme(target.getSchemeName()).setPath(configuration.getPath()).build();
            } catch (URISyntaxException ex) {
                LOG.warn("[{}] failed to build request URI", this.getApplicationToken(), ex);
                listener.onInternalError();
            }
        }
        LOG.trace("[{}] appending {} logs to rest endpoint", this.getApplicationToken(),
                logEventPack.getEvents().size());
        final RestConfig configuration = this.configuration;
        try {
            for (final LogEventDto dto : generateLogEvent(logEventPack, header)) {
                executor.submit(new Runnable() {

                    @Override
                    public void run() {
                        try {
                            LOG.trace("[{}] appending {} to rest endpoint",
                                    RestLogAppender.this.getApplicationToken(), dto);
                            final HttpRequest request = createRequest(configuration, dto);
                            LOG.trace("[{}] executing {}", RestLogAppender.this.getApplicationToken(),
                                    request.getRequestLine());

                            CloseableHttpResponse response = client.execute(target, request);
                            try {
                                int responseCode = response.getStatusLine().getStatusCode();
                                LOG.trace("[{}] received {} response code",
                                        RestLogAppender.this.getApplicationToken(), response);
                                if (responseCode >= 200 && responseCode < 400) {
                                    LOG.trace("[{}] logs appended successfully", getName());
                                    listener.onSuccess();
                                } else {
                                    LOG.warn("[{}] bad response code {}", getName(), responseCode);
                                    listener.onRemoteError();
                                }
                            } finally {
                                response.close();
                            }
                        } catch (IOException ex) {
                            LOG.error("[{}] Failed to send log event.", getName(), ex);
                            listener.onConnectionError();
                        } catch (Exception ex) {
                            LOG.error("[{}] Failed to send log event.", getName(), ex);
                            listener.onInternalError();
                        }
                    }
                });
            }
        } catch (IOException ex) {
            LOG.error("[{}] Failed to send log events.", getName(), ex);
            listener.onInternalError();
        }
    }

    private HttpRequest createRequest(RestConfig configuration, LogEventDto dto) throws URISyntaxException {
        String body = buildRequestBody(configuration, dto);
        ContentType contentType = buildContentType(configuration);
        StringEntity entity = new StringEntity(body, contentType);
        final HttpEntityEnclosingRequestBase request;

        if (configuration.getMethod() == MethodType.POST) {
            request = new HttpPost(targetUri);
        } else {
            request = new HttpPut(targetUri);
        }
        request.setEntity(entity);
        return request;
    }

    private ContentType buildContentType(RestConfig configuration) {
        ContentType contentType;
        if (configuration.getMimeType() == RequestType.TEXT) {
            contentType = ContentType.create("text/plain", "UTF-8");
        } else {
            contentType = ContentType.create("application/json", "UTF-8");
        }
        return contentType;
    }

    private String buildRequestBody(RestConfig configuration, LogEventDto dto) {
        String body;
        if (configuration.getHeader()) {
            StringBuilder sb = new StringBuilder();
            sb.append("{\"header\":").append(dto.getHeader()).append(",");
            sb.append("\"event\":").append(dto.getEvent()).append("}");
            body = sb.toString();
        } else {
            body = dto.getEvent();
        }
        return body;
    }

    @Override
    public void close() {
        closed = true;
        try {
            client.close();
            executor.shutdown();
            executor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (IOException | InterruptedException ex) {
            LOG.error("Failed to close appender: {}", ex.getMessage(), ex);
        }
    }
}