aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/webstatusserver/TestStatusHandler.java
blob: 3d1357eab2561e5c21d30646ee2c7052f41b0f8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright 2014 Google Inc. All rights reserved.
//
// 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.google.devtools.build.lib.webstatusserver;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;

import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Collection of handlers for displaying the test data.
 */
class TestStatusHandler {
  private StaticResourceHandler frontendHandler;
  private WebStatusBuildLog buildLog;
  private HttpHandler detailsHandler;
  private HttpServer server;
  private ImmutableList<HttpContext> contexts;
  private CommandJsonData commandHandler;
  private Gson gson = new Gson();

  public TestStatusHandler(HttpServer server, WebStatusBuildLog buildLog) {
    Builder<HttpContext> builder = ImmutableList.builder();
    this.buildLog = buildLog;
    this.server = server;
    detailsHandler = new TestStatusResultJsonData(this);
    commandHandler = new CommandJsonData(this);
    frontendHandler = StaticResourceHandler.createFromRelativePath("static/test.html", "text/html");
    builder.add(
        server.createContext("/tests/" + buildLog.getCommandId() + "/details", detailsHandler));
    builder.add(
        server.createContext("/tests/" + buildLog.getCommandId() + "/info", commandHandler));
    builder.add(server.createContext("/tests/" + buildLog.getCommandId(), frontendHandler));
    contexts = builder.build();
  }

  public WebStatusBuildLog getBuildLog() {
    return buildLog;
  }

  
  /**
   *  Serves JSON objects containing command info, which will be rendered by frontend.
   */
  private class CommandJsonData implements HttpHandler {
    private TestStatusHandler testStatusHandler;
    
    public CommandJsonData(TestStatusHandler testStatusHandler) {
      this.testStatusHandler = testStatusHandler;
    }

    @Override
    public void handle(HttpExchange exchange) throws IOException {
      exchange.getResponseHeaders().put("Content-Type", ImmutableList.of("application/json"));
      Type commandInfoType = new TypeToken<Map<String, JsonElement>>() {}.getType();
      JsonObject response = gson.toJsonTree(testStatusHandler.buildLog.getCommandInfo(),
          commandInfoType).getAsJsonObject();
      response.addProperty("startTime", testStatusHandler.buildLog.getStartTime());
      response.addProperty("finished", testStatusHandler.buildLog.finished());

      String serializedResponse = response.toString();
      exchange.sendResponseHeaders(200, serializedResponse.length());
      try (OutputStream os = exchange.getResponseBody()) {
        os.write(serializedResponse.getBytes(StandardCharsets.UTF_8));
      }
    }
  }
  
  /**
   * Serves JSON objects containing test cases, which will be rendered by frontend.
   */
  private class TestStatusResultJsonData implements HttpHandler {
    private TestStatusHandler testStatusHandler;

    public TestStatusResultJsonData(TestStatusHandler testStatusHandler) {
      this.testStatusHandler = testStatusHandler;
    }

    @Override
    public void handle(HttpExchange exchange) throws IOException {
      Map<String, JsonObject> testInfo = testStatusHandler.buildLog.getTestCases();
      exchange.getResponseHeaders().put("Content-Type", ImmutableList.of("application/json"));
      JsonObject response = new JsonObject();
      for (Entry<String, JsonObject> testCase : testInfo.entrySet()) {
        response.add(testCase.getKey(), testCase.getValue());
      }

      String serializedResponse = response.toString();
      exchange.sendResponseHeaders(200, serializedResponse.length());
      try (OutputStream os = exchange.getResponseBody()) {
        os.write(serializedResponse.getBytes(StandardCharsets.UTF_8));
      }
    }
  }

  /**
   * Adds another URI for existing test data. If specified URI is already used by some other 
   * handler, the previous handler will be removed.
   */
  public void overrideURI(String uri) {
    String detailsPath = uri + "/details";
    String commandPath = uri + "/info";
    try {
      this.server.removeContext(detailsPath);
      this.server.removeContext(commandPath);
    } catch (IllegalArgumentException e) {
      // There was nothing to remove, so proceed with creation (unfortunately the server api doesn't
      // have "hasContext" method)
    }
    this.server.createContext(detailsPath, this.detailsHandler); 
    this.server.createContext(commandPath, this.commandHandler);   
  }
  
  /**
   * Deregisters all the handlers associated with the test.
   */
  public void deregister() {
    for (HttpContext c : this.contexts) {
      this.server.removeContext(c);
    }
  }
}