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

com.linkedin.parseq.JhatHandler Maven / Gradle / Ivy

package com.linkedin.parseq;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;

import com.linkedin.parseq.httpclient.HttpClient;
import com.linkedin.parseq.trace.ShallowTraceBuilder;
import com.linkedin.parseq.trace.Trace;
import com.linkedin.parseq.trace.TraceBuilder;
import com.linkedin.parseq.trace.TraceRelationship;
import com.linkedin.parseq.trace.codec.json.JsonTraceCodec;
import com.ning.http.client.Response;

final class JhatHandler extends AbstractHandler {

  private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
  private static final Pattern REGEX = Pattern.compile("^.*?.*?", Pattern.DOTALL);
  private static final JsonTraceCodec CODEC = new JsonTraceCodec();


  private final Engine _engine;
  private final String _script;

  JhatHandler(Engine engine) throws IOException {
    _engine = engine;
    _script = read(getClass().getClassLoader().getResourceAsStream("RecoverParSeqTracesFromHeapDump.js"));
  }

  private static String read(InputStream input) throws IOException {
    try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) {
      return buffer.lines().collect(Collectors.joining("\n"));
    }
  }

  @Override
  public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
      throws IOException, ServletException {
    if (target.startsWith("/jhat")) {

      baseRequest.setHandled(true);
      // Process request in async mode
      final AsyncContext ctx = request.startAsync();

      final Task responseTask = fetchJSON(request)
          .recover("handleFailure", this::handleFailure)
          .andThen("writeResponseAndComplete", r -> writeResponseAndComplete(response, r, ctx));

      // Execute
      _engine.run(responseTask);
    }
  }

  private void writeResponseAndComplete(HttpServletResponse response, HttpResponse r, AsyncContext ctx) throws IOException {
    response.getWriter().write(r.getBody());
    response.setStatus(r.getStatus());
    ctx.complete();
  }

  private HttpResponse handleFailure(Throwable t) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    t.printStackTrace(pw);
    String stackTrace = sw.toString();
    return new HttpResponse(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error processing request:\n" + stackTrace);
  }

  private Task fetchJSON(HttpServletRequest request) {
    String location = request.getParameter("location");
    if (location == null) {
      return Task.value(new HttpResponse(HttpServletResponse.SC_BAD_REQUEST, "Missing location query parameter"));
    } else {
      return Task.flatten(Task.callable(() -> oqlGetTask(location)))
        .map("processOQLResponse", this::processOQLResponse);
    }
  }

  private Task oqlGetTask(String location) {
    try {
      return HttpClient.get(location + "/oql/")
        .setRequestTimeout(900000)
        .addQueryParam("query", _script).task("runOQL");
    } catch (Exception e) {
      throw new RuntimeException("Can't create GET request to jhat server using location: " + location, e);
    }
  }

  private HttpResponse processOQLResponse(Response response) throws IOException {
    String responseBody = response.getResponseBody();
    if (response.getStatusCode() != HttpServletResponse.SC_OK) {
      return new HttpResponse(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to query Jhat:\n" + responseBody);
    } else {
      Matcher regexMatcher = REGEX.matcher(responseBody);
      if (regexMatcher.find()) {
        String cutResponse = regexMatcher.group(1);
        String fixedCutResponse = cutResponse.substring(0, cutResponse.length() - 4) + " ]";
        final JsonNode resultsArr =
            OBJECT_MAPPER.readTree(OBJECT_MAPPER.getJsonFactory().createJsonParser(fixedCutResponse));
        final StringJoiner joiner = new StringJoiner(", ", "[ ", " ]");
        for (JsonNode node : resultsArr) {
          Trace trace = CODEC.decode(node.toString());
          TraceBuilder builder = new TraceBuilder(trace.getRelationships().size() + 1, trace.getPlanClass(), trace.getPlanId());
          Map traceMap = new HashMap<>();
          trace.getTraceMap().forEach((key, value) -> {
            ShallowTraceBuilder stb = new ShallowTraceBuilder(value);
            traceMap.put(key, stb);
            builder.addShallowTrace(stb);
          });
          for (TraceRelationship rel : trace.getRelationships()) {
            builder.addRelationship(rel.getRelationhsip(), traceMap.get(rel.getFrom()), traceMap.get(rel.getTo()));
          }
          joiner.add(CODEC.encode(builder.build()));
        }
        return new HttpResponse(HttpServletResponse.SC_OK, joiner.toString());
      } else {
        return new HttpResponse(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
            "Failed parsing Jhat response:\n" + responseBody);
      }
    }
  }
}    









\\s*(.*)\\s*