All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.zeebe.broker.exporter.debug.DebugHttpServer Maven / Gradle / Ivy

/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
 * one or more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * Licensed under the Zeebe Community License 1.1. You may not use this file
 * except in compliance with the Zeebe Community License 1.1.
 */
package io.zeebe.broker.exporter.debug;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Charsets;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import io.zeebe.protocol.record.Record;
import io.zeebe.util.collection.Tuple;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.stream.Collectors;

public final class DebugHttpServer {

  private static final Charset CHARSET = Charsets.UTF_8;
  private static final String[] RESOURCE_NAMES =
      new String[] {
        "index.html",
        "bootstrap-4.1.3.min.css",
        "bootstrap-4.1.3.min.js",
        "jquery-3.3.1.slim.min.js",
        "mustache-3.0.0.min.js"
      };

  private static final Map CONTENT_TYPES = new HashMap<>();

  static {
    CONTENT_TYPES.put(".css", "text/css");
    CONTENT_TYPES.put(".html", "text/html");
    CONTENT_TYPES.put(".js", "text/javascript");
    CONTENT_TYPES.put(".json", "application/json");
  }

  private final int maxSize;
  private final Map resources;
  private final LinkedList records;
  private HttpServer server;

  public DebugHttpServer(final int port, final int maxSize) {
    this.maxSize = maxSize;
    server = startHttpServer(port);
    resources = loadResources();
    records = new LinkedList<>();
  }

  public void close() {
    if (server != null) {
      server.stop(0);
      server = null;
    }
  }

  private HttpServer startHttpServer(final int port) {
    try {
      final HttpServer httpServer = HttpServer.create(new InetSocketAddress(port), 0);
      httpServer.createContext("/", new RequestHandler());
      httpServer.start();
      return server;
    } catch (final IOException e) {
      throw new RuntimeException("Failed to start debug exporter http server", e);
    }
  }

  @SuppressWarnings("unchecked")
  private Map loadResources() {
    return Arrays.stream(RESOURCE_NAMES)
        .map(resourceName -> new Tuple<>(resourceName, loadResource(resourceName)))
        .collect(Collectors.toConcurrentMap(Tuple::getLeft, Tuple::getRight));
  }

  private byte[] loadResource(final String resourceName) {
    try (final InputStream resourceAsStream =
        DebugHttpServer.class.getResourceAsStream(resourceName)) {
      if (resourceAsStream != null) {
        return resourceAsStream.readAllBytes();
      } else {
        throw new RuntimeException(
            "Failed to find resource " + resourceName + " for debug http exporter");
      }
    } catch (final IOException e) {
      throw new RuntimeException(
          "Failed to read resource " + resourceName + " for debug http exporter", e);
    }
  }

  public synchronized void add(final Record record) throws JsonProcessingException {
    while (records.size() >= maxSize) {
      records.removeLast();
    }

    records.addFirst(record.toJson());
  }

  class RequestHandler implements HttpHandler {

    @Override
    public void handle(final HttpExchange httpExchange) throws IOException {
      String path = httpExchange.getRequestURI().getPath().substring(1);
      if (path.isEmpty()) {
        path = "index.html";
      }

      final String extension = path.substring(path.lastIndexOf('.'));
      final String contentType = CONTENT_TYPES.get(extension);
      if (contentType != null) {
        httpExchange.getResponseHeaders().add("Content-Type", contentType);
      }

      final byte[] response;
      if ("records.json".equals(path)) {
        response = getRecords();
      } else {
        response = resources.get(path);
      }

      if (response.length > 0) {
        httpExchange.sendResponseHeaders(200, response.length);
        try (final OutputStream outputStream = httpExchange.getResponseBody()) {
          outputStream.write(response);
        }
      } else {
        httpExchange.sendResponseHeaders(404, 0);
      }
    }

    private byte[] getRecords() {
      final String json = "[" + String.join(",", records) + "]";
      return json.getBytes(CHARSET);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy