Package org.platformlayer.metrics.client

Source Code of org.platformlayer.metrics.client.MetricClientImpl

package org.platformlayer.metrics.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;

import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SchemeSocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DecompressingHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.platformlayer.http.SslHelpers;
import org.platformlayer.metrics.MetricTreeObject;
import org.platformlayer.metrics.model.MetricQuery;
import org.platformlayer.ops.OpsException;
import org.platformlayer.rest.RestClientException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fathomdb.Configuration;
import com.fathomdb.crypto.CertificateAndKey;
import com.fathomdb.crypto.EncryptionStore;
import com.fathomdb.crypto.SimpleClientCertificateKeyManager;
import com.fathomdb.crypto.ssl.PublicKeyTrustManager;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;

@Singleton
public class MetricClientImpl implements MetricClient {
  public static class Provider implements com.google.inject.Provider<MetricClient> {
    @Inject
    Configuration configuration;

    @Inject
    EncryptionStore encryptionStore;

    @Override
    public MetricClient get() {
      try {
        return MetricClientImpl.build(configuration, encryptionStore);
      } catch (OpsException e) {
        throw new IllegalStateException("Error building metric client", e);
      }
    }
  }

  private static final Logger log = LoggerFactory.getLogger(MetricClientImpl.class);

  final HttpClient httpClient;
  final URI metricBaseUrl;
  final MetricTreeSerializer metricTreeSerializer;

  final String project;

  final MetricTreeObject tags;

  public MetricClientImpl(URI metricBaseUrl, String project, MetricTreeObject tags,
      CertificateAndKey certificateAndKey, List<String> trustKeys) {
    super();
    this.metricBaseUrl = metricBaseUrl;
    this.project = project;
    this.tags = tags;
    this.metricTreeSerializer = new MetricTreeSerializer();

    this.httpClient = buildHttpClient(certificateAndKey, trustKeys);
  }

  @Override
  public void close() {
    httpClient.getConnectionManager().shutdown();
  }

  private HttpClient buildHttpClient(CertificateAndKey certificateAndKey, List<String> trustKeys) {
    int port = metricBaseUrl.getPort();
    if (port == -1) {
      String scheme = metricBaseUrl.getScheme();
      if (scheme.equals("https")) {
        port = 443;
      } else if (scheme.equals("http")) {
        port = 80;
      } else {
        throw new IllegalArgumentException("Unknown scheme: " + scheme);
      }
    }

    SchemeSocketFactory schemeSocketFactory;
    try {
      KeyManager keyManager = new SimpleClientCertificateKeyManager(certificateAndKey);

      TrustManager trustManager;
      X509HostnameVerifier hostnameVerifier;
      if (trustKeys != null) {
        trustManager = new PublicKeyTrustManager(trustKeys);
        hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
      } else {
        trustManager = null;
        hostnameVerifier = SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
      }
      javax.net.ssl.SSLSocketFactory sslSocketFactory = SslHelpers
          .buildSslSocketFactory(keyManager, trustManager);

      schemeSocketFactory = new SSLSocketFactory(sslSocketFactory, hostnameVerifier);
    } catch (GeneralSecurityException e) {
      throw new IllegalArgumentException("Error building SSL client", e);
    }

    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(new Scheme("https", port, schemeSocketFactory));

    PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(schemeRegistry);

    HttpClient httpClient = new DefaultHttpClient(connectionManager);

    httpClient = new DecompressingHttpClient(httpClient);

    return httpClient;
  }

  // TODO: Throw on failure??
  @Override
  public boolean sendMetrics(MetricTreeObject tree) {
    if (tags != null) {
      tree.mergeTree(tags);
    }

    URI url = metricBaseUrl.resolve("api/metric/").resolve(project);
    HttpPost request = new HttpPost(url);
    HttpResponse response = null;
    try {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      metricTreeSerializer.serialize(tree, baos);
      baos.close();

      byte[] data = baos.toByteArray();

      log.debug("POSTing " + new String(data));

      // TODO: Stream body? We'd just need a custom ByteArrayEntity class
      request.setEntity(new ByteArrayEntity(data));

      response = httpClient.execute(request);

      StatusLine statusLine = response.getStatusLine();
      if (statusLine.getStatusCode() != 200) {
        log.warn("Error writing to PlatformLayer metrics server: " + statusLine);
        return false;
      } else {
        EntityUtils.consume(response.getEntity());
        response = null;
        // consumeResponse(request, response);
        log.debug("Posted metrics");
        return true;
      }
    } catch (IOException e) {
      if (log.isDebugEnabled()) {
        log.debug("Error writing to PlatformLayer metrics server", e);
      }
      log.warn("Error writing to PlatformLayer metrics server {}", e.getMessage());
      return false;
    } finally {
      if (response != null) {
        try {
          EntityUtils.consume(response.getEntity());
        } catch (IOException e) {
          log.warn("Error consuming response", e);
        }
      }
    }
  }

  @Override
  public MetricServiceData getMetrics(MetricQuery query) throws RestClientException {
    URI url = metricBaseUrl.resolve("api/metric/").resolve(project + "/");

    URIBuilder uriBuilder = new URIBuilder(url);

    if (query != null) {
      for (String filter : query.filters) {
        int firstEquals = filter.indexOf('=');
        if (firstEquals == -1) {
          uriBuilder.addParameter("has." + filter, "");
        } else {
          String key = filter.substring(0, firstEquals);
          String value = filter.substring(firstEquals + 1);

          uriBuilder.addParameter("filter." + key, value);
        }
      }

      for (String projection : query.projections) {
        int firstEquals = projection.indexOf('=');
        if (firstEquals == -1) {
          uriBuilder.addParameter("select." + projection, "");
        } else {
          throw new IllegalArgumentException();
        }
      }

      if (query.flatten) {
        uriBuilder.addParameter("flatten", "true");
      }
    }

    try {
      url = uriBuilder.build();
    } catch (URISyntaxException e) {
      throw new IllegalArgumentException("Error building URI", e);
    }

    HttpGet request = new HttpGet(url);
    HttpResponse response = null;

    try {
      response = httpClient.execute(request);

      StatusLine statusLine = response.getStatusLine();
      if (statusLine.getStatusCode() != 200) {
        log.info("Error reading from metrics service: " + statusLine);

        throw new RestClientException("Error reading from metrics service", null, statusLine.getStatusCode());
      } else {
        MetricServiceData ret = new MetricServiceData(request, response);

        response = null; // Don't close yet

        return ret;
      }
    } catch (IOException e) {
      throw new RestClientException("Error reading from metrics service", e);
    } finally {
      if (response != null) {
        try {
          EntityUtils.consume(response.getEntity());
        } catch (IOException e) {
          log.warn("Error consuming response", e);
        }
      }
    }
  }

  public static MetricClient build(Configuration configuration, EncryptionStore encryptionStore) throws OpsException {
    if (!configuration.lookup("metrics.report.enabled", true)) {
      return new DummyMetricClient();
    }

    // String cert = configuration.get("metrics.report.ssl.cert");
    String cert = configuration.get("metrics.tls.clientcert");
    CertificateAndKey certificateAndKey = encryptionStore.getCertificateAndKey(cert);

    String project = configuration.get("metrics.report.project");

    MetricTreeObject tags = new MetricTreeObject(null);
    Map<String, String> tagProperties = configuration.getChildProperties("metrics.report.tags.");
    copyPropertiesToTree(tagProperties, tags.getSubtree("tags"));

    return build(configuration, encryptionStore, project, tags, certificateAndKey);
  }

  private static void copyPropertiesToTree(Map<String, String> properties, MetricTreeObject dest) {
    for (Entry<String, String> entry : properties.entrySet()) {
      String key = entry.getKey();
      String value = entry.getValue();

      List<String> tokens = Lists.newArrayList(Splitter.on('.').split(key));

      MetricTreeObject subtree = dest;
      for (int i = 0; i < tokens.size() - 1; i++) {
        subtree = subtree.getSubtree(tokens.get(i));
      }

      String valueKey = tokens.get(tokens.size() - 1);

      subtree.addString(valueKey, value);
    }
  }

  public static MetricClientImpl build(Configuration configuration, EncryptionStore encryptionStore, String project,
      MetricTreeObject tags, CertificateAndKey certificateAndKey) throws OpsException {
    String metricBaseUrl = configuration.lookup("metrics.report.url", "https://metrics.platformlayer.net:8099/");

    String trustKeysString = configuration.lookup("metrics.report.ssl.keys", null);

    List<String> trustKeys = null;
    if (trustKeysString != null) {
      trustKeys = Lists.newArrayList(Splitter.on(',').trimResults().split(trustKeysString));
    }

    URI uri = URI.create(metricBaseUrl);

    MetricClientImpl metricSender = new MetricClientImpl(uri, project, tags, certificateAndKey, trustKeys);
    return metricSender;
  }

}
TOP

Related Classes of org.platformlayer.metrics.client.MetricClientImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.