Upload data to google spreadsheets.

Change-Id: I85b6d30ecbeacdfdc311b257c1036e661dc8254c
diff --git a/common/pom.xml b/common/pom.xml
index 60ad7e0..04a456f 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -44,6 +44,31 @@
       <version>4.11</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.google.api-client</groupId>
+      <artifactId>google-api-client-jackson2</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.api-client</groupId>
+      <artifactId>google-api-client</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.oauth-client</groupId>
+      <artifactId>google-oauth-client</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.oauth-client</groupId>
+      <artifactId>google-oauth-client-java6</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>18.0</version>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/common/src/main/java/com/google/gwt/benchmark/oauth2/server/OAuthHelper.java b/common/src/main/java/com/google/gwt/benchmark/oauth2/server/OAuthHelper.java
new file mode 100644
index 0000000..3d8070f
--- /dev/null
+++ b/common/src/main/java/com/google/gwt/benchmark/oauth2/server/OAuthHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 Google 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 com.google.gwt.benchmark.oauth2.server;
+
+import com.google.api.client.auth.oauth2.Credential;
+import com.google.api.client.extensions.java6.auth.oauth2.AbstractPromptReceiver;
+import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
+import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
+import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
+import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
+import com.google.api.client.http.HttpTransport;
+import com.google.api.client.json.JsonFactory;
+import com.google.api.client.json.jackson2.JacksonFactory;
+import com.google.api.client.util.store.FileDataStoreFactory;
+import com.google.common.collect.Sets;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.security.GeneralSecurityException;
+import java.util.Set;
+
+/**
+ * OAuthHelper enables easy OAuth.
+ */
+public class OAuthHelper {
+  private static class MyReceiver extends AbstractPromptReceiver {
+    @Override
+    public String getRedirectUri() throws IOException {
+      // this just emits the code so that we can paste it back into the console
+      return "urn:ietf:wg:oauth:2.0:oob";
+    }
+  }
+
+  public static Credential authorize(File dataDirectory, String clientJsonSecret)
+      throws IOException, GeneralSecurityException {
+    FileDataStoreFactory dataStoreFactory = new FileDataStoreFactory(dataDirectory);
+
+    HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
+    JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
+    // load client secrets
+    GoogleClientSecrets clientSecrets =
+        GoogleClientSecrets.load(JSON_FACTORY, new StringReader(clientJsonSecret));
+
+    Set<String> scopes =
+        Sets.newHashSet("https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds");
+
+    // set up authorization code flow
+    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport,
+        JSON_FACTORY, clientSecrets, scopes).setDataStoreFactory(dataStoreFactory).build();
+    // authorize
+    return new AuthorizationCodeInstalledApp(flow, new MyReceiver()).authorize("user");
+  }
+}
diff --git a/compileserver/pom.xml b/compileserver/pom.xml
index a733fb5..6dc65cf 100644
--- a/compileserver/pom.xml
+++ b/compileserver/pom.xml
@@ -88,6 +88,42 @@
       <artifactId>zip4j</artifactId>
       <version>1.3.1</version>
     </dependency>
+    <dependency>
+      <groupId>com.google.api-client</groupId>
+      <artifactId>google-api-client-jackson2</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.api-client</groupId>
+      <artifactId>google-api-client</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.oauth-client</groupId>
+      <artifactId>google-oauth-client</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.oauth-client</groupId>
+      <artifactId>google-oauth-client-java6</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.gdata</groupId>
+      <artifactId>core</artifactId>
+      <version>1.47.1</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>18.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.truth</groupId>
+      <artifactId>truth</artifactId>
+      <version>0.25</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   <build>
     <plugins>
diff --git a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/guice/BenchmarkModule.java b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/guice/BenchmarkModule.java
index 2773d50..5bdd1bd 100644
--- a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/guice/BenchmarkModule.java
+++ b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/guice/BenchmarkModule.java
@@ -13,6 +13,7 @@
  */
 package com.google.gwt.benchmark.compileserver.server.guice;
 
+import com.google.gdata.client.spreadsheet.SpreadsheetService;
 import com.google.gwt.benchmark.compileserver.server.manager.BenchmarkCompiler;
 import com.google.gwt.benchmark.compileserver.server.manager.BenchmarkReporter;
 import com.google.gwt.benchmark.compileserver.server.manager.BenchmarkWorker;
@@ -26,13 +27,12 @@
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
+import com.google.inject.Provides;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
 
 import java.io.File;
-import java.io.IOException;
-import java.net.HttpURLConnection;
 import java.net.URL;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
@@ -56,7 +56,6 @@
         Runner.Factory.class));
     install(new FactoryModuleBuilder().build(BenchmarkWorker.Factory.class));
     install(new FactoryModuleBuilder().build(BenchmarkReporter.Factory.class));
-    bind(BenchmarkReporter.HttpURLConnectionFactory.class).to(HttpUrlConnectionProvider.class);
     bind(MailHelper.class).to(MailHelperProdImpl.class);
     bind(String.class).annotatedWith(Names.named("randomStringProvider"))
         .toProvider(RandomStringProvider.class);
@@ -79,16 +78,20 @@
         .toInstance(settings.getScriptsDirectory());
     bind(Boolean.class).annotatedWith(Names.named("useReporter"))
         .toInstance(settings.reportResults());
-    bind(String.class).annotatedWith(Names.named("reporterSecret")).toInstance(
-        settings.getReporterSecret());
-    bind(String.class).annotatedWith(Names.named("benchmarkDashboardUrl"))
-        .toInstance(settings.getReporterUrl());
     bind(File.class).annotatedWith(Names.named("persistenceDir"))
         .toInstance(settings.getPersistenceDir());
     bind(File.class).annotatedWith(Names.named("gwtSourceLocation"))
         .toInstance(settings.getGwtSourceLocation());
     bind(MailSettings.class).toInstance(settings.getMailSettings());
+    bind(String.class).annotatedWith(Names.named("spreadSheetId")).toInstance(
+        settings.getSpreadSheetId());
+    bind(String.class).annotatedWith(Names.named("client_json_secret")).toInstance(
+        settings.getOauthSecret());
+  }
 
+  @Provides
+  public SpreadsheetService spreadSheetService() {
+    return new SpreadsheetService("benchmark");
   }
 
   private static class PoolProvider implements Provider<ExecutorService> {
@@ -106,22 +109,6 @@
     }
   }
 
-  private static class HttpUrlConnectionProvider implements
-      BenchmarkReporter.HttpURLConnectionFactory {
-
-    private String url;
-
-    @Inject
-    public HttpUrlConnectionProvider(@Named("benchmarkDashboardUrl") String url) {
-      this.url = url;
-    }
-
-    @Override
-    public HttpURLConnection create() throws IOException {
-      return (HttpURLConnection) new URL(url).openConnection();
-    }
-  }
-
   private static class RandomStringProvider implements Provider<String> {
     @Override
     public String get() {
diff --git a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkManager.java b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkManager.java
index 42aff0d..9137b41 100644
--- a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkManager.java
+++ b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkManager.java
@@ -103,7 +103,7 @@
         }
       }
       if (workCount.decrementAndGet() == 0) {
-        maybeReportResults(benchmarkRun.getCommitId(), benchmarkRun.getCommitMsEpoch());
+        maybeReportResults(benchmarkRun.getCommitId());
       }
     }
 
@@ -300,7 +300,7 @@
     return br;
   }
 
-  protected void maybeReportResults(String commitId, long commitMsEpoch) {
+  protected void maybeReportResults(String commitId) {
     Map<String, BenchmarkRun> results;
     synchronized (benchmarkRunsByNameLock) {
       results = deepClone(benchmarkRunsByName);
@@ -338,7 +338,7 @@
         commands.add(Command.SUCCESSFUL_RUN);
       }
     };
-    new Thread(reporterFactory.create(results, commitId, commitMsEpoch, p)).start();
+    new Thread(reporterFactory.create(results, commitId, p)).start();
   }
 
   private Command getNextCommand() {
diff --git a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkReporter.java b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkReporter.java
index 7a2804b..7eee209 100644
--- a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkReporter.java
+++ b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkReporter.java
@@ -13,41 +13,49 @@
  */
 package com.google.gwt.benchmark.compileserver.server.manager;
 
-import com.google.gwt.benchmark.common.shared.json.BenchmarkResultJson;
-import com.google.gwt.benchmark.common.shared.json.BenchmarkRunJson;
-import com.google.gwt.benchmark.common.shared.json.JsonFactory;
+import com.google.api.client.auth.oauth2.Credential;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.gdata.client.spreadsheet.FeedURLFactory;
+import com.google.gdata.client.spreadsheet.SpreadsheetService;
+import com.google.gdata.data.PlainTextConstruct;
+import com.google.gdata.data.spreadsheet.CellEntry;
+import com.google.gdata.data.spreadsheet.CellFeed;
+import com.google.gdata.data.spreadsheet.WorksheetEntry;
+import com.google.gdata.data.spreadsheet.WorksheetFeed;
+import com.google.gdata.util.ServiceException;
 import com.google.gwt.benchmark.compileserver.server.manager.BenchmarkRun.Result;
+import com.google.gwt.benchmark.oauth2.server.OAuthHelper;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.name.Named;
-import com.google.web.bindery.autobean.shared.AutoBean;
-import com.google.web.bindery.autobean.shared.AutoBeanCodex;
-import com.google.web.bindery.autobean.shared.AutoBeanUtils;
 
-import org.apache.commons.io.IOUtils;
-
+import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
- * BenchmarkReporter reports benchmark results to a remote URL.
+ * BenchmarkReporter reports benchmark results to a google spreadsheet.
  * <p>
- * It does a HTTP Put with JSON in the body using the AutoBeans interfaces from the common project.
- * On Failure it retries a couple of times before reporting a permanent failure.
+ * It creates a worksheet for each benchmark, adds headers (commit id + runners). It also searches
+ * for the current commit id in the spreadsheet and will update a row, this means that even if the
+ * reporter failed one update the data will be consitent after the next upload.
  */
 public class BenchmarkReporter implements Runnable {
 
-  public interface HttpURLConnectionFactory {
-    HttpURLConnection create() throws IOException;
-  }
+  private static final String COMMIT_HEADER = "commit";
 
   public interface ReportProgressHandler {
     void onCommitReported();
@@ -57,118 +65,314 @@
 
   public interface Factory {
     BenchmarkReporter create(Map<String, BenchmarkRun> results,
-        @Assisted("commitId") String commitId, @Assisted("commitDate") long commitMsEpoch,
-        ReportProgressHandler reportProgressHandler);
+        @Assisted("commitId") String commitId, ReportProgressHandler reportProgressHandler);
   }
 
-  public static final int[] WAITING_TIME_SECONDS = new int[] {1, 10, 100, 1000, 1000};
+  private static class ValueToAdd {
+    int row;
+    int col;
+    String value;
+  }
 
-  private static final int HTTP_OK = 200;
+  public static final List<Integer> WAITING_TIME_SECONDS =
+      Lists.newArrayList(1, 10, 100, 1000, 1000);
+
   private static Logger logger = Logger.getLogger(BenchmarkReporter.class.getName());
 
-  private final Map<String, BenchmarkRun> results;
+  private final Map<String, BenchmarkRun> benchmarkRunsByBenchmarkName;
   private final String commitId;
-  private final HttpURLConnectionFactory httpURLConnectionFactory;
 
   private final ReportProgressHandler reportProgressHandler;
 
-  private final long commitMsEpoch;
+  private final File oAuthDir;
 
-  private String reporterSecret;
+  private final String clientJsonSecret;
+
+  private final Provider<SpreadsheetService> spreadSheetServiveProvider;
+
+  private final String spreadSheetId;
 
   @Inject
-  public BenchmarkReporter(HttpURLConnectionFactory httpURLConnectionFactory,
-      @Named("reporterSecret") String reporterSecret, @Assisted Map<String, BenchmarkRun> results,
-      @Assisted("commitId") String commitId, @Assisted("commitDate") long commitMsEpoch,
-      @Assisted ReportProgressHandler reportProgressHandler) {
-    this.httpURLConnectionFactory = httpURLConnectionFactory;
-    this.reporterSecret = reporterSecret;
-    this.results = results;
+  public BenchmarkReporter(@Assisted Map<String, BenchmarkRun> benchmarkRunsByBenchmarkName,
+      @Assisted("commitId") String commitId, @Assisted ReportProgressHandler reportProgressHandler,
+      @Named("persistenceDir") File oAuthDir, @Named("client_json_secret") String clientJsonSecret,
+      Provider<SpreadsheetService> spreadSheetServiveProvider,
+      @Named("spreadSheetId") String spreadSheetId) {
+    this.benchmarkRunsByBenchmarkName = benchmarkRunsByBenchmarkName;
     this.commitId = commitId;
-    this.commitMsEpoch = commitMsEpoch;
     this.reportProgressHandler = reportProgressHandler;
+    this.oAuthDir = oAuthDir;
+    this.clientJsonSecret = clientJsonSecret;
+    this.spreadSheetServiveProvider = spreadSheetServiveProvider;
+    this.spreadSheetId = spreadSheetId;
   }
 
   @Override
   public void run() {
-    AutoBean<BenchmarkRunJson> bean = AutoBeanUtils.getAutoBean(createBenchmarkRunJson());
-    String jsonString = AutoBeanCodex.encode(bean).getPayload();
-
-    boolean sent = false;
-    for (int count = 0; count < WAITING_TIME_SECONDS.length; count++) {
-      if (postResultToServer(jsonString)) {
-        sent = true;
-
-        break;
+    for (int delay : WAITING_TIME_SECONDS) {
+      if (postResultToServer()) {
+        reportProgressHandler.onCommitReported();
+        return;
       }
       logger.warning(String.format("Could not post results to dashboard retrying in %d seconds.",
-          WAITING_TIME_SECONDS[count]));
-      if (!sleep(WAITING_TIME_SECONDS[count])) {
+          delay));
+      if (!sleep(delay)) {
         break;
       }
     }
-
-    if (!sent) {
-      reportProgressHandler.onPermanentFailure();
-    } else {
-      reportProgressHandler.onCommitReported();
-    }
+    reportProgressHandler.onPermanentFailure();
   }
 
-  private BenchmarkRunJson createBenchmarkRunJson() {
-    JsonFactory.Factory factory = JsonFactory.get();
+  private Map<String, WorksheetEntry> sheetsByName(SpreadsheetService service) throws IOException,
+      ServiceException {
+    Map<String, WorksheetEntry> map = Maps.newHashMap();
 
-    BenchmarkRunJson runJSON = factory.run().as();
-    runJSON.setCommitId(commitId);
-    runJSON.setCommitTimeMsEpoch(commitMsEpoch);
-    Map<String, List<BenchmarkResultJson>> results = new LinkedHashMap<>();
+    URL worksheetFeedUrl =
+        FeedURLFactory.getDefault().getWorksheetFeedUrl(spreadSheetId, "private", "values");
+    WorksheetFeed feed = service.getFeed(worksheetFeedUrl, WorksheetFeed.class);
+    List<WorksheetEntry> worksheetList = feed.getEntries();
 
-    for (Entry<String, BenchmarkRun> br : this.results.entrySet()) {
-
-      String moduleName = br.getKey();
-      List<BenchmarkResultJson> list = new ArrayList<>();
-      results.put(moduleName, list);
-
-      for (Entry<RunnerConfig, Result> entry : br.getValue().getResults().entrySet()) {
-        Result result = entry.getValue();
-        RunnerConfig runnerConfig = entry.getKey();
-        BenchmarkResultJson resultJSON = factory.result().as();
-        resultJSON.setBenchmarkName(moduleName);
-        resultJSON.setRunnerId(runnerConfig.toString());
-        resultJSON.setRunsPerSecond(result.getRunsPerSecond());
-        list.add(resultJSON);
-      }
-
+    for (WorksheetEntry worksheetEntry : worksheetList) {
+      map.put(worksheetEntry.getTitle().getPlainText(), worksheetEntry);
     }
-    runJSON.setResultByBenchmarkName(results);
-    return runJSON;
+    return map;
   }
 
-  private boolean postResultToServer(String json) {
-    OutputStream out = null;
+  private boolean postResultToServer() {
     try {
-
-      HttpURLConnection httpCon = httpURLConnectionFactory.create();
-      httpCon.addRequestProperty("auth", reporterSecret);
-      httpCon.setDoOutput(true);
-      httpCon.setRequestMethod("PUT");
-
-      out = httpCon.getOutputStream();
-      out.write(json.getBytes("UTF-8"));
-
-      if (httpCon.getResponseCode() == HTTP_OK) {
-        return true;
-      }
-
-    } catch (IOException e) {
-      logger.log(Level.WARNING, "Could not post results to server", e);
-    } finally {
-      IOUtils.closeQuietly(out);
+      doPostResult();
+    } catch (Exception e) {
+      logger.log(Level.WARNING, "Failed to post results", e);
+      return false;
     }
-    return false;
+    return true;
   }
 
-  // Visible for testing
+  @VisibleForTesting
+  void doPostResult() throws Exception {
+    Credential credential = authorize();
+    SpreadsheetService service = spreadSheetServiveProvider.get();
+    service.setOAuth2Credentials(credential);
+    Map<String, WorksheetEntry> sheetsByName = sheetsByName(service);
+
+    // calculate all sheets that need to be created
+    Set<String> sheetsToCreate = Sets.newHashSet(benchmarkRunsByBenchmarkName.keySet());
+    Set<String> allSpreadSheetNames = sheetsByName.keySet();
+    sheetsToCreate.removeAll(allSpreadSheetNames);
+
+    Map<String, WorksheetEntry> createdWorkSheetsByName = createSheets(service, sheetsToCreate);
+    sheetsByName.putAll(createdWorkSheetsByName);
+
+
+    List<String> benchmarkNamesList = Lists.newArrayList(benchmarkRunsByBenchmarkName.keySet());
+    // make sure our order of adding the spreadsheets is deterministic (makes testing easier)
+    Collections.sort(benchmarkNamesList);
+
+    for (String benchmarkName : benchmarkNamesList) {
+      BenchmarkRun benchmarkRun = benchmarkRunsByBenchmarkName.get(benchmarkName);
+      WorksheetEntry worksheetEntry = sheetsByName.get(benchmarkName);
+      if (worksheetEntry == null) {
+        // This should never happen since we just created the damn thing
+        throw new IllegalStateException();
+      }
+      postBenchmarkRunToSpreadSheet(service, worksheetEntry, benchmarkRun);
+    }
+  }
+
+  private void postBenchmarkRunToSpreadSheet(SpreadsheetService service, WorksheetEntry worksheetEntry,
+      BenchmarkRun benchmarkRun) throws Exception {
+
+    logger.info(String.format("Updating spreadsheet with commit %s for benchmark %s", commitId,
+        benchmarkRun.getModuleName()));
+    ensureAllHeadersPresent(service, worksheetEntry, benchmarkRun);
+
+    // Load headers from server so we get the actual order after we added some
+    List<String> headers = getHeaders(service, worksheetEntry);
+
+    // remove the first entry since its only the "commit header"
+    headers.remove(0);
+
+    // Maybe the commit id already exists in the spreadsheet
+    // Reusing rows for commit ids means that even if our update fails half way it will be
+    // eventually consistent
+    int rowIndex = getRowIndexForCommit(service, worksheetEntry, commitId);
+
+    if (rowIndex == -1) {
+      // No entry for this commit id which is fine
+      // This means that we will just create a new row
+      rowIndex = getFirstEmptyRow(service, worksheetEntry);
+    }
+    List<ValueToAdd> valuesToAdd = calculateValuesToAdd(headers, benchmarkRun, rowIndex);
+    addOrUpdateCells(service, worksheetEntry, valuesToAdd);
+  }
+
+  private void ensureAllHeadersPresent(SpreadsheetService service, WorksheetEntry worksheetEntry,
+      BenchmarkRun benchmarkRun) throws IOException, ServiceException, URISyntaxException {
+    List<String> headers = getHeaders(service, worksheetEntry);
+    List<String> runnerNames = getNames(benchmarkRun.getRunConfigs());
+    List<String> missingNames = Lists.newArrayList(runnerNames);
+    missingNames.removeAll(headers);
+
+    if (!headers.contains(COMMIT_HEADER)) {
+      List<String> tmp = Lists.newArrayList(COMMIT_HEADER);
+      tmp.addAll(missingNames);
+      missingNames = tmp;
+    }
+    maybeAddMissingHeaders(service, worksheetEntry, missingNames);
+  }
+
+  private void addOrUpdateCells(SpreadsheetService service, WorksheetEntry worksheet,
+      List<ValueToAdd> valuesToAdd) throws IOException, ServiceException, URISyntaxException {
+    URL cellFeedUrl = new URI(worksheet.getCellFeedUrl().toString()).toURL();
+    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
+    for (ValueToAdd valueToAdd : valuesToAdd) {
+      CellEntry cellEntry= new CellEntry (valueToAdd.row, valueToAdd.col, valueToAdd.value);
+      cellFeed.insert(cellEntry);
+    }
+  }
+
+  private List<ValueToAdd> calculateValuesToAdd(List<String> headers, BenchmarkRun benchmarkRun,
+      int rowIndex) {
+    List<ValueToAdd> valuesToAdd = Lists.newArrayList();
+
+    ValueToAdd v = new ValueToAdd();
+    v.row = rowIndex;
+    v.col = 1;
+    v.value = commitId;
+    valuesToAdd.add(v);
+
+    int index = 2;
+    for (String header : headers) {
+      RunnerConfig runnerConfigByName = getRunnerConfigByName(benchmarkRun.getRunConfigs(), header);
+      Result result = benchmarkRun.getResults().get(runnerConfigByName);
+      double runsPerSecond = result.getRunsPerSecond();
+
+      ValueToAdd valueToAdd = new ValueToAdd();
+      valueToAdd.row = rowIndex;
+      valueToAdd.col = index;
+      valueToAdd.value = Double.toString(runsPerSecond);
+      valuesToAdd.add(valueToAdd);
+      index++;
+    }
+    return valuesToAdd;
+  }
+
+
+
+  private RunnerConfig getRunnerConfigByName(List<RunnerConfig> runConfigs, String name) {
+    for (RunnerConfig runnerConfig : runConfigs) {
+     if(name.equals(runnerConfig.toString())) {
+       return runnerConfig;
+     }
+   }
+    throw new IllegalStateException("should never happen");
+  }
+
+
+  private int getFirstEmptyRow(SpreadsheetService service, WorksheetEntry worksheet)
+      throws URISyntaxException, IOException, ServiceException {
+    URL cellFeedUrl =
+        new URI(worksheet.getCellFeedUrl().toString() + "?min-col=1&max-col=1").toURL();
+    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
+
+    int size = cellFeed.getEntries().size();
+
+    if (size + 1 >= worksheet.getRowCount()) {
+      worksheet.setRowCount(size + 1);
+    }
+
+    return size + 1;
+  }
+
+  private void maybeAddMissingHeaders(SpreadsheetService service, WorksheetEntry worksheet,
+      List<String> missingNames) throws URISyntaxException, IOException, ServiceException {
+    Collections.sort(missingNames);
+
+    URL cellFeedUrl =
+        new URI(worksheet.getCellFeedUrl().toString() + "?min-row=1&max-row=1").toURL();
+    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
+
+    int size = cellFeed.getEntries().size();
+
+    if (size + missingNames.size() >= worksheet.getColCount()) {
+      worksheet.setColCount(size + missingNames.size());
+    }
+
+    for (String name : missingNames) {
+      CellEntry cellEntry = new CellEntry(1, 1 + size, name);
+      cellFeed.insert(cellEntry);
+      size++;
+    }
+  }
+
+  private List<String> getNames(List<RunnerConfig> runnerConfigs) {
+    List<String> list = Lists.newArrayList();
+    for (RunnerConfig config : runnerConfigs) {
+      list.add(config.toString());
+    }
+    return list;
+  }
+
+  private List<String> getHeaders(SpreadsheetService service, WorksheetEntry worksheet)
+      throws IOException, ServiceException, URISyntaxException {
+    URL cellFeedUrl =
+        new URI(worksheet.getCellFeedUrl().toString() + "?min-row=1&max-row=1").toURL();
+    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
+
+    List<String> headers = Lists.newArrayList();
+
+    for (CellEntry cell : cellFeed.getEntries()) {
+      String value = cell.getCell().getValue();
+      if ("".equals(value)) {
+        continue;
+      }
+      headers.add(value);
+    }
+    return headers;
+  }
+
+  private int getRowIndexForCommit(SpreadsheetService service, WorksheetEntry worksheet,
+      String commitId) throws URISyntaxException, IOException, ServiceException {
+    URL cellFeedUrl =
+        new URI(worksheet.getCellFeedUrl().toString() + "?min-col=1&max-col=1").toURL();
+    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
+
+    for (CellEntry cell : cellFeed.getEntries()) {
+      String value = cell.getCell().getValue();
+      if (commitId.equals(value)) {
+        return cell.getCell().getRow();
+      }
+    }
+    return -1;
+  }
+
+  private Map<String, WorksheetEntry> createSheets(SpreadsheetService service,
+      Set<String> sheetsToCreate) throws IOException, ServiceException {
+    Map<String, WorksheetEntry> map = Maps.newHashMap();
+    if (sheetsToCreate.isEmpty()) {
+      return map;
+    }
+
+    URL worksheetFeedUrl =
+        FeedURLFactory.getDefault().getWorksheetFeedUrl(spreadSheetId, "private", "values");
+    WorksheetFeed feed = service.getFeed(worksheetFeedUrl, WorksheetFeed.class);
+
+    List<String> sortedSheetsToCreate = Lists.newArrayList(sheetsToCreate);
+    // make sure our order of adding the spreadsheets is deterministic (makes testing easier)
+    Collections.sort(sortedSheetsToCreate);
+
+    for (String name : sortedSheetsToCreate) {
+      WorksheetEntry entry = new WorksheetEntry();
+      entry.setTitle(new PlainTextConstruct(name));
+      entry.setRowCount(10000);
+      entry.setColCount(10);
+      map.put(name, feed.insert(entry));
+    }
+
+    return map;
+  }
+
+  @VisibleForTesting
   boolean sleep(int seconds) {
     try {
       Thread.sleep(1000L * seconds);
@@ -179,4 +383,9 @@
       return false;
     }
   }
+
+  @VisibleForTesting
+  Credential authorize() throws IOException, GeneralSecurityException {
+    return OAuthHelper.authorize(oAuthDir, clientJsonSecret);
+  }
 }
diff --git a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/RunnerConfig.java b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/RunnerConfig.java
index e443dba..a916388 100644
--- a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/RunnerConfig.java
+++ b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/RunnerConfig.java
@@ -83,4 +83,11 @@
    * @return the version of the browser for this config.
    */
   String getBrowserVersion();
+
+
+  /**
+   * Returns a human readable representation of this runner.
+   */
+  @Override
+  public String toString();
 }
diff --git a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/SingleRunBenchmarkManager.java b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/SingleRunBenchmarkManager.java
index 9551b5f..ba68085 100644
--- a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/SingleRunBenchmarkManager.java
+++ b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/SingleRunBenchmarkManager.java
@@ -77,7 +77,7 @@
   }
 
   @Override
-  protected void maybeReportResults(String commitId, long commitMsEpoch) {
+  protected void maybeReportResults(String commitId) {
     currentlyRunning = false;
   }
 }
diff --git a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/WebDriverRunner.java b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/WebDriverRunner.java
index 064f139..f2a1598 100644
--- a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/WebDriverRunner.java
+++ b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/manager/WebDriverRunner.java
@@ -77,11 +77,15 @@
   public void run() {
     logger.info("Starting webdriver for " + url);
 
-    DesiredCapabilities capabilities = createCapabilities(config);
+
     RemoteWebDriver driver = null;
     try {
+      DesiredCapabilities capabilities = createCapabilities(config);
+      logger.info("Got capabilities for " + config);
       driver = new RemoteWebDriver(hubURL, capabilities);
+      logger.info("Got driver for " + config);
       driver.navigate().to(url);
+      logger.info("Navigated to " + url);
 
       long startMs = System.currentTimeMillis();
 
diff --git a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/runners/settings/Settings.java b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/runners/settings/Settings.java
index 3a3f1a4..607bbb8 100644
--- a/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/runners/settings/Settings.java
+++ b/compileserver/src/main/java/com/google/gwt/benchmark/compileserver/server/runners/settings/Settings.java
@@ -13,6 +13,9 @@
  */
 package com.google.gwt.benchmark.compileserver.server.runners.settings;
 
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
 import org.apache.commons.io.IOUtils;
 
 import java.io.File;
@@ -61,6 +64,9 @@
 
       settings.mailSettings =
           new MailSettings(mailFrom, mailTo, mailHost, mailUsername, mailPassword);
+      settings.oauthSecret =
+          Files.toString(new File(prop.getProperty("oauth_secret_file")), Charsets.UTF_8);
+      settings.spreadSheetId = prop.getProperty("spreadSheetId");
     } finally {
       IOUtils.closeQuietly(stream);
     }
@@ -92,6 +98,8 @@
   private File gwtSourceLocation;
   private MailSettings mailSettings;
   private int servletContainerPort;
+  private String oauthSecret;
+  private String spreadSheetId;
 
   public File getBenchmarkRootDirectory() {
     return benchmarkRootDirectory;
@@ -153,5 +161,13 @@
     return servletContainerPort;
   }
 
+  public String getOauthSecret() {
+    return oauthSecret;
+  }
+
+  public String getSpreadSheetId() {
+    return spreadSheetId;
+  }
+
   private Settings() {}
 }
diff --git a/compileserver/src/test/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkManagerTest.java b/compileserver/src/test/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkManagerTest.java
index 0a1100f..ddb1c14 100644
--- a/compileserver/src/test/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkManagerTest.java
+++ b/compileserver/src/test/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkManagerTest.java
@@ -142,7 +142,7 @@
         ArgumentCaptor.forClass(ReportProgressHandler.class);
 
     Mockito.when(reporterFactory.create(resultCaptor.capture(), Mockito.anyString(),
-        Mockito.anyLong(), reportProgressHandlerCaptor.capture())).thenReturn(benchmarkReporter);
+        reportProgressHandlerCaptor.capture())).thenReturn(benchmarkReporter);
 
     Assert.assertFalse(manager.isRunning());
 
@@ -387,7 +387,7 @@
         ArgumentCaptor.forClass(ReportProgressHandler.class);
 
     Mockito.when(reporterFactory.create(resultCaptor.capture(), Mockito.anyString(),
-        Mockito.anyLong(), reportProgressHandlerCaptor.capture())).thenReturn(benchmarkReporter);
+        reportProgressHandlerCaptor.capture())).thenReturn(benchmarkReporter);
 
     Assert.assertFalse(manager.isRunning());
 
@@ -487,7 +487,7 @@
         ArgumentCaptor.forClass(ReportProgressHandler.class);
 
     Mockito.when(reporterFactory.create(resultCaptor.capture(), Mockito.anyString(),
-        Mockito.anyLong(), reportProgressHandlerCaptor.capture())).thenReturn(benchmarkReporter);
+        reportProgressHandlerCaptor.capture())).thenReturn(benchmarkReporter);
 
     Assert.assertFalse(manager.isRunning());
 
diff --git a/compileserver/src/test/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkReporterTest.java b/compileserver/src/test/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkReporterTest.java
index 1d6c91f..846689f 100644
--- a/compileserver/src/test/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkReporterTest.java
+++ b/compileserver/src/test/java/com/google/gwt/benchmark/compileserver/server/manager/BenchmarkReporterTest.java
@@ -13,27 +13,42 @@
  */
 package com.google.gwt.benchmark.compileserver.server.manager;
 
-import com.google.gwt.benchmark.common.shared.json.BenchmarkResultJson;
-import com.google.gwt.benchmark.common.shared.json.BenchmarkRunJson;
-import com.google.gwt.benchmark.common.shared.json.JsonFactory;
-import com.google.gwt.benchmark.compileserver.server.manager.BenchmarkReporter.HttpURLConnectionFactory;
-import com.google.gwt.benchmark.compileserver.server.manager.BenchmarkReporter.ReportProgressHandler;
-import com.google.web.bindery.autobean.shared.AutoBean;
-import com.google.web.bindery.autobean.shared.AutoBeanCodex;
+import static com.google.common.truth.Truth.assertThat;
 
-import org.junit.Assert;
+import com.google.api.client.auth.oauth2.Credential;
+import com.google.common.collect.Lists;
+import com.google.gdata.client.spreadsheet.SpreadsheetService;
+import com.google.gdata.data.PlainTextConstruct;
+import com.google.gdata.data.spreadsheet.Cell;
+import com.google.gdata.data.spreadsheet.CellEntry;
+import com.google.gdata.data.spreadsheet.CellFeed;
+import com.google.gdata.data.spreadsheet.WorksheetEntry;
+import com.google.gdata.data.spreadsheet.WorksheetFeed;
+import com.google.gdata.util.ServiceException;
+import com.google.gwt.benchmark.compileserver.server.manager.BenchmarkReporter.ReportProgressHandler;
+import com.google.inject.Provider;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
+import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Test for {@link BenchmarkReporter}.
@@ -42,17 +57,34 @@
 
   private BenchmarkReporter reporter;
   private HashMap<String, BenchmarkRun> results;
-  private HttpURLConnectionFactory urlFactory;
-  private HttpURLConnection urlConnection;
-  private OutputStream outputStream;
   private String commitId;
-  private long commitDate;
   private ReportProgressHandler reportProgressHandler;
+  private SpreadsheetService spreadsheetService;
+  private Provider<SpreadsheetService> spreadsheetServiceProvider;
+  private File oauthDir;
+  private Credential credential;
 
+  private URL SPREAD_SHEET_URL;
+  private URL HEADER_URL1;
+  private URL HEADER_URL2;
+  private URL COMMIT_URL1;
+  private URL COMMIT_URL2;
+
+  @SuppressWarnings("unchecked")
   @Before
-  public void setup() {
+  public void setup() throws MalformedURLException, URISyntaxException {
+
+    SPREAD_SHEET_URL = new URI(
+        "https://spreadsheets.google.com/feeds/worksheets/spreadSheetId/private/values").toURL();
+
+    HEADER_URL1 = new URI("https://foo/module1?min-row=1&max-row=1").toURL();
+    HEADER_URL2 = new URI("https://foo/module2?min-row=1&max-row=1").toURL();
+
+    COMMIT_URL1 = new URI("https://foo/module1?min-col=1&max-col=1").toURL();
+    COMMIT_URL2 = new URI("https://foo/module2?min-col=1&max-col=1").toURL();
+
     commitId = "commitId1";
-    commitDate = 77;
+    int commitDate = 77;
 
     results = new HashMap<>();
     BenchmarkRun benchmarkRun = new BenchmarkRun("module1", commitId, commitDate);
@@ -68,105 +100,289 @@
     benchmarkRun1.addResult(RunnerConfigs.FIREFOX_LINUX, 5);
     results.put("module2", benchmarkRun1);
 
-    urlFactory = Mockito.mock(BenchmarkReporter.HttpURLConnectionFactory.class);
-
-    urlConnection = Mockito.mock(HttpURLConnection.class);
-
-    outputStream = Mockito.mock(OutputStream.class);
-
     reportProgressHandler = Mockito.mock(ReportProgressHandler.class);
 
+    spreadsheetService = mock(SpreadsheetService.class);
+
+    spreadsheetServiceProvider = mock(Provider.class);
+    when(spreadsheetServiceProvider.get()).thenReturn(spreadsheetService);
+
+    oauthDir = mock(File.class);
+
+    credential = mock(Credential.class);
+
   }
 
+  @SuppressWarnings("unchecked")
   @Test
-  public void testSuccessFulPostToServer() throws IOException {
+  public void testAddToExistingSpreadSheets() throws IOException, URISyntaxException,
+      ServiceException {
 
-    Mockito.when(urlFactory.create()).thenReturn(urlConnection);
-    Mockito.when(urlConnection.getOutputStream()).thenReturn(outputStream);
-    Mockito.when(urlConnection.getResponseCode()).thenReturn(200);
+    reporter = new BenchmarkReporter(results, commitId, reportProgressHandler, oauthDir, "secret",
+        spreadsheetServiceProvider, "spreadSheetId") {
 
-    reporter = new BenchmarkReporter(urlFactory, "auth1", results, commitId, commitDate,
-        reportProgressHandler);
+        @Override
+      Credential authorize() throws IOException, GeneralSecurityException {
+        return credential;
+      }
+
+        @Override
+      boolean sleep(int seconds) {
+        return true;
+      }
+    };
+
+    List<CellEntry> headerEntriesList = Lists.<CellEntry> newArrayList(
+        mockCellEntry(1, 1, "commit"), mockCellEntry(1, 2, "linux chrome"),
+        mockCellEntry(1, 3, "linux firefox"));
+
+    WorksheetFeed worksheetFeed = mock(WorksheetFeed.class);
+    when(spreadsheetService.getFeed(SPREAD_SHEET_URL, WorksheetFeed.class)).thenReturn(
+        worksheetFeed);
+
+    // mock two worksheets
+    WorksheetEntry module1Worksheet = mockWorkSheetEntry("module1", "https://foo/module1");
+    WorksheetEntry module2Worksheet = mockWorkSheetEntry("module2", "https://foo/module2");
+    when(worksheetFeed.getEntries()).thenReturn(Arrays.asList(module1Worksheet, module2Worksheet));
+
+    // mock cell feeds of the two work sheets
+    CellFeed cellFeedSheet1 = mock(CellFeed.class);
+    when(cellFeedSheet1.getEntries()).thenReturn(Lists.<CellEntry> newArrayList(),
+        Lists.<CellEntry> newArrayList(), headerEntriesList);
+    when(spreadsheetService.getFeed(HEADER_URL1, CellFeed.class)).thenReturn(cellFeedSheet1);
+    CellFeed cellFeedSheet2 = mock(CellFeed.class);
+    when(cellFeedSheet2.getEntries()).thenReturn(Lists.<CellEntry> newArrayList(),
+        Lists.<CellEntry> newArrayList(), headerEntriesList);
+    when(spreadsheetService.getFeed(HEADER_URL2, CellFeed.class)).thenReturn(cellFeedSheet2);
+
+    // mock queries for commit row
+    List<CellEntry> commitsCol = Lists.<CellEntry> newArrayList(mockCellEntry(1, 1, "commit"));
+    CellFeed commitCol1 = mock(CellFeed.class);
+    when(commitCol1.getEntries()).thenReturn(commitsCol);
+    when(spreadsheetService.getFeed(COMMIT_URL1, CellFeed.class)).thenReturn(commitCol1);
+    CellFeed commitCol2 = mock(CellFeed.class);
+    when(commitCol2.getEntries()).thenReturn(commitsCol);
+    when(spreadsheetService.getFeed(COMMIT_URL2, CellFeed.class)).thenReturn(commitCol2);
+
+    // return mock for the work
+    CellFeed module1CellFeed = mock(CellFeed.class);
+    when(spreadsheetService.getFeed(new URI("https://foo/module1").toURL(), CellFeed.class))
+        .thenReturn(module1CellFeed);
+    CellFeed module2CellFeed = mock(CellFeed.class);
+    when(spreadsheetService.getFeed(new URI("https://foo/module2").toURL(), CellFeed.class))
+        .thenReturn(module2CellFeed);
 
     reporter.run();
 
-    Mockito.verify(outputStream).close();
-    Mockito.verify(urlConnection).setRequestMethod("PUT");
-    Mockito.verify(urlConnection).addRequestProperty("auth", "auth1");
-    ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
-    Mockito.verify(outputStream).write(captor.capture());
+    verify(spreadsheetService).setOAuth2Credentials(credential);
 
-    byte[] jsonBytes = captor.getValue();
-    String jsonString = new String(jsonBytes, "UTF-8");
+    ArgumentCaptor<CellEntry> cellEntryModule1Captor = ArgumentCaptor.forClass(CellEntry.class);
+    verify(module1CellFeed, times(3)).insert(cellEntryModule1Captor.capture());
 
-    AutoBean<BenchmarkRunJson> bean =
-        AutoBeanCodex.decode(JsonFactory.get(), BenchmarkRunJson.class, jsonString);
-    BenchmarkRunJson benchmarkRunJSON = bean.as();
+    List<CellEntry> cellsModule1 = cellEntryModule1Captor.getAllValues();
+    assertThat(cellsModule1.size()).isEqualTo(3);
 
-    Assert.assertEquals(commitId, benchmarkRunJSON.getCommitId());
-    Assert.assertEquals(commitDate, benchmarkRunJSON.getCommitTimeMsEpoch(), 0.0001);
+    assertThat(cellsModule1.get(0).getCell().getRow()).isEqualTo(2);
+    assertThat(cellsModule1.get(0).getCell().getCol()).isEqualTo(1);
+    assertThat(cellsModule1.get(0).getCell().getInputValue()).isEqualTo("commitId1");
 
-    Map<String, List<BenchmarkResultJson>> resultsJSON =
-        benchmarkRunJSON.getResultByBenchmarkName();
+    assertThat(cellsModule1.get(1).getCell().getRow()).isEqualTo(2);
+    assertThat(cellsModule1.get(1).getCell().getCol()).isEqualTo(2);
+    assertThat(cellsModule1.get(1).getCell().getInputValue()).isEqualTo("2.0");
 
-    Assert.assertEquals(2, resultsJSON.size());
-    List<BenchmarkResultJson> module1List = resultsJSON.get("module1");
-    Assert.assertEquals(2, module1List.size());
-    Assert.assertEquals("module1", module1List.get(0).getBenchmarkName());
-    Assert.assertEquals(2, module1List.get(0).getRunsPerSecond(), 0.0001);
-    Assert.assertEquals(RunnerConfigs.CHROME_LINUX.toString(),
-        module1List.get(0).getRunnerId().toString());
-    Assert.assertEquals("module1", module1List.get(1).getBenchmarkName());
-    Assert.assertEquals(3, module1List.get(1).getRunsPerSecond(), 0.0001);
-    Assert.assertEquals(RunnerConfigs.FIREFOX_LINUX.toString(),
-        module1List.get(1).getRunnerId().toString());
+    assertThat(cellsModule1.get(2).getCell().getRow()).isEqualTo(2);
+    assertThat(cellsModule1.get(2).getCell().getCol()).isEqualTo(3);
+    assertThat(cellsModule1.get(2).getCell().getInputValue()).isEqualTo("3.0");
 
-    List<BenchmarkResultJson> module1List2 = resultsJSON.get("module2");
-    Assert.assertEquals("module2", module1List2.get(0).getBenchmarkName());
-    Assert.assertEquals(4, module1List2.get(0).getRunsPerSecond(), 0.0001);
-    Assert.assertEquals(RunnerConfigs.CHROME_LINUX.toString(),
-        module1List2.get(0).getRunnerId().toString());
-    Assert.assertEquals("module2", module1List2.get(1).getBenchmarkName());
-    Assert.assertEquals(5, module1List2.get(1).getRunsPerSecond(), 0.0001);
-    Assert.assertEquals(RunnerConfigs.FIREFOX_LINUX.toString(),
-        module1List2.get(1).getRunnerId().toString());
+    ArgumentCaptor<CellEntry> cellEntryModule2Captor = ArgumentCaptor.forClass(CellEntry.class);
+    verify(module2CellFeed, times(3)).insert(cellEntryModule2Captor.capture());
 
-    Mockito.verify(reportProgressHandler).onCommitReported();
+    List<CellEntry> cellsModule2 = cellEntryModule2Captor.getAllValues();
+    assertThat(cellsModule2.size()).isEqualTo(3);
+
+    assertThat(cellsModule2.get(0).getCell().getRow()).isEqualTo(2);
+    assertThat(cellsModule2.get(0).getCell().getCol()).isEqualTo(1);
+    assertThat(cellsModule2.get(0).getCell().getInputValue()).isEqualTo("commitId1");
+
+    assertThat(cellsModule2.get(1).getCell().getRow()).isEqualTo(2);
+    assertThat(cellsModule2.get(1).getCell().getCol()).isEqualTo(2);
+    assertThat(cellsModule2.get(1).getCell().getInputValue()).isEqualTo("4.0");
+
+    assertThat(cellsModule2.get(2).getCell().getRow()).isEqualTo(2);
+    assertThat(cellsModule2.get(2).getCell().getCol()).isEqualTo(3);
+    assertThat(cellsModule2.get(2).getCell().getInputValue()).isEqualTo("5.0");
   }
 
   @Test
-  public void testFailingRetries() throws IOException {
+  public void createWorkSheets() throws IOException, ServiceException, URISyntaxException {
+    reporter = new BenchmarkReporter(results, commitId, reportProgressHandler, oauthDir, "secret",
+        spreadsheetServiceProvider, "spreadSheetId") {
 
-    Mockito.when(urlFactory.create()).thenReturn(urlConnection);
-    Mockito.when(urlConnection.getOutputStream()).thenReturn(outputStream);
-    Mockito.when(urlConnection.getResponseCode()).thenReturn(500);
+        @Override
+      Credential authorize() throws IOException, GeneralSecurityException {
+        return credential;
+      }
 
+        @Override
+      boolean sleep(int seconds) {
+        return true;
+      }
+    };
+
+    WorksheetFeed worksheetFeed = mock(WorksheetFeed.class);
+    when(spreadsheetService.getFeed(SPREAD_SHEET_URL, WorksheetFeed.class)).thenReturn(
+        worksheetFeed);
+    // no worksheets on server
+    when(worksheetFeed.getEntries()).thenReturn(Lists.<WorksheetEntry> newArrayList());
+
+    // setup mocking for adding worksheets
+    ArgumentCaptor<WorksheetEntry> worksheetEntryCaptor =
+        ArgumentCaptor.forClass(WorksheetEntry.class);
+    WorksheetEntry module1Worksheet = mockWorkSheetEntry("module1", "https://foo/module1");
+    WorksheetEntry module2Worksheet = mockWorkSheetEntry("module2", "https://foo/module2");
+    when(worksheetFeed.insert(worksheetEntryCaptor.capture())).thenReturn(module1Worksheet,
+        module2Worksheet);
+
+    // no headers in worksheets
+    CellFeed emptyHeaderFeed = mockEmptyCellFeed();
+
+    CellFeed feedThatGetsHeadersInsertedModule1 = mockEmptyCellFeed();
+    ArgumentCaptor<CellEntry> cellEntryCaptor1 = ArgumentCaptor.forClass(CellEntry.class);
+    when(feedThatGetsHeadersInsertedModule1.insert(cellEntryCaptor1.capture())).thenReturn(null);
+
+    CellFeed feedThatGetsHeadersInsertedModule2 = mockEmptyCellFeed();
+    ArgumentCaptor<CellEntry> cellEntryCaptor2 = ArgumentCaptor.forClass(CellEntry.class);
+    when(feedThatGetsHeadersInsertedModule2.insert(cellEntryCaptor2.capture())).thenReturn(null);
+
+    // headers
+    List<CellEntry> headerEntriesList = Lists.<CellEntry> newArrayList(
+        mockCellEntry(1, 1, "commit"), mockCellEntry(1, 2, "linux chrome"),
+        mockCellEntry(1, 3, "linux firefox"));
+    CellFeed addedHeaderFeed = mock(CellFeed.class);
+    when(addedHeaderFeed.getEntries()).thenReturn(headerEntriesList);
+
+    // mock queries for commit row
+    List<CellEntry> commitsCol = Lists.<CellEntry> newArrayList(mockCellEntry(1, 1, "commit"));
+    CellFeed commitCol = mock(CellFeed.class);
+    when(commitCol.getEntries()).thenReturn(commitsCol);
+    when(spreadsheetService.getFeed(COMMIT_URL1, CellFeed.class)).thenReturn(commitCol);
+    when(spreadsheetService.getFeed(COMMIT_URL2, CellFeed.class)).thenReturn(commitCol);
+
+    when(spreadsheetService.getFeed(HEADER_URL1, CellFeed.class)).thenReturn(emptyHeaderFeed,
+        feedThatGetsHeadersInsertedModule1, addedHeaderFeed);
+    when(spreadsheetService.getFeed(HEADER_URL2, CellFeed.class)).thenReturn(emptyHeaderFeed,
+        feedThatGetsHeadersInsertedModule2, addedHeaderFeed);
+
+    CellFeed module1AddCellFeed = mockEmptyCellFeed();
+    when(spreadsheetService.getFeed(new URI("https://foo/module1").toURL(), CellFeed.class))
+        .thenReturn(module1AddCellFeed);
+
+    CellFeed module2AddCellFeed = mockEmptyCellFeed();
+    when(spreadsheetService.getFeed(new URI("https://foo/module2").toURL(), CellFeed.class))
+        .thenReturn(module2AddCellFeed);
+
+    ArgumentCaptor<CellEntry> module1AddCellFeedCaptor = ArgumentCaptor.forClass(CellEntry.class);
+    when(module1AddCellFeed.insert(module1AddCellFeedCaptor.capture())).thenReturn(null);
+    ArgumentCaptor<CellEntry> module2AddCellFeedCaptor = ArgumentCaptor.forClass(CellEntry.class);
+    when(module2AddCellFeed.insert(module2AddCellFeedCaptor.capture())).thenReturn(null);
+
+    reporter.run();
+
+    assertHeader(cellEntryCaptor1.getAllValues());
+    assertHeader(cellEntryCaptor2.getAllValues());
+
+    List<CellEntry> addedCellsModule1 = module1AddCellFeedCaptor.getAllValues();
+    assertThat(addedCellsModule1.size()).isEqualTo(3);
+    assertThat(addedCellsModule1.get(0).getCell().getRow()).isEqualTo(2);
+    assertThat(addedCellsModule1.get(0).getCell().getCol()).isEqualTo(1);
+    assertThat(addedCellsModule1.get(0).getCell().getInputValue()).isEqualTo("commitId1");
+    assertThat(addedCellsModule1.get(1).getCell().getRow()).isEqualTo(2);
+    assertThat(addedCellsModule1.get(1).getCell().getCol()).isEqualTo(2);
+    assertThat(addedCellsModule1.get(1).getCell().getInputValue()).isEqualTo("2.0");
+    assertThat(addedCellsModule1.get(2).getCell().getRow()).isEqualTo(2);
+    assertThat(addedCellsModule1.get(2).getCell().getCol()).isEqualTo(3);
+    assertThat(addedCellsModule1.get(2).getCell().getInputValue()).isEqualTo("3.0");
+
+    List<CellEntry> addedCellsModule2 = module2AddCellFeedCaptor.getAllValues();
+    assertThat(addedCellsModule2.size()).isEqualTo(3);
+    assertThat(addedCellsModule2.get(0).getCell().getRow()).isEqualTo(2);
+    assertThat(addedCellsModule2.get(0).getCell().getCol()).isEqualTo(1);
+    assertThat(addedCellsModule2.get(0).getCell().getInputValue()).isEqualTo("commitId1");
+    assertThat(addedCellsModule2.get(1).getCell().getRow()).isEqualTo(2);
+    assertThat(addedCellsModule2.get(1).getCell().getCol()).isEqualTo(2);
+    assertThat(addedCellsModule2.get(1).getCell().getInputValue()).isEqualTo("4.0");
+    assertThat(addedCellsModule2.get(2).getCell().getRow()).isEqualTo(2);
+    assertThat(addedCellsModule2.get(2).getCell().getCol()).isEqualTo(3);
+    assertThat(addedCellsModule2.get(2).getCell().getInputValue()).isEqualTo("5.0");
+
+    List<WorksheetEntry> addedWorkSheets = worksheetEntryCaptor.getAllValues();
+
+    assertThat(addedWorkSheets.size()).isEqualTo(2);
+    assertThat(addedWorkSheets.get(0).getTitle().getPlainText()).isEqualTo("module1");
+    assertThat(addedWorkSheets.get(1).getTitle().getPlainText()).isEqualTo("module2");
+
+    verify(spreadsheetService).setOAuth2Credentials(credential);
+  }
+
+  private void assertHeader(List<CellEntry> headerEntries) {
+    assertThat(headerEntries.size()).isEqualTo(3);
+    assertThat(headerEntries.get(0).getCell().getRow()).isEqualTo(1);
+    assertThat(headerEntries.get(0).getCell().getCol()).isEqualTo(1);
+    assertThat(headerEntries.get(0).getCell().getInputValue()).isEqualTo("commit");
+
+    assertThat(headerEntries.get(1).getCell().getRow()).isEqualTo(1);
+    assertThat(headerEntries.get(1).getCell().getCol()).isEqualTo(2);
+    assertThat(headerEntries.get(1).getCell().getInputValue()).isEqualTo("linux chrome");
+
+    assertThat(headerEntries.get(2).getCell().getRow()).isEqualTo(1);
+    assertThat(headerEntries.get(2).getCell().getCol()).isEqualTo(3);
+    assertThat(headerEntries.get(2).getCell().getInputValue()).isEqualTo("linux firefox");
+  }
+
+  private WorksheetEntry mockWorkSheetEntry(String title, String url) throws MalformedURLException,
+      URISyntaxException {
+    WorksheetEntry worksheetEntry = mock(WorksheetEntry.class);
+    when(worksheetEntry.getTitle()).thenReturn(new PlainTextConstruct(title));
+    when(worksheetEntry.getCellFeedUrl()).thenReturn(new URI(url).toURL());
+    return worksheetEntry;
+  }
+
+  @Test
+  public void testFailingRetries() {
     final List<Integer> waitingTimes = new ArrayList<>();
+    reporter = new BenchmarkReporter(results, commitId, reportProgressHandler, oauthDir, "secret",
+        spreadsheetServiceProvider, "spreadSheetId") {
+        @Override
+      void doPostResult() throws Exception {
+        throw new Exception();
+      }
 
-    reporter = new BenchmarkReporter(urlFactory, "auth1", results, commitId, commitDate,
-        reportProgressHandler) {
-
-      @Override
+        @Override
       boolean sleep(int seconds) {
         waitingTimes.add(seconds);
         return true;
       }
     };
-
     reporter.run();
-
-    Mockito.verify(outputStream, Mockito.times(BenchmarkReporter.WAITING_TIME_SECONDS.length))
-        .close();
-    Mockito.verify(urlConnection, Mockito.times(BenchmarkReporter.WAITING_TIME_SECONDS.length))
-        .setRequestMethod("PUT");
-
-    Assert.assertEquals(BenchmarkReporter.WAITING_TIME_SECONDS.length, waitingTimes.size());
-
-    for (int i = 0; i < BenchmarkReporter.WAITING_TIME_SECONDS.length; i++) {
-      Assert.assertEquals(BenchmarkReporter.WAITING_TIME_SECONDS[i],
-          waitingTimes.get(i).intValue());
-    }
-
+    assertThat(waitingTimes).containsExactlyElementsIn(BenchmarkReporter.WAITING_TIME_SECONDS);
     Mockito.verify(reportProgressHandler).onPermanentFailure();
   }
+
+  private CellEntry mockCellEntry(int row, int col, String value) {
+    Cell cell = mock(Cell.class);
+    when(cell.getValue()).thenReturn(value);
+    when(cell.getCol()).thenReturn(Integer.valueOf(col));
+    when(cell.getRow()).thenReturn(Integer.valueOf(row));
+
+    CellEntry cellEntry = mock(CellEntry.class);
+    when(cellEntry.getCell()).thenReturn(cell);
+
+    return cellEntry;
+  }
+
+  private CellFeed mockEmptyCellFeed() {
+    CellFeed cellFeed = mock(CellFeed.class);
+    when(cellFeed.getEntries()).thenReturn(Lists.<CellEntry> newArrayList());
+    return cellFeed;
+  }
 }
diff --git a/dashboard/pom.xml b/dashboard/pom.xml
deleted file mode 100644
index 92f7393..0000000
--- a/dashboard/pom.xml
+++ /dev/null
@@ -1,335 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>com.google.gwt.benchmark</groupId>
-  <artifactId>gwt-benchmark-dashboard</artifactId>
-  <version>1.0-SNAPSHOT</version>
-  <packaging>war</packaging>
-
-  <parent>
-    <groupId>com.google.gwt.benchmark</groupId>
-    <artifactId>gwt-benchmark-parent</artifactId>
-    <version>1.0-SNAPSHOT</version>
-  </parent>
-
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <gwtversion>2.6.1</gwtversion>
-    <appengine.target.version>1.9.1</appengine.target.version>
-  </properties>
-  
-  <!-- Add a local repo until we get an updated version of gwt-visualization into central -->
-  <repositories>
-    <repository>
-        <id>project.local</id>
-        <name>project</name>
-        <url>file:${project.basedir}/repo</url>
-    </repository>
-  </repositories>
-
-  <dependencies>
-
-    <dependency>
-      <groupId>com.google.gwt.benchmark</groupId>
-      <artifactId>gwt-benchmark-common</artifactId>
-      <version>1.0-SNAPSHOT</version>
-    </dependency>
-
-    <dependency>
-      <groupId>com.google.inject.extensions</groupId>
-      <artifactId>guice-servlet</artifactId>
-      <version>3.0</version>
-    </dependency>
-
-    <dependency>
-      <groupId>com.google.gwt.inject</groupId>
-      <artifactId>gin</artifactId>
-      <version>2.1.2</version>
-      <classifier />
-    </dependency>
-
-    <dependency>
-      <groupId>com.google.gwt.google-apis</groupId>
-      <artifactId>gwt-visualization</artifactId>
-      <version>1.1.2</version>
-    </dependency>
-
-    <!-- Compile/runtime dependencies -->
-    <dependency>
-      <groupId>com.google.appengine</groupId>
-      <artifactId>appengine-api-1.0-sdk</artifactId>
-      <version>${appengine.target.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>servlet-api</artifactId>
-      <version>2.5</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>jstl</groupId>
-      <artifactId>jstl</artifactId>
-      <version>1.2</version>
-    </dependency>
-
-    <dependency>
-      <groupId>org.json</groupId>
-      <artifactId>json</artifactId>
-      <version>20090211</version>
-    </dependency>
-
-    <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-      <version>1.7</version>
-    </dependency>
-
-    <!-- Test Dependencies -->
-    <dependency>
-      <groupId>com.google.appengine</groupId>
-      <artifactId>appengine-testing</artifactId>
-      <version>${appengine.target.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.google.appengine</groupId>
-      <artifactId>appengine-api-stubs</artifactId>
-      <version>${appengine.target.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.google.appengine</groupId>
-      <artifactId>appengine-tools-sdk</artifactId>
-      <version>${appengine.target.version}</version>
-      <scope>test</scope>
-    </dependency>
-
-    <dependency>
-      <groupId>com.google.appengine</groupId>
-      <artifactId>appengine-api-labs</artifactId>
-      <version>${appengine.target.version}</version>
-      <scope>test</scope>
-    </dependency>
-
-    <dependency>
-      <groupId>commons-lang</groupId>
-      <artifactId>commons-lang</artifactId>
-      <version>2.3</version>
-    </dependency>
-
-    <dependency>
-      <groupId>javax.jdo</groupId>
-      <artifactId>jdo-api</artifactId>
-      <version>3.0.1</version>
-    </dependency>
-    <dependency>
-      <groupId>org.datanucleus</groupId>
-      <artifactId>datanucleus-core</artifactId>
-      <version>3.1.1</version>
-      <scope>runtime</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.datanucleus</groupId>
-      <artifactId>datanucleus-api-jdo</artifactId>
-      <version>3.1.2</version>
-    </dependency>
-    <dependency>
-      <groupId>com.google.appengine.orm</groupId>
-      <artifactId>datanucleus-appengine</artifactId>
-      <version>2.1.2</version>
-    </dependency>
-
-    <dependency>
-      <groupId>com.google.gwt</groupId>
-      <artifactId>gwt-user</artifactId>
-      <version>${gwtversion}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.google.gwt</groupId>
-      <artifactId>gwt-servlet</artifactId>
-      <version>${gwtversion}</version>
-    </dependency>
-    <dependency>
-      <groupId>com.google.web.bindery</groupId>
-      <artifactId>requestfactory-server</artifactId>
-      <version>${gwtversion}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>javax.validation</groupId>
-      <artifactId>validation-api</artifactId>
-      <version>1.0.0.GA</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>javax.validation</groupId>
-      <artifactId>validation-api</artifactId>
-      <version>1.0.0.GA</version>
-      <classifier>sources</classifier>
-      <scope>provided</scope>
-    </dependency>
-
-    <dependency>
-      <groupId>javax.persistence</groupId>
-      <artifactId>persistence-api</artifactId>
-      <version>1.0</version>
-    </dependency>
-
-    <dependency>
-      <groupId>commons-io</groupId>
-      <artifactId>commons-io</artifactId>
-      <version>2.4</version>
-    </dependency>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava-gwt</artifactId>
-      <version>17.0</version>
-    </dependency>
-
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>4.11</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-all</artifactId>
-      <version>1.9.5</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.google.gwt.gwtmockito</groupId>
-      <artifactId>gwtmockito</artifactId>
-      <version>1.1.3</version>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <version>2.5.1</version>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <configuration>
-          <source>1.7</source>
-          <target>1.7</target>
-        </configuration>
-      </plugin>
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-war-plugin</artifactId>
-        <version>2.3</version>
-        <configuration>
-          <!-- <archiveClasses>true</archiveClasses> -->
-          <webResources>
-            <!-- in order to interpolate version from pom into appengine-web.xml -->
-            <resource>
-              <directory>${basedir}/src/main/webapp/WEB-INF</directory>
-              <filtering>true</filtering>
-              <targetPath>WEB-INF</targetPath>
-            </resource>
-          </webResources>
-        </configuration>
-      </plugin>
-
-      <plugin>
-        <groupId>com.google.appengine</groupId>
-        <artifactId>appengine-maven-plugin</artifactId>
-        <version>${appengine.target.version}</version>
-      </plugin>
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-eclipse-plugin</artifactId>
-        <version>2.8</version>
-
-        <configuration>
-          <downloadSources>true</downloadSources>
-          <downloadJavadocs>false</downloadJavadocs>
-          <buildOutputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</buildOutputDirectory>
-          <projectnatures>
-            <projectnature>org.eclipse.jdt.core.javanature</projectnature>
-            <projectnature>com.google.gdt.eclipse.core.webAppNature</projectnature>
-            <nature>com.google.appengine.eclipse.core.gaeNature</nature>
-            <nature>com.google.gwt.eclipse.core.gwtNature</nature>
-          </projectnatures>
-          <buildcommands>
-            <buildcommand>org.eclipse.jdt.core.javabuilder</buildcommand>
-            <buildcommand>com.google.gdt.eclipse.core.webAppProjectValidator</buildcommand>
-
-            <buildcommand>com.google.appengine.eclipse.core.projectValidator</buildcommand>
-            <buildcommand>com.google.gwt.eclipse.core.gwtProjectValidator</buildcommand>
-            <buildcommand>com.google.appengine.eclipse.core.enhancerbuilder</buildcommand>
-
-          </buildcommands>
-          <classpathContainers>
-            <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
-            <classpathContainer>com.google.appengine.eclipse.core.GAE_CONTAINER</classpathContainer>
-            <classpathContainer>com.google.gwt.eclipse.core.GWT_CONTAINER</classpathContainer>
-          </classpathContainers>
-          <excludes>
-            <exclude>com.google.gwt:gwt-user</exclude>
-            <exclude>com.google.gwt:gwt-dev</exclude>
-            <exclude>javax.validation:validation-api</exclude>
-            <exclude>com.google.appengine:appengine-api-1.0-sdk</exclude>
-            <exclude>org.datanucleus:datanucleus-core</exclude>
-            <exclude>org.datanucleus:datanucleus-enhancer</exclude>
-            <exclude>org.datanucleus:datanucleus-jpa</exclude>
-            <exclude>org.datanucleus:datanucleus-api-jdo</exclude>
-            <exclude>com.google.appengine.orm:datanucleus-appengine</exclude>
-            <exclude>com.google.appengine:appengine-tools-sdk</exclude>
-          </excludes>
-
-
-        </configuration>
-      </plugin>
-
-      <plugin>
-        <groupId>org.datanucleus</groupId>
-        <artifactId>maven-datanucleus-plugin</artifactId>
-        <version>3.1.2</version>
-        <configuration>
-          <log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
-          <verbose>false</verbose>
-          <fork>false</fork>
-        </configuration>
-        <executions>
-          <execution>
-            <phase>process-classes</phase>
-            <goals>
-              <goal>enhance</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>gwt-maven-plugin</artifactId>
-        <version>${gwtversion}</version>
-        <executions>
-          <execution>
-            <configuration>
-              <module>com.google.gwt.benchmark.dashboard.Dashboard</module>
-              <runTarget>index.html</runTarget>
-              <copyWebapp>true</copyWebapp>
-            </configuration>
-            <goals>
-              <goal>compile</goal>
-              <goal>test</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-</project>
-
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar
deleted file mode 100644
index 81e7cf1..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar
+++ /dev/null
Binary files differ
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar.md5 b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar.md5
deleted file mode 100644
index 5188a09..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-57e2b982a3c1b0a1909749b35115d5e4
\ No newline at end of file
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar.sha1 b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar.sha1
deleted file mode 100644
index 8351e15..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b645c02f8214dd00603716af74b70ab749f17c1f
\ No newline at end of file
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom
deleted file mode 100644
index 5b9f2fa..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>com.google.gwt.google-apis</groupId>
-  <artifactId>gwt-visualization</artifactId>
-  <version>1.1.2</version>
-</project>
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom.md5 b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom.md5
deleted file mode 100644
index 1344f4f..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-c095ac29765a8a37d987293ec680f8c7
\ No newline at end of file
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom.sha1 b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom.sha1
deleted file mode 100644
index d818e27..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/1.1.2/gwt-visualization-1.1.2.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-5b680d4445732b17a96365124104b85a8711f233
\ No newline at end of file
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml
deleted file mode 100644
index 65a9df8..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<metadata>
-  <groupId>com.google.gwt.google-apis</groupId>
-  <artifactId>gwt-visualization</artifactId>
-  <versioning>
-    <release>1.1.2</release>
-    <versions>
-      <version>1.1.2</version>
-    </versions>
-    <lastUpdated>20140606123626</lastUpdated>
-  </versioning>
-</metadata>
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml.md5 b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml.md5
deleted file mode 100644
index 0ecff84..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml.md5
+++ /dev/null
@@ -1 +0,0 @@
-7a732e7e8ea2b1f368075d519a6b1a72
\ No newline at end of file
diff --git a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml.sha1 b/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml.sha1
deleted file mode 100644
index a34594e..0000000
--- a/dashboard/repo/com/google/gwt/google-apis/gwt-visualization/maven-metadata.xml.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b4b13fd0513940f20dda8697d73e9222cb150ddf
\ No newline at end of file
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/Dashboard.gwt.xml b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/Dashboard.gwt.xml
deleted file mode 100644
index 55d255b..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/Dashboard.gwt.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.6.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.6.0/distro-source/core/src/gwt-module.dtd">
-<module rename-to="dashboard">
-  <inherits name="com.google.gwt.user.User" />
-  <inherits name="com.google.gwt.activity.Activity"/>
-  <inherits name="com.google.gwt.inject.Inject"/>
-  <inherits name='com.google.gwt.visualization.Visualization'/>
-  <inherits name="com.google.common.collect.Collect"/>
-  <inherits name="com.google.gwt.benchmark.common.Common" />
-  <source path="client" excludes='**/*Test.java'/>
-  <source path="shared" />
-  <entry-point class="com.google.gwt.benchmark.dashboard.client.DashBoardEntryPoint"/>
-
-  <!-- This can be removed for GWT 2.7 -->
-  <add-linker name="xsiframe" />
-  <set-configuration-property name="devModeRedirectEnabled"
-    value="true" />
-</module>
\ No newline at end of file
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/DashBoardEntryPoint.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/DashBoardEntryPoint.java
deleted file mode 100644
index 257b032..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/DashBoardEntryPoint.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.client;
-
-import com.google.gwt.benchmark.dashboard.client.ui.GraphComposite;
-import com.google.gwt.benchmark.dashboard.client.ui.ModuleOverviewComposite;
-import com.google.gwt.core.client.EntryPoint;
-import com.google.gwt.core.shared.GWT;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.inject.client.AbstractGinModule;
-import com.google.gwt.inject.client.GinModules;
-import com.google.gwt.inject.client.Ginjector;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.inject.Provides;
-import com.google.web.bindery.event.shared.EventBus;
-import com.google.web.bindery.event.shared.SimpleEventBus;
-
-import javax.inject.Singleton;
-
-/**
- * EntryPoint for dashboard UI.
- */
-public class DashBoardEntryPoint implements EntryPoint {
-
-  private Injector injector;
-  private SimplePanel simplePanel;
-
-
-  @GinModules(Module.class)
-  public interface Injector extends Ginjector {
-
-    EventBus getEventBus();
-
-    ModuleOverviewComposite getModuleOverviewComposite();
-
-    GraphComposite getGraphComposite();
-  }
-
-  public static class Module extends AbstractGinModule {
-
-    @Override
-    protected void configure() {
-      bind(EventBus.class).to(SimpleEventBus.class).in(Singleton.class);
-    }
-
-    @Provides
-    protected Label createLabel() {
-      return new Label();
-    }
-
-    @Provides
-    protected CheckBox createCheckBox() {
-      return new CheckBox();
-    }
-  }
-
-  @Override
-  public void onModuleLoad() {
-    injector = GWT.create(Injector.class);
-    simplePanel = new SimplePanel();
-    RootPanel.get().add(simplePanel);
-
-
-    String token = History.getToken();
-    handleHistory(token);
-
-    History.addValueChangeHandler(new ValueChangeHandler<String>() {
-
-      @Override
-      public void onValueChange(ValueChangeEvent<String> event) {
-          handleHistory(event.getValue());
-      }
-    });
-  }
-
-  private void handleHistory(String token) {
-    if(token.startsWith("!graph?")) {
-      GraphComposite graphComposite = injector.getGraphComposite();
-      simplePanel.setWidget(graphComposite);
-      graphComposite.start();
-    } else {
-      ModuleOverviewComposite moduleOverviewComposite = injector.getModuleOverviewComposite();
-      simplePanel.setWidget(moduleOverviewComposite);
-      moduleOverviewComposite.start();
-    }
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphComposite.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphComposite.java
deleted file mode 100644
index 5f45d05..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphComposite.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.client.ui;
-
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Joiner;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableMap;
-import com.google.gwt.benchmark.dashboard.shared.service.DashboardServiceAsync;
-import com.google.gwt.benchmark.dashboard.shared.service.dto.BenchmarkResultsTable;
-import com.google.gwt.core.shared.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.visualization.client.AbstractDataTable.ColumnType;
-import com.google.gwt.visualization.client.DataTable;
-import com.google.gwt.visualization.client.visualizations.corechart.AxisOptions;
-import com.google.gwt.visualization.client.visualizations.corechart.Options;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * This widget fetches benchmark runs from the server and renders a graph.
- */
-public class GraphComposite extends Composite {
-
-  interface Binder extends UiBinder<Widget, GraphComposite> {
-  }
-
-  private static Binder uiBinder = GWT.create(Binder.class);
-
-  private boolean graphWidgetLoaded;
-  private boolean dataReady;
-  private BenchmarkResultsTable result;
-  private Set<String> runnerIds = new TreeSet<>();
-
-  private final List<HandlerRegistration> handlers = new ArrayList<HandlerRegistration>();
-
-  private String benchmarkName;
-  private int week;
-  private int year;
-
-  private final HistoryAccessor history;
-  private final DashboardServiceAsync service;
-  private final Provider<CheckBox> checkboxProvider;
-
-  @UiField
-  Button backButton;
-
-  @UiField
-  Button forwardButton;
-
-  @UiField
-  Panel checkBoxContainer;
-
-  @UiField
-  GraphWidget graphWidget;
-
-  @UiField
-  Label errorLabel;
-
-  @UiField
-  Label loadingLabel;
-
-  @UiField
-  Widget container;
-
-  @UiField
-  Label weekLabel;
-
-  @Inject
-  public GraphComposite(DashboardServiceAsync service, Provider<CheckBox> checkboxProvider,
-      HistoryAccessor history) {
-    this.service = service;
-    this.checkboxProvider = checkboxProvider;
-    this.history = history;
-    initWidget(uiBinder.createAndBindUi(this));
-  }
-
-  public void start() {
-    graphWidgetLoaded = false;
-    dataReady = false;
-    if (parseHistory(history.getToken())) {
-      loadData();
-
-      graphWidget.init(new Runnable() {
-
-          @Override
-        public void run() {
-          graphWidgetLoaded = true;
-          maybeRender();
-        }
-      });
-    }
-  }
-
-  private void maybeRender() {
-    if (!graphWidgetLoaded || !dataReady) {
-      return;
-    }
-    resetView();
-    renderCheckBoxes();
-    renderGraph();
-  }
-
-  private void renderGraph() {
-    if (result.getColumnCount() == 0) {
-      graphWidget.clear();
-      return;
-    }
-
-    DataTable data = graphWidget.createData();
-    data.addColumn(ColumnType.STRING, "Commits");
-
-    for (String runnerId : result.getAllRunnerIds()) {
-      if (!runnerIds.contains(runnerId)) {
-        continue;
-      }
-      data.addColumn(ColumnType.NUMBER, runnerId);
-    }
-
-    data.addRows(result.getRowCount());
-    for (int i = 0; i < result.getRowCount(); i++) {
-      data.setValue(i, 0, result.getCommitIds().get(i));
-    }
-
-    int skippedColumn = 0;
-    for (int columnIndex = 0; columnIndex < result.getColumnCount(); columnIndex++) {
-      if (!runnerIds.contains(result.getRunnerId(columnIndex))) {
-        skippedColumn++;
-        continue;
-      }
-      for (int rowIndex = 0; rowIndex < result.getRowCount(); rowIndex++) {
-        data.setValue(rowIndex, columnIndex + 1 - skippedColumn,
-            result.getRunsPerSecond(columnIndex, rowIndex));
-      }
-    }
-
-    Options options = graphWidget.createOptions();
-    options.setWidth(800);
-    options.setHeight(600);
-
-    options.setTitle(result.getBenchmarkName());
-
-    AxisOptions vAxisOptions = graphWidget.createAxisOptions();
-    vAxisOptions.setMinValue(0);
-    options.setVAxisOptions(vAxisOptions);
-
-    AxisOptions hAxisOptions = graphWidget.createAxisOptions();
-    hAxisOptions.setTextPosition("none");
-    options.setHAxisOptions(hAxisOptions);
-
-    graphWidget.displayChart(options, data);
-  }
-
-  private void renderCheckBoxes() {
-    for (final String runner : result.getAllRunnerIds()) {
-      CheckBox checkBox = checkboxProvider.get();
-      checkBox.setValue(runnerIds.contains(runner));
-      checkBox.setText(runner);
-      handlers.add(checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
-
-        @Override
-        public void onValueChange(ValueChangeEvent<Boolean> event) {
-          onCheckboxClicked(runner, event.getValue());
-        }
-      }));
-      checkBoxContainer.add(checkBox);
-    }
-  }
-
-  private void resetView() {
-    for (HandlerRegistration hr : handlers) {
-      hr.removeHandler();
-    }
-    handlers.clear();
-    checkBoxContainer.clear();
-    container.setVisible(true);
-    weekLabel.setText(result.getWeekName());
-  }
-
-  private void onCheckboxClicked(String runners, boolean value) {
-    if (value) {
-      runnerIds.add(runners);
-    } else {
-      runnerIds.remove(runners);
-    }
-
-    if (runnerIds.isEmpty()) {
-      runnerIds = new TreeSet<>(result.getAllRunnerIds());
-    }
-    history.newItem(createHistoryToken(), false);
-    maybeRender();
-  }
-
-  private void loadData() {
-    loadingLabel.setVisible(true);
-    errorLabel.setVisible(false);
-    container.setVisible(false);
-
-    AsyncCallback<BenchmarkResultsTable> callback = new AsyncCallback<BenchmarkResultsTable>() {
-
-      @Override
-      public void onFailure(Throwable caught) {
-        errorLabel.setText("Error while fetching graphs: " + caught.getMessage());
-        errorLabel.setVisible(true);
-        loadingLabel.setVisible(false);
-      }
-
-      @Override
-      public void onSuccess(BenchmarkResultsTable result) {
-        loadingLabel.setVisible(false);
-        GraphComposite.this.result = result;
-        if (GraphComposite.this.runnerIds.isEmpty()) {
-          GraphComposite.this.runnerIds =  new TreeSet<String>(result.getAllRunnerIds());
-        }
-        GraphComposite.this.week = result.getWeek();
-        GraphComposite.this.year = result.getYear();
-        dataReady = true;
-        maybeRender();
-      }
-    };
-
-    if (week == -1 || year == -1) {
-      service.getLatestGraphs(benchmarkName, callback);
-    } else {
-      service.getGraphs(benchmarkName, week, year, callback);
-    }
-  }
-
-  //Visible for testing
-  @UiHandler("backButton")
-  void onBackButtonClicked(@SuppressWarnings("unused") ClickEvent e) {
-    // until the next time we have 54 weeks in a year (2028),
-    // this system will not be running anymore
-    // and even then we won't be working over new year either.
-    if (week - 1 < 1) {
-      year -= 1;
-      week = 53;
-    } else {
-      week -= 1;
-    }
-
-    history.newItem(createHistoryToken(), false);
-    loadData();
-  }
-
-  // Visible for testing
-  @UiHandler("forwardButton")
-  void onForwardButtonClicked(@SuppressWarnings("unused") ClickEvent e) {
-    // until the next time we have 54 weeks in a year (2028),
-    // this system will not be running anymore
-    // and even then we won't be working over new year either.
-    if (week + 1 > 53) {
-      year += 1;
-      week = 1;
-    } else {
-      week += 1;
-    }
-
-    history.newItem(createHistoryToken(), false);
-    loadData();
-  }
-
-  private String createHistoryToken() {
-    ImmutableMap<String, Object> params = ImmutableMap.<String, Object> of(
-        "benchmark", benchmarkName, "w", week, "y", year, "rids", runnerIds);
-    return "!graph?" + Joiner.on("&").withKeyValueSeparator("=").join(params);
-  }
-
-  private boolean parseHistory(String token) {
-    if (!token.startsWith("!graph?")) {
-      history.newItem("", true);
-      return false;
-    }
-
-    token = token.substring("!graph?".length());
-    Map<String, String> split = null;
-    try {
-      split = Splitter.on("&").withKeyValueSeparator("=").split(token);
-    } catch (IllegalArgumentException e) {
-      history.newItem("", true);
-      return false;
-    }
-
-    benchmarkName = split.get("benchmark");
-    if (benchmarkName == null) {
-      history.newItem("", true);
-      return false;
-    }
-
-    String weekString = split.get("w");
-    String yearString = split.get("y");
-
-    if (weekString == null || yearString == null) {
-      year = -1;
-      week = -1;
-      runnerIds = new TreeSet<>();
-      return true;
-    }
-
-    week = Integer.parseInt(weekString);
-    year = Integer.parseInt(yearString);
-
-    Splitter splitter = Splitter.on(",").trimResults(
-        CharMatcher.anyOf("[]").or(CharMatcher.WHITESPACE));
-
-    runnerIds = new TreeSet<String>(splitter.splitToList(split.get("rids")));
-    return true;
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphComposite.ui.xml b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphComposite.ui.xml
deleted file mode 100644
index 68bcb7f..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphComposite.ui.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
-    xmlns:g='urn:import:com.google.gwt.user.client.ui'
-    xmlns:gw='urn:import:com.google.gwt.benchmark.dashboard.client.ui'>
-
-  <g:HTMLPanel>
-    <g:HTMLPanel ui:field="container">
-      <g:Button text="back" ui:field="backButton" />
-      <g:InlineLabel ui:field="weekLabel" />
-      <g:Button text="forward" ui:field="forwardButton"/>
-      <g:FlowPanel ui:field="checkBoxContainer"/>
-      <gw:GraphWidget ui:field="graphWidget" />
-    </g:HTMLPanel>
-    <g:Label ui:field="errorLabel"/>
-    <g:Label ui:field="loadingLabel">
-      Loading
-    </g:Label>
-  </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphWidget.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphWidget.java
deleted file mode 100644
index 0d80b8b..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/GraphWidget.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.client.ui;
-
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.visualization.client.DataTable;
-import com.google.gwt.visualization.client.VisualizationUtils;
-import com.google.gwt.visualization.client.visualizations.corechart.AxisOptions;
-import com.google.gwt.visualization.client.visualizations.corechart.Options;
-import com.google.gwt.visualization.client.visualizations.corechart.LineChart;
-
-/**
- * This widget provides access to the visualization API.
- */
-public class GraphWidget extends Composite {
-
-  private SimplePanel chartContainer = new SimplePanel();
-
-  public GraphWidget() {
-    initWidget(chartContainer);
-  }
-
-  public DataTable createData() {
-    return DataTable.create();
-  }
-
-  public Options createOptions() {
-    return Options.create();
-  }
-
-  public AxisOptions createAxisOptions() {
-    return AxisOptions.create();
-  }
-
-  public void clear() {
-    chartContainer.clear();
-  }
-
-  public void displayChart(Options options, DataTable data) {
-    LineChart lineChart = new LineChart(data, options);
-    chartContainer.setWidget(lineChart);
-  }
-
-  public void init(Runnable r) {
-    VisualizationUtils.loadVisualizationApi(r, LineChart.PACKAGE);
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/HistoryAccessor.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/HistoryAccessor.java
deleted file mode 100644
index b6ffc5b..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/HistoryAccessor.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.client.ui;
-
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window;
-
-/**
- * HistoryAccessor is a thin wrapper around GWT's history implementation.
- */
-public class HistoryAccessor {
-  public String getToken() {
-    return History.getToken();
-  }
-
-  public void newItem(String token, boolean issueEvent) {
-    History.newItem(token, issueEvent);
-  }
-
-  public void replaceItem(String token, @SuppressWarnings("unused") boolean issueEvent) {
-    // TODO switch to new History implementation in GWT 2.7
-    // This will not work without xsiframe linker on some browsers
-    Window.Location.replace("!#" + token);
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewComposite.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewComposite.java
deleted file mode 100644
index 8767b16..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewComposite.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.client.ui;
-
-import com.google.gwt.benchmark.dashboard.shared.service.DashboardServiceAsync;
-import com.google.gwt.core.shared.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.inject.Provider;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * This widget displays the overview of all benchmarks.
- */
-public class ModuleOverviewComposite extends Composite {
-
-  interface Binder extends UiBinder<Widget, ModuleOverviewComposite> {
-  }
-
-  private static Binder uiBinder = GWT.create(Binder.class);
-
-  private final DashboardServiceAsync service;
-  private final Provider<Label> labelProvider;
-  private final List<HandlerRegistration> handlers = new ArrayList<>();
-  private final HistoryAccessor history;
-
-  @UiField
-  Panel contentContainer;
-
-  @UiField
-  Label errorLabel;
-
-  @UiField
-  Label loadingLabel;
-
-  @Inject
-  public ModuleOverviewComposite(DashboardServiceAsync service, Provider<Label> labelProvider, HistoryAccessor history) {
-    this.service = service;
-    this.labelProvider = labelProvider;
-    this.history = history;
-    initWidget(uiBinder.createAndBindUi(this));
-  }
-
-  public void start() {
-    loadBenchmarks();
-  }
-
-  private void loadBenchmarks() {
-    errorLabel.setVisible(false);
-    loadingLabel.setVisible(true);
-    contentContainer.setVisible(false);
-
-    service.getLatestBenchmarkNames(new AsyncCallback<ArrayList<String>>() {
-
-      @Override
-      public void onFailure(Throwable caught) {
-        errorLabel.setVisible(true);
-        errorLabel.setText("Can not load benchmarks");
-        loadingLabel.setVisible(false);
-      }
-
-      @Override
-      public void onSuccess(ArrayList<String> result) {
-        renderGraphList(result);
-      }
-    });
-  }
-
-  private void renderGraphList(List<String> benchmarkNames) {
-    loadingLabel.setVisible(false);
-    contentContainer.setVisible(true);
-    contentContainer.clear();
-
-    for (final String benchmarkName : benchmarkNames) {
-      Label label = labelProvider.get();
-      handlers.add(label.addClickHandler(new ClickHandler() {
-        @Override
-        public void onClick(ClickEvent event) {
-          ModuleOverviewComposite.this.onClick(benchmarkName);
-        }
-      }));
-      label.setText(benchmarkName);
-      contentContainer.add(label);
-    }
-  }
-
-  private void onClick(String module) {
-    history.newItem("!graph?benchmark=" + module, true);
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewComposite.ui.xml b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewComposite.ui.xml
deleted file mode 100644
index beba7cf..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewComposite.ui.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'>
-  <g:HTMLPanel>
-    <g:FlowPanel ui:field="contentContainer"/>
-    <g:Label ui:field="errorLabel" />
-    <g:Label ui:field="loadingLabel" />
-  </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/AuthController.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/AuthController.java
deleted file mode 100644
index 5e77394..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/AuthController.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.controller;
-
-import com.google.appengine.api.datastore.DatastoreFailureException;
-import com.google.appengine.api.datastore.DatastoreService;
-import com.google.appengine.api.datastore.DatastoreServiceFactory;
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.EntityNotFoundException;
-import com.google.appengine.api.datastore.KeyFactory;
-
-import java.util.ConcurrentModificationException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * AuthContoller stores and verifies an auth token.
- */
-public class AuthController {
-
-  private static final Logger logger = Logger.getLogger(AuthController.class.getName());
-
-  public void updateAuth(String auth) throws ControllerException {
-
-    if (auth == null || auth.length() < 6) {
-      throw new ControllerException("Auth empty or too short (min. 6 chars)");
-    }
-
-    try {
-      DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
-      Entity entity = new Entity(KeyFactory.createKey("Auth", "auth"));
-      entity.setProperty("value", auth);
-      datastore.put(entity);
-    } catch (ConcurrentModificationException | DatastoreFailureException e) {
-      logger.log(Level.WARNING, "Can not persist new auth", e);
-      throw new ControllerException("Can not persist new auth", e);
-    }
-  }
-
-  public boolean validateAuth(String auth) {
-    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
-
-    try {
-      Entity entity = datastore.get(KeyFactory.createKey("Auth", "auth"));
-      String authFromStore = (String)entity.getProperty("value");
-      if(authFromStore.equals(auth)) {
-        return true;
-      }
-      logger.severe("Auth failed validation");
-      return false;
-    } catch (EntityNotFoundException e) {
-      logger.log(Level.SEVERE, "No auth entry in datastore", e);
-      return false;
-    }
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/BenchmarkController.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/BenchmarkController.java
deleted file mode 100644
index f872215..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/BenchmarkController.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.controller;
-
-import com.google.appengine.api.datastore.DatastoreFailureException;
-import com.google.appengine.api.datastore.DatastoreService;
-import com.google.appengine.api.datastore.DatastoreServiceFactory;
-import com.google.appengine.api.datastore.DatastoreTimeoutException;
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.FetchOptions;
-import com.google.appengine.api.datastore.Key;
-import com.google.appengine.api.datastore.PreparedQuery;
-import com.google.appengine.api.datastore.Query;
-import com.google.appengine.api.datastore.Query.Filter;
-import com.google.appengine.api.datastore.Query.FilterOperator;
-import com.google.appengine.api.datastore.Query.SortDirection;
-import com.google.appengine.api.datastore.Transaction;
-import com.google.appengine.api.taskqueue.Queue;
-import com.google.appengine.api.taskqueue.QueueFactory;
-import com.google.appengine.api.taskqueue.TaskOptions;
-import com.google.gwt.benchmark.common.shared.json.BenchmarkResultJson;
-import com.google.gwt.benchmark.common.shared.json.BenchmarkRunJson;
-import com.google.gwt.benchmark.dashboard.server.domain.BenchmarkGraph;
-import com.google.gwt.benchmark.dashboard.server.domain.BenchmarkResult;
-import com.google.gwt.benchmark.dashboard.server.domain.BenchmarkRun;
-import com.google.gwt.benchmark.dashboard.server.guice.DashboardServletGuiceModule;
-import com.google.gwt.benchmark.dashboard.shared.service.dto.BenchmarkResultsTable;
-
-import org.apache.commons.lang.ArrayUtils;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.ConcurrentModificationException;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * This controller is responsible for adding and retrieving benchmark data.
- */
-public class BenchmarkController {
-
-  private static final Logger logger = Logger.getLogger(BenchmarkController.class.getName());
-
-  private static class ToPersist {
-
-    private final long commitTimeMsEpoch;
-    private final BenchmarkRun benchmarkRun;
-    private final List<BenchmarkResult> benchmarkResults;
-
-    public ToPersist(BenchmarkRun benchmarkRun, List<BenchmarkResult> benchmarkResults,
-        long commitTimeMsEpoch) {
-      this.benchmarkRun = benchmarkRun;
-      this.benchmarkResults = Collections.unmodifiableList(benchmarkResults);
-      this.commitTimeMsEpoch = commitTimeMsEpoch;
-    }
-  }
-
-  private static class WeekSpan {
-    private final int commitWeek;
-    private final int commitYear;
-    private final long weekStartMsEpoch;
-    private final long weekEndMsEpoch;
-
-    public WeekSpan(int commitWeek, int commitYear, long weekStartMsEpoch, long weekEndMsEpoch) {
-      this.commitWeek = commitWeek;
-      this.commitYear = commitYear;
-      this.weekStartMsEpoch = weekStartMsEpoch;
-      this.weekEndMsEpoch = weekEndMsEpoch;
-    }
-  }
-
-  private static class BenchmarkGraphData {
-    private final List<String> commitIds;
-    private final List<Double> runsPerSecond;
-
-    public BenchmarkGraphData(List<String> commitIds, List<Double> runsPerSecond) {
-      this.commitIds = Collections.unmodifiableList(commitIds);
-      this.runsPerSecond = Collections.unmodifiableList(runsPerSecond);
-    }
-  }
-
-  /**
-   * Returns the name of each benchmark included in the most
-   * recent benchmark run.
-   */
-  public List<String> getLatestBenchmarkNames() throws ControllerException {
-    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
-
-    Query query = BenchmarkResult.createQueryLastest();
-
-    PreparedQuery prepare = ds.prepare(query);
-    List<Entity> list = prepare.asList(FetchOptions.Builder.withLimit(1));
-
-    if (list.size() == 0) {
-      throw new ControllerException("No entries in datastore");
-    }
-
-    BenchmarkRun run = new BenchmarkRun(list.get(0));
-    List<Key> results = run.getResults();
-    Map<Key, Entity> map = ds.get(results);
-    Set<String> modules = new HashSet<>();
-
-    for (Entry<Key, Entity> entry : map.entrySet()) {
-      BenchmarkResult result = new BenchmarkResult(entry.getValue());
-      modules.add(result.getBenchmarkName());
-    }
-
-    ArrayList<String> resultList = new ArrayList<>(modules);
-    Collections.sort(resultList);
-    return resultList;
-  }
-
-  public BenchmarkResultsTable getGraphs(String benchmarkName, int week, int year) {
-    Query query = BenchmarkGraph.createQuery(benchmarkName, week, year);
-    List<BenchmarkGraph> graphs = executeQuery(query);
-    return createResponse(benchmarkName, graphs, week, year);
-  }
-
-  public void addBenchmarkResult(BenchmarkRunJson benchmarkRunJSON) throws ControllerException {
-    ToPersist toPersist = createDomainObjects(benchmarkRunJSON);
-    persistBenchmarkRun(toPersist, 3);
-    addUpdateRequestToTaskQueue(toPersist);
-  }
-
-  public void updateGraph(long commitTimeMsEpoch, String benchmarkName, String runnerId)
-      throws ControllerException {
-
-    WeekSpan weekSpan = createWeekSpan(commitTimeMsEpoch);
-
-    BenchmarkGraphData benchmarkGraphData = calculateNewGraphData(benchmarkName, runnerId,
-        weekSpan.weekStartMsEpoch, weekSpan.weekEndMsEpoch);
-
-    putBenchmarkGraph(benchmarkName, runnerId, weekSpan.commitWeek, weekSpan.commitYear,
-        benchmarkGraphData.commitIds, benchmarkGraphData.runsPerSecond);
-  }
-
-  private BenchmarkResultsTable createResponse(String benchmarkName,
-      List<BenchmarkGraph> graphs, int week, int year) {
-    Collections.sort(graphs, new Comparator<BenchmarkGraph>() {
-
-      @Override
-      public int compare(BenchmarkGraph o1, BenchmarkGraph o2) {
-        return o1.getRunnerId().compareTo(o2.getRunnerId());
-      }
-    });
-    String weekName = String.format("Week: %d Year: %d", week, year);
-    List<String> allRunnerIds = new ArrayList<String>();
-
-    List<String> commitIds = new ArrayList<String>();
-    List<double[]> runnerResultList = new ArrayList<>();
-
-    boolean first = true;
-
-    for (BenchmarkGraph benchmarkGraph : graphs) {
-      if (first) {
-        commitIds.addAll(benchmarkGraph.getCommitIds());
-        first = false;
-      } else {
-        // if an update is in progress some graph entities might have been updated
-        // and some not. In this case we are just going to return an empty response
-        if (commitIds.size() != benchmarkGraph.getCommitIds().size()) {
-          return BenchmarkResultsTable.create(benchmarkName, weekName, year, week,
-              Collections.<String>emptyList(),Collections.<String>emptyList(),
-              Collections.<double[]>emptyList());
-        }
-      }
-
-      allRunnerIds.add(benchmarkGraph.getRunnerId());
-      Double[] runsPerSecond = benchmarkGraph.getRunsPerSecond().toArray(new Double[] {});
-      double[] runs = ArrayUtils.toPrimitive(runsPerSecond);
-      runnerResultList.add(runs);
-    }
-
-    return BenchmarkResultsTable.create(benchmarkName, weekName, year, week, commitIds,
-        allRunnerIds, runnerResultList);
-  }
-
-  private List<BenchmarkGraph> executeQuery(Query query) {
-    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
-    PreparedQuery prepare = ds.prepare(query);
-
-    List<Entity> entityList = prepare.asList(FetchOptions.Builder.withDefaults());
-
-    List<BenchmarkGraph> list = new ArrayList<>();
-    for (Entity entity : entityList) {
-      list.add(new BenchmarkGraph(entity));
-    }
-    return list;
-  }
-
-
-
-  private BenchmarkGraphData calculateNewGraphData(String benchmarkName, String runnerId,
-      long weekStartMsEpoch, long weekEndMsEpoch) {
-
-    DatastoreService dataStore = DatastoreServiceFactory.getDatastoreService();
-    Query query =
-        new Query(BenchmarkRun.NAME).addSort("commitTimeMsEpoch", SortDirection.ASCENDING);
-    Filter startFilter = new Query.FilterPredicate("commitTimeMsEpoch",
-        FilterOperator.GREATER_THAN_OR_EQUAL, weekStartMsEpoch);
-    Filter endFilter =
-        new Query.FilterPredicate("commitTimeMsEpoch", FilterOperator.LESS_THAN, weekEndMsEpoch);
-
-    Filter compositeFilter = Query.CompositeFilterOperator.and(startFilter, endFilter);
-    query.setFilter(compositeFilter);
-    PreparedQuery prepare = dataStore.prepare(query);
-
-    List<Entity> entityList = prepare.asList(FetchOptions.Builder.withDefaults());
-
-    ArrayList<Key> keys = new ArrayList<Key>(entityList.size());
-    List<String> commitIds = new ArrayList<>(entityList.size());
-    for (Entity entity : entityList) {
-      keys.add(BenchmarkResult.createKey(entity.getKey(), benchmarkName, runnerId));
-      commitIds.add(new BenchmarkRun(entity).getCommitId());
-    }
-
-    Map<Key, Entity> brMap = dataStore.get(keys);
-    List<BenchmarkResult> results = new ArrayList<>(brMap.size());
-
-    for (Key key : keys) {
-      results.add(new BenchmarkResult(brMap.get(key)));
-    }
-
-    List<Double> runsPerSecond = new ArrayList<>(entityList.size());
-    for (BenchmarkResult benchmarkResult : results) {
-      runsPerSecond.add(benchmarkResult.getRunsPerSecond());
-    }
-
-    return new BenchmarkGraphData(commitIds, runsPerSecond);
-  }
-
-  private void putBenchmarkGraph(String benchmarkName, String runnerId, int week, int year,
-      List<String> commitIds, List<Double> runsPerSecond) throws ControllerException {
-    BenchmarkGraph graph = new BenchmarkGraph(benchmarkName, runnerId, week, year);
-    graph.setRunsPerSecond(runsPerSecond);
-    graph.setCommitIds(commitIds);
-    try {
-      DatastoreService dataStore = DatastoreServiceFactory.getDatastoreService();
-      dataStore.put(graph.getEntity());
-    } catch (DatastoreFailureException | ConcurrentModificationException e) {
-      throw new ControllerException("Can not persist BenchmarkGraph", e);
-    }
-  }
-
-  private WeekSpan createWeekSpan(long commitTimeMsEpoch) {
-    Calendar cal = Calendar.getInstance();
-    cal.setTime(new Date(commitTimeMsEpoch));
-    int week = cal.get(Calendar.WEEK_OF_YEAR);
-    int year = cal.get(Calendar.YEAR);
-
-    Calendar calendar = Calendar.getInstance();
-    calendar.clear();
-    calendar.set(Calendar.WEEK_OF_YEAR, week);
-    calendar.set(Calendar.YEAR, year);
-    long startSearch = calendar.getTimeInMillis();
-
-    calendar.add(Calendar.DAY_OF_YEAR, 7);
-    long endSearch = calendar.getTimeInMillis();
-
-    return new WeekSpan(week, year, startSearch, endSearch);
-  }
-
-  private void addUpdateRequestToTaskQueue(ToPersist toPersist) {
-    Queue queue = QueueFactory.getQueue("graph-queue");
-    for (BenchmarkResult benchmarkResult : toPersist.benchmarkResults) {
-      TaskOptions taskOptions = TaskOptions.Builder.withUrl(
-          DashboardServletGuiceModule.GRAPH_QUEUE_URL).param("commitTimeMsEpoch",
-          String.valueOf(toPersist.commitTimeMsEpoch)).param("benchmarkName",
-          benchmarkResult.getBenchmarkName()).param("runnerId", benchmarkResult.getRunnerId());
-      queue.add(taskOptions);
-    }
-  }
-
-  private ToPersist createDomainObjects(BenchmarkRunJson benchmarkRunJSON) {
-    String commitId = benchmarkRunJSON.getCommitId();
-    long commitTimeMsEpoch = Math.round(benchmarkRunJSON.getCommitTimeMsEpoch());
-    BenchmarkRun benchmarkRun = new BenchmarkRun(commitId, commitTimeMsEpoch);
-    List<BenchmarkResult> brToPersist = new ArrayList<>();
-
-    HashSet<String> runnerIds = new HashSet<>();
-    Map<String, List<BenchmarkResultJson>> results = benchmarkRunJSON.getResultByBenchmarkName();
-
-    for (Entry<String, List<BenchmarkResultJson>> entry : results.entrySet()) {
-
-      String moduleName = entry.getKey();
-      List<BenchmarkResultJson> listResults = entry.getValue();
-      for (BenchmarkResultJson benchmarkResultJSON : listResults) {
-        runnerIds.add(benchmarkResultJSON.getRunnerId());
-        BenchmarkResult benchmarkResult = new BenchmarkResult(benchmarkRun.getKey(), moduleName,
-            benchmarkResultJSON.getRunnerId());
-        benchmarkResult.setRunsPerSecond(benchmarkResultJSON.getRunsPerSecond());
-        brToPersist.add(benchmarkResult);
-      }
-    }
-
-    benchmarkRun.setRunnerIds(new ArrayList<>(runnerIds));
-
-    return new ToPersist(benchmarkRun, brToPersist, commitTimeMsEpoch);
-  }
-
-  private void persistBenchmarkRun(ToPersist toPersist, int retryCount) throws ControllerException {
-    for(int i = 0; i < retryCount; i++) {
-        logger.info(String.format("persistBenchmarkRun try %d", (i+1)));
-        if (persistBenchmarkRun(toPersist)) {
-          logger.info(String.format("persistBenchmarkRun try %d succeded", (i+1)));
-          return;
-        }
-        logger.info(String.format("persistBenchmarkRun try %d failed", (i+1)));
-    }
-
-    logger.warning((String.format("persistBenchmarkRun gave up after %d retries", retryCount)));
-    throw new ControllerException(
-        (String.format("persistBenchmarkRun gave up after %d retries", retryCount)));
-  }
-
-  private boolean persistBenchmarkRun(ToPersist toPersist) {
-    // If a client will try to persist the same benchmark run twice, we will simply
-    // update the existing entry in the datastore, effectively meaning last entry wins.
-    // There is an uncovered edge case: If a benchmark run is reuploaded with fewer executed
-    // benchmarks these entries will be left in the datastore.
-
-    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
-    Transaction transaction = null;
-
-    try {
-      transaction = datastore.beginTransaction();
-      List<Entity> entities = new ArrayList<>();
-      for (BenchmarkResult br : toPersist.benchmarkResults) {
-        entities.add(br.getEntity());
-      }
-      List<Key> list = datastore.put(entities);
-      BenchmarkRun benchmarkRun = toPersist.benchmarkRun;
-      benchmarkRun.setResults(list);
-      datastore.put(benchmarkRun.getEntity());
-      transaction.commit();
-      return true;
-    } catch (DatastoreTimeoutException | DatastoreFailureException
-        | ConcurrentModificationException e) {
-      logger.log(Level.WARNING, "Can not persist benchmark results", e);
-      return false;
-    } finally {
-      if (transaction != null && transaction.isActive()) {
-        transaction.rollback();
-      }
-    }
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/ControllerException.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/ControllerException.java
deleted file mode 100644
index 5232860..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/controller/ControllerException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.controller;
-
-/**
- * Generic base exception for all controllers.
- */
-public class ControllerException extends Exception {
-
-  public ControllerException(String message) {
-    super(message);
-  }
-
-  public ControllerException(String message, Throwable e) {
-    super(message, e);
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkGraph.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkGraph.java
deleted file mode 100644
index 78ab75c..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkGraph.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.domain;
-
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.Key;
-import com.google.appengine.api.datastore.KeyFactory;
-import com.google.appengine.api.datastore.Query;
-import com.google.appengine.api.datastore.Query.Filter;
-import com.google.appengine.api.datastore.Query.FilterOperator;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A BenchmarkGraph contains all results for a certain module and runner for one week.
- */
-public class BenchmarkGraph {
-
-  public static final String NAME = "Graph";
-
-  public static Key createKey(String benchmarkName, String runnerId, int week, int year) {
-    return KeyFactory.createKey(NAME,
-        BenchmarkGraph.createName(benchmarkName, runnerId, week, year));
-  }
-
-  private static String createName(String module, String runnerId, int week, int year) {
-    return module + "_" + runnerId + "_" + week + "_" + year;
-  }
-
-
-  public  static Query createQuery(String benchmarkName, int week, int year) {
-    Query query = new Query(BenchmarkGraph.NAME);
-    Filter moduleFilter = new Query.FilterPredicate("module", FilterOperator.EQUAL, benchmarkName);
-    Filter weekFilter = new Query.FilterPredicate("week", FilterOperator.EQUAL, week);
-    Filter yearFilter = new Query.FilterPredicate("year", FilterOperator.EQUAL, year);
-
-    Filter compositeFilter =
-        Query.CompositeFilterOperator.and(moduleFilter, weekFilter, yearFilter);
-
-    query.setFilter(compositeFilter);
-    return query;
-  }
-
-  private Entity entity;
-
-  /**
-   * Creates a new, empty graph.
-   */
-  public BenchmarkGraph(String module, String runnerId, int week, int year) {
-    entity = new Entity(createKey(module, runnerId, week, year));
-    setModule(module);
-    setYear(year);
-    setWeek(week);
-    setCommitIds(new ArrayList<String>());
-    setRunsPerSecond(new ArrayList<Double>());
-    setRunnerId(runnerId);
-  }
-
-  /**
-   * Wraps an existing graph loaded from the datastore.
-   */
-  public BenchmarkGraph(Entity entity) {
-    this.entity = entity;
-  }
-
-  private void setWeek(int week) {
-    entity.setProperty("week", week);
-  }
-
-  public int getWeek() {
-    return ((Long) entity.getProperty("week")).intValue();
-  }
-
-  private void setYear(int year) {
-    entity.setProperty("year", year);
-  }
-
-  public int getYear() {
-    return ((Long) entity.getProperty("year")).intValue();
-  }
-
-  @SuppressWarnings("unchecked")
-  public List<String> getCommitIds() {
-    return Collections.unmodifiableList((List<String>) entity.getProperty("commitIds"));
-  }
-
-  public void setCommitIds(List<String> commitIds) {
-    entity.setProperty("commitIds", commitIds);
-  }
-
-  private void setModule(String module) {
-    entity.setProperty("module", module);
-  }
-
-  public String getModule() {
-    return (String) entity.getProperty("module");
-  }
-
-  public String getRunnerId() {
-    return (String) entity.getProperty("runnerId");
-  }
-
-  private void setRunnerId(String runnerId) {
-    entity.setProperty("runnerId", runnerId);
-  }
-
-  public void setRunsPerSecond(List<Double> runsPerSecond) {
-    entity.setProperty("runsPerSecond", runsPerSecond);
-  }
-
-  @SuppressWarnings("unchecked")
-  public List<Double> getRunsPerSecond() {
-    return Collections.unmodifiableList((List<Double>) entity.getProperty("runsPerSecond"));
-  }
-
-  public Entity getEntity() {
-    return entity;
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkResult.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkResult.java
deleted file mode 100644
index c6ee7e6..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkResult.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.domain;
-
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.Key;
-import com.google.appengine.api.datastore.KeyFactory;
-import com.google.appengine.api.datastore.Query;
-import com.google.appengine.api.datastore.Query.SortDirection;
-
-/**
- * A BenchmarkResult contains the runs per minute for one module on one runner.
- */
-public class BenchmarkResult {
-
-  public static Key createKey(Key run, String benchmarkName, String runnerId) {
-    String name = createName(benchmarkName, runnerId);
-    return KeyFactory.createKey(run, NAME, name);
-  }
-
-  private static String createName(String benchmarkName, String runnerId) {
-    return benchmarkName + "&" + runnerId;
-  }
-
-  public static Query createQueryLastest() {
-    Query query = new Query(BenchmarkRun.NAME);
-    query.addSort("commitTimeMsEpoch", SortDirection.DESCENDING);
-    return query;
-  }
-
-  public static final String NAME = "BenchmarkResult";
-
-  private Entity entity;
-
-  public BenchmarkResult(Key run, String benchmarkName, String runnerId) {
-    entity = new Entity(createKey(run, benchmarkName, runnerId));
-    entity.setProperty("runnerId", runnerId);
-    entity.setProperty("benchmarkName", benchmarkName);
-    setRunsPerSecond(0);
-  }
-
-  public BenchmarkResult(Entity entity) {
-    this.entity = entity;
-  }
-
-  public String getBenchmarkName() {
-    return (String) entity.getProperty("benchmarkName");
-  }
-
-  public String getRunnerId() {
-    return (String) entity.getProperty("runnerId");
-  }
-
-  public void setRunsPerSecond(double runsPerMinute) {
-    entity.setProperty("runsPerSecond", runsPerMinute);
-  }
-
-  public double getRunsPerSecond() {
-    return (double) entity.getProperty("runsPerSecond");
-  }
-
-  public Key getKey() {
-    return entity.getKey();
-  }
-
-  public Entity getEntity() {
-    return entity;
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkRun.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkRun.java
deleted file mode 100644
index 8590332..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/domain/BenchmarkRun.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.domain;
-
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.Key;
-import com.google.appengine.api.datastore.KeyFactory;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A BenchmarkRun contains all information for one execution of all benchmarks.
- */
-public class BenchmarkRun {
-
-  public static final String NAME = "BenchmarkRun";
-
-  public static Key createKey(String commitId) {
-    return KeyFactory.createKey(NAME, commitId);
-  }
-
-  private Entity entity;
-
-  public BenchmarkRun(String commitId, long commitTimeMsEpoch) {
-    entity = new Entity(createKey(commitId));
-    entity.setProperty("commitId", commitId);
-    entity.setProperty("commitTimeMsEpoch", commitTimeMsEpoch);
-  }
-
-  public BenchmarkRun(Entity entity) {
-    this.entity = entity;
-  }
-
-  public void setRunnerIds(List<String> runnerIds) {
-    entity.setProperty("runnerIds", runnerIds);
-  }
-
-  @SuppressWarnings("unchecked")
-  public List<String> getRunnerIds() {
-    return Collections.unmodifiableList((List<String>) entity.getProperty("runnerIds"));
-  }
-
-  @SuppressWarnings("unchecked")
-  public List<Key> getResults() {
-    return Collections.unmodifiableList((List<Key>) entity.getProperty("results"));
-  }
-
-  public void setResults(List<Key> results) {
-    entity.setProperty("results", results);
-  }
-
-  public String getCommitId() {
-    return (String) entity.getProperty("commitId");
-  }
-
-  public long getCommitTimeMsEpoch() {
-    return (long) entity.getProperty("commitTimeMsEpoch");
-  }
-
-  public Key getKey() {
-    return entity.getKey();
-  }
-
-  public Entity getEntity() {
-    return entity;
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/guice/DashboardServletGuiceModule.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/guice/DashboardServletGuiceModule.java
deleted file mode 100644
index fffc74c..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/guice/DashboardServletGuiceModule.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.guice;
-
-import com.google.gwt.benchmark.dashboard.server.service.DashBoardServiceImpl;
-import com.google.gwt.benchmark.dashboard.server.servlets.AddBenchmarkResultServlet;
-import com.google.gwt.benchmark.dashboard.server.servlets.AuthServlet;
-import com.google.gwt.benchmark.dashboard.server.servlets.GraphUpdateWorkerServlet;
-import com.google.inject.servlet.ServletModule;
-
-/**
- * Guice module for the Dashboard.
- */
-public class DashboardServletGuiceModule extends ServletModule {
-
-  public static final String GRAPH_QUEUE_URL = "/tasks/graph-queue";
-
-  @Override
-  protected void configureServlets() {
-    serve("/post_result").with(AddBenchmarkResultServlet.class);
-    serve("/admin/").with(AuthServlet.class);
-    serve(GRAPH_QUEUE_URL).with(GraphUpdateWorkerServlet.class);
-    serve("/dashboard/data/service").with(DashBoardServiceImpl.class);
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/guice/GuiceServletConfig.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/guice/GuiceServletConfig.java
deleted file mode 100644
index 6bd2efa..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/guice/GuiceServletConfig.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.guice;
-
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.servlet.GuiceServletContextListener;
-
-/**
- * Servlet context listener, will be invoked by the servlet container.
- */
-public class GuiceServletConfig extends GuiceServletContextListener {
-
-  @Override
-  protected Injector getInjector() {
-    return Guice.createInjector(new DashboardServletGuiceModule());
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/service/DashBoardServiceImpl.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/service/DashBoardServiceImpl.java
deleted file mode 100644
index ded8fe4..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/service/DashBoardServiceImpl.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.service;
-
-import com.google.gwt.benchmark.common.shared.service.ServiceException;
-import com.google.gwt.benchmark.dashboard.server.controller.BenchmarkController;
-import com.google.gwt.benchmark.dashboard.server.controller.ControllerException;
-import com.google.gwt.benchmark.dashboard.shared.service.DashboardService;
-import com.google.gwt.benchmark.dashboard.shared.service.dto.BenchmarkResultsTable;
-import com.google.gwt.user.server.rpc.RemoteServiceServlet;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Implementation for GWT service.
- */
-@Singleton
-public class DashBoardServiceImpl extends RemoteServiceServlet implements DashboardService {
-
-  private static final Logger logger = Logger.getLogger(DashBoardServiceImpl.class.getName());
-
-  private BenchmarkController controller;
-
-  @Inject
-  public DashBoardServiceImpl(BenchmarkController controller) {
-    this.controller = controller;
-  }
-
-  @Override
-  public ArrayList<String> getLatestBenchmarkNames() throws ServiceException {
-    try {
-      return new ArrayList<>(controller.getLatestBenchmarkNames());
-    } catch (ControllerException e) {
-      logger.log(Level.WARNING, "Can not load modules", e);
-      throw new ServiceException("Can not load modules");
-    }
-  }
-
-  @Override
-  public BenchmarkResultsTable getLatestGraphs(String benchmarkName) throws ServiceException {
-    Calendar cal = Calendar.getInstance();
-    int week = cal.get(Calendar.WEEK_OF_YEAR);
-    int year = cal.get(Calendar.YEAR);
-    return controller.getGraphs(benchmarkName, week, year);
-  }
-
-  @Override
-  public BenchmarkResultsTable getGraphs(String benchmarkName, int week, int year)
-      throws ServiceException {
-      return controller.getGraphs(benchmarkName, week, year);
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/AddBenchmarkResultServlet.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/AddBenchmarkResultServlet.java
deleted file mode 100644
index b4d23b3..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/AddBenchmarkResultServlet.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.servlets;
-
-import com.google.gwt.benchmark.common.shared.json.BenchmarkRunJson;
-import com.google.gwt.benchmark.common.shared.json.JsonFactory;
-import com.google.gwt.benchmark.dashboard.server.controller.AuthController;
-import com.google.gwt.benchmark.dashboard.server.controller.BenchmarkController;
-import com.google.inject.Singleton;
-import com.google.web.bindery.autobean.shared.AutoBean;
-import com.google.web.bindery.autobean.shared.AutoBeanCodex;
-
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * A put request on this servlet will add a new benchmark to the dashboard.
- */
-@Singleton
-public class AddBenchmarkResultServlet extends HttpServlet {
-
-  private static final Logger logger = Logger.getLogger(AddBenchmarkResultServlet.class.getName());
-
-  private final BenchmarkController benchmarkController;
-  private final AuthController authController;
-
-  @Inject
-  public AddBenchmarkResultServlet(AuthController authController, BenchmarkController benchmarkController) {
-    this.authController = authController;
-    this.benchmarkController = benchmarkController;
-  }
-
-  @Override
-  protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
-      IOException {
-
-    String auth = req.getHeader("auth");
-    if (!authController.validateAuth(auth)) {
-      resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-      return;
-    }
-
-    BenchmarkRunJson benchmarkRunJSON = null;
-    String json = null;
-    try {
-      json = IOUtils.toString(req.getInputStream(), "UTF-8");
-      AutoBean<BenchmarkRunJson> bean =
-          AutoBeanCodex.decode(JsonFactory.get(), BenchmarkRunJson.class, json);
-      benchmarkRunJSON = bean.as();
-    } catch (Exception e) {
-      logger.log(Level.WARNING, "Can not deserialize JSON", e);
-      if (json != null) {
-        logger.warning(json);
-      }
-      resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-      resp.getWriter().write("Can't parse JSON, see App Engine log for details.");
-      return;
-    }
-
-    try {
-
-      benchmarkController.addBenchmarkResult(benchmarkRunJSON);
-      resp.setStatus(HttpServletResponse.SC_OK);
-    } catch (Exception e) {
-      logger.log(Level.WARNING, "Can not add benchmark results", e);
-      resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-    }
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/AuthServlet.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/AuthServlet.java
deleted file mode 100644
index f1541f6..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/AuthServlet.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.servlets;
-
-import com.google.gwt.benchmark.dashboard.server.controller.AuthController;
-import com.google.gwt.benchmark.dashboard.server.controller.ControllerException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * This servlet can update the auth phrase required to post benchmarks.
- */
-@Singleton
-public class AuthServlet extends HttpServlet {
-
-  private final AuthController authController;
-
-  @Inject
-  public AuthServlet(AuthController authController) {
-    this.authController = authController;
-  }
-
-  @Override
-  protected void doGet(HttpServletRequest request, HttpServletResponse response)
-      throws ServletException, IOException {
-    request.getRequestDispatcher("/admin/change_pwd_form.html").forward(request, response);
-  }
-
-  @Override
-  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
-      IOException {
-    String newPassword = req.getParameter("password");
-    try {
-      authController.updateAuth(newPassword);
-    } catch (ControllerException e) {
-      resp.getWriter().print(e.getMessage());
-    }
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/GraphUpdateWorkerServlet.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/GraphUpdateWorkerServlet.java
deleted file mode 100644
index 42464a0..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/server/servlets/GraphUpdateWorkerServlet.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.servlets;
-
-import com.google.gwt.benchmark.dashboard.server.controller.BenchmarkController;
-import com.google.gwt.benchmark.dashboard.server.controller.ControllerException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * This servlet handles request for the graph update queue.
- */
-@Singleton
-public class GraphUpdateWorkerServlet extends HttpServlet {
-
-  private static final Logger logger = Logger.getLogger(GraphUpdateWorkerServlet.class.getName());
-
-  private BenchmarkController controller;
-
-
-  @Inject
-  public GraphUpdateWorkerServlet(BenchmarkController controller) {
-    this.controller = controller;
-  }
-
-  @Override
-  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
-      IOException {
-
-    long commitTimeMsEpoch = Long.parseLong(request.getParameter("commitTimeMsEpoch"));
-    String benchmarkName = request.getParameter("benchmarkName");
-    String runnerId = request.getParameter("runnerId");
-
-    logger.info(String.format("Received update request for graph (%s %s %d", benchmarkName, runnerId, commitTimeMsEpoch));
-
-    try {
-      controller.updateGraph(commitTimeMsEpoch, benchmarkName, runnerId);
-    } catch (ControllerException e) {
-      logger.log(Level.WARNING, "Can not update graph", e);
-      throw new ServletException("Can not update graph", e);
-    }
-  }
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/DashboardService.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/DashboardService.java
deleted file mode 100644
index abf9d9f..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/DashboardService.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.google.gwt.benchmark.dashboard.shared.service;
-
-import com.google.gwt.benchmark.common.shared.service.ServiceException;
-import com.google.gwt.benchmark.dashboard.shared.service.dto.BenchmarkResultsTable;
-import com.google.gwt.user.client.rpc.RemoteService;
-import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
-
-import java.util.ArrayList;
-
-/**
- * Service interface implemented server side.
- */
-@RemoteServiceRelativePath("data/service")
-public interface DashboardService extends RemoteService {
-  ArrayList<String> getLatestBenchmarkNames() throws ServiceException;
-
-  BenchmarkResultsTable getLatestGraphs(String benchmarkName) throws ServiceException;
-
-  BenchmarkResultsTable getGraphs(String benchmarkName, int week, int year)
-      throws ServiceException;
-
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/DashboardServiceAsync.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/DashboardServiceAsync.java
deleted file mode 100644
index 2e26209..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/DashboardServiceAsync.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.google.gwt.benchmark.dashboard.shared.service;
-
-import com.google.gwt.benchmark.dashboard.shared.service.dto.BenchmarkResultsTable;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-import java.util.ArrayList;
-
-/**
- * Service interface to be injected into every object that needs data from the server.
- */
-public interface DashboardServiceAsync {
-  void getLatestBenchmarkNames(AsyncCallback<ArrayList<String>> callback);
-
-  void getLatestGraphs(String module, AsyncCallback<BenchmarkResultsTable> callback);
-
-  void getGraphs(String module, int week, int year,
-      AsyncCallback<BenchmarkResultsTable> callback);
-}
diff --git a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/dto/BenchmarkResultsTable.java b/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/dto/BenchmarkResultsTable.java
deleted file mode 100644
index b5421dc..0000000
--- a/dashboard/src/main/java/com/google/gwt/benchmark/dashboard/shared/service/dto/BenchmarkResultsTable.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.shared.service.dto;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.common.collect.Lists;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * A table of results for one benchmark and one week.
- * There is a row for each commit at which the benchmark was run, and a column for each variation
- * of the benchmark.
- * <p>
- * The columns are in chronological order; the most recent is first. The rows are ordered by their
- * corresponding runner ids.
- */
-public class BenchmarkResultsTable implements Serializable {
-
-  public static BenchmarkResultsTable create(String benchmarkName, String weekName, int year,
-      int week, Collection<String> commitIds, Collection<String> runnerIds,
-      Collection<double[]> cellData) {
-    checkArgument(runnerIds.size() == cellData.size());
-    for (double[] row : cellData) {
-      checkArgument(row.length == commitIds.size());
-    }
-
-    BenchmarkResultsTable table = new BenchmarkResultsTable();
-    table.allRunnerIds = Lists.newArrayList(runnerIds);
-    table.benchmarkName = benchmarkName;
-    table.commitIds = Lists.newArrayList(commitIds);
-    table.runnerResultList = Lists.newArrayList(cellData);
-    table.weekName = weekName;
-    table.year = year;
-    table.week = week;
-    return table;
-  }
-
-  private ArrayList<double[]> runnerResultList;
-  private ArrayList<String> commitIds;
-
-  private ArrayList<String> allRunnerIds;
-  private String benchmarkName;
-
-  private String weekName;
-  private int year;
-
-  private int week;
-
-  protected BenchmarkResultsTable() {
-  }
-
-  public ArrayList<String> getCommitIds() {
-    return commitIds;
-  }
-
-  /** The name of the benchmark shown in this table */
-  public String getBenchmarkName() {
-    return benchmarkName;
-  }
-
-  /**
-   * The commit id for a column.
-   */
-  public String getCommitId(int columnIndex) {
-    return commitIds.get(columnIndex);
-  }
-
-  /**
-   * The runner id for a row.
-   */
-  public String getRunnerId(int rowIndex) {
-    return allRunnerIds.get(rowIndex);
-  }
-
-  /**
-   * The data for one cell in the table.
-   */
-  public double getRunsPerSecond(int rowIndex, int columnIndex) {
-    return runnerResultList.get(rowIndex)[columnIndex];
-  }
-
-  /**
-   * All runner ids for this benchmark in this week.
-   */
-  public ArrayList<String> getAllRunnerIds() {
-    return allRunnerIds;
-  }
-
-  public int getRowCount() {
-    return commitIds.size();
-  }
-
-  public int getColumnCount() {
-    return allRunnerIds.size();
-  }
-
-  /** The ISO-8601 week number. */
-  public int getWeek() {
-    return week;
-  }
-
-  /** The ISO-8601 week number. */
-  public int getYear() {
-    return year;
-  }
-
-  /**
-   * A user-friendly description of the week shown in this table.
-   */
-  public String getWeekName() {
-    return weekName;
-  }
-}
diff --git a/dashboard/src/main/webapp/WEB-INF/appengine-web.xml b/dashboard/src/main/webapp/WEB-INF/appengine-web.xml
deleted file mode 100644
index a6fe8e3..0000000
--- a/dashboard/src/main/webapp/WEB-INF/appengine-web.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
-    <application>gwt-bench</application>
-    <version>1</version>
-    <threadsafe>true</threadsafe>
-
-    <system-properties>
-        <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
-    </system-properties>
-</appengine-web-app>
diff --git a/dashboard/src/main/webapp/WEB-INF/logging.properties b/dashboard/src/main/webapp/WEB-INF/logging.properties
deleted file mode 100644
index 0c2ea51..0000000
--- a/dashboard/src/main/webapp/WEB-INF/logging.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# A default java.util.logging configuration.
-# (All App Engine logging is through java.util.logging by default).
-#
-# To use this configuration, copy it into your application's WEB-INF
-# folder and add the following to your appengine-web.xml:
-#
-# <system-properties>
-#   <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
-# </system-properties>
-#
-
-# Set the default logging level for all loggers to WARNING
-.level = WARNING
diff --git a/dashboard/src/main/webapp/WEB-INF/queue.xml b/dashboard/src/main/webapp/WEB-INF/queue.xml
deleted file mode 100644
index a1762be..0000000
--- a/dashboard/src/main/webapp/WEB-INF/queue.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<queue-entries>
-  <queue>
-    <name>graph-queue</name>
-    <rate>5/s</rate>
-    <bucket-size>40</bucket-size>
-  </queue>
-</queue-entries>
\ No newline at end of file
diff --git a/dashboard/src/main/webapp/WEB-INF/web.xml b/dashboard/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644
index 54cc5f3..0000000
--- a/dashboard/src/main/webapp/WEB-INF/web.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
-
-  <welcome-file-list>
-    <welcome-file>index.html</welcome-file>
-  </welcome-file-list>
-
-  <filter>
-    <filter-name>guiceFilter</filter-name>
-    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
-  </filter>
-
-  <filter-mapping>
-    <filter-name>guiceFilter</filter-name>
-    <url-pattern>/*</url-pattern>
-  </filter-mapping>
-
-  <listener>
-    <listener-class>com.google.gwt.benchmark.dashboard.server.guice.GuiceServletConfig</listener-class>
-  </listener>
-
-  <security-constraint>
-    <web-resource-collection>
-      <web-resource-name>admin</web-resource-name>
-        <url-pattern>/admin/*</url-pattern>
-      </web-resource-collection>
-      <auth-constraint>
-        <role-name>admin</role-name>
-      </auth-constraint>
-  </security-constraint>
-
-  <security-constraint>
-    <web-resource-collection>
-      <web-resource-name>post_result</web-resource-name>
-      <url-pattern>/post_result</url-pattern>
-    </web-resource-collection>
-    <user-data-constraint>
-      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
-    </user-data-constraint>
-  </security-constraint>
-
-  <security-constraint>
-    <web-resource-collection>
-      <web-resource-name>tasks</web-resource-name>
-        <url-pattern>/tasks/*</url-pattern>
-    </web-resource-collection>
-    <auth-constraint>
-        <role-name>admin</role-name>
-    </auth-constraint>
-  </security-constraint>
-
-</web-app>
diff --git a/dashboard/src/main/webapp/admin/change_pwd_form.html b/dashboard/src/main/webapp/admin/change_pwd_form.html
deleted file mode 100644
index 22312d3..0000000
--- a/dashboard/src/main/webapp/admin/change_pwd_form.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!doctype html>
-<html>
-  <head></head>
-  <body>
-    <form action="/admin/" method="post">
-      <div>
-        <label for="password">New Password:</label>
-        <input type="password" name="password" />
-      </div>
-    </form>
-  </body>
-</html>
\ No newline at end of file
diff --git a/dashboard/src/main/webapp/index.html b/dashboard/src/main/webapp/index.html
deleted file mode 100644
index f51fbba..0000000
--- a/dashboard/src/main/webapp/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<meta http-equiv="content-type" content="text/html; charset=UTF-8">
-<title>GWT Benchmarks</title>
-<script type="text/javascript" src="dashboard/dashboard.nocache.js"></script>
-</head>
-<body>
-</body>
-</html>
\ No newline at end of file
diff --git a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/client/ui/GraphCompositeTest.java b/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/client/ui/GraphCompositeTest.java
deleted file mode 100644
index b1b6d60..0000000
--- a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/client/ui/GraphCompositeTest.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.client.ui;
-
-import com.google.gwt.benchmark.dashboard.shared.service.DashboardServiceAsync;
-import com.google.gwt.benchmark.dashboard.shared.service.dto.BenchmarkResultsTable;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.i18n.client.NumberFormat;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.visualization.client.AbstractDataTable.ColumnType;
-import com.google.gwt.visualization.client.DataTable;
-import com.google.gwt.visualization.client.visualizations.corechart.AxisOptions;
-import com.google.gwt.visualization.client.visualizations.corechart.Options;
-import com.google.gwtmockito.GwtMockitoTestRunner;
-import com.google.inject.Provider;
-
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-/**
- * Test for {@link GraphComposite}.
- */
-@RunWith(GwtMockitoTestRunner.class)
-public class GraphCompositeTest {
-
-  private static class CheckboxHolder {
-    CheckBox checkBox;
-    HandlerRegistration handlerRegistration;
-
-    public CheckboxHolder(CheckBox checkBox, HandlerRegistration handlerRegistration) {
-      this.checkBox = checkBox;
-      this.handlerRegistration = handlerRegistration;
-    }
-  }
-
-  private GraphComposite composite;
-
-  @Mock private DashboardServiceAsync service;
-  @Mock private NumberFormat numberFormat;
-  @Mock private Provider<CheckBox> checkBoxProvider;
-  @Mock private HistoryAccessor historyAccessor;
-  @Mock DataTable data;
-  @Mock Options options;
-  @Mock ValueChangeEvent<Boolean> vce;
-
-  @Captor private ArgumentCaptor<AsyncCallback<BenchmarkResultsTable>> asyncCaptor;
-  @Captor private ArgumentCaptor<Runnable> runableCaptor;
-  @Captor private ArgumentCaptor<ValueChangeHandler<Boolean>> valueChangeHandlerCaptor;
-
-  @Before
-  public void setup() {
-    composite = new GraphComposite(service, checkBoxProvider, historyAccessor);
-  }
-
-  @Test
-  public void testStartWithNowAndNoRunners() {
-
-    when(historyAccessor.getToken()).thenReturn("!graph?benchmark=module1");
-
-    composite.start();
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(false);
-    verify(composite.graphWidget).init(Mockito.<Runnable> anyObject());
-
-    verify(service).getLatestGraphs(eq("module1"), asyncCaptor.capture());
-  }
-
-  @Test
-  public void testStartWithOneRunners() {
-
-    when(historyAccessor.getToken()).thenReturn("!graph?benchmark=module1&w=4&y=2014&rids=[linux_ff]");
-
-    composite.start();
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(false);
-    verify(composite.graphWidget).init(Mockito.<Runnable> anyObject());
-
-    verify(service).getGraphs(eq("module1"), eq(4), eq(2014), asyncCaptor.capture());
-  }
-
-  @Test
-  public void testStartWithNowAndTwoRunners() {
-
-    when(historyAccessor.getToken()).thenReturn("!graph?benchmark=module1&w=5&y=2014&rids=[linux_ff, linux_chrome]");
-
-    composite.start();
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(false);
-    verify(composite.graphWidget).init(Mockito.<Runnable> anyObject());
-
-    verify(service).getGraphs(eq("module1"), eq(5), eq(2014), asyncCaptor.capture());
-  }
-
-  @Test
-  public void testRenderData() {
-
-    when(historyAccessor.getToken()).thenReturn("!graph?benchmark=module1&w=5&y=2014&rids=[linux_ff, linux_chrome]");
-
-    composite.start();
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(false);
-    verify(composite.graphWidget).init(runableCaptor.capture());
-    verify(service).getGraphs(eq("module1"), eq(5), eq(2014), asyncCaptor.capture());
-
-    verifyNoMoreInteractions(composite.container);
-
-    BenchmarkResultsTable result = BenchmarkResultsTable.create("module1", "someWeekName1", 2014, 5,
-        Arrays.asList("commit1", "commit2", "commit3"),
-        Arrays.asList("linux_ff", "linux_chrome", "win_ie11"),
-        Arrays.asList(new double[] {1, 2, 3}, new double[] {4, 5, 6}, new double[] {7, 8, 9}));
-
-    CheckboxHolder holder1 = createMockedCheckBox();
-    CheckboxHolder holder2 = createMockedCheckBox();
-    CheckboxHolder holder3 = createMockedCheckBox();
-
-    when(checkBoxProvider.get()).thenReturn(holder1.checkBox, holder2.checkBox, holder3.checkBox);
-
-    when(composite.graphWidget.createData()).thenReturn(data);
-    when(composite.graphWidget.createOptions()).thenReturn(options);
-
-    // Test that we do not render before the visualization api is loaded
-    asyncCaptor.getValue().onSuccess(result);
-    verifyNoMoreInteractions(composite.container);
-
-    runableCaptor.getValue().run();
-
-    verify(composite.loadingLabel).setVisible(false);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(true);
-
-    verify(holder1.checkBox).setText("linux_ff");
-    verify(holder2.checkBox).setText("linux_chrome");
-    verify(holder3.checkBox).setText("win_ie11");
-
-    verify(holder1.checkBox).setValue(true);
-    verify(holder2.checkBox).setValue(true);
-    verify(holder3.checkBox).setValue(false);
-
-    verify(holder1.checkBox).addValueChangeHandler(valueChangeHandlerCaptor.capture());
-
-    verify(composite.checkBoxContainer).add(holder1.checkBox);
-    verify(composite.checkBoxContainer).add(holder2.checkBox);
-    verify(composite.checkBoxContainer).add(holder3.checkBox);
-
-    verify(data).addColumn(ColumnType.STRING, "Commits");
-    verify(data).addColumn(ColumnType.NUMBER, "linux_ff");
-    verify(data).addColumn(ColumnType.NUMBER, "linux_chrome");
-
-    verify(data).addRows(3);
-
-    verify(data).setValue(0, 0, "commit1");
-    verify(data).setValue(1, 0, "commit2");
-    verify(data).setValue(2, 0, "commit3");
-
-    verify(data).setValue(0, 1, 1.0);
-    verify(data).setValue(1, 1, 2.0);
-    verify(data).setValue(2, 1, 3.0);
-
-    verify(data).setValue(0, 2, 4.0);
-    verify(data).setValue(1, 2, 5.0);
-    verify(data).setValue(2, 2, 6.0);
-
-    verify(options).setWidth(800);
-    verify(options).setHeight(600);
-    verify(options).setTitle("module1");
-
-    verify(composite.graphWidget).createData();
-    verify(composite.graphWidget).createOptions();
-    verify(composite.graphWidget, times(2)).createAxisOptions();
-    verify(composite.graphWidget).displayChart(options, data);
-    verify(options).setHAxisOptions(Mockito.<AxisOptions> anyObject());
-    verify(options).setVAxisOptions(Mockito.<AxisOptions> anyObject());
-
-    verifyNoMoreInteractions(composite.graphWidget, data, options);
-
-    // change a CheckBox
-    when(vce.getValue()).thenReturn(false);
-    valueChangeHandlerCaptor.getValue().onValueChange(vce);
-
-    verify(holder1.handlerRegistration).removeHandler();
-    verify(holder2.handlerRegistration).removeHandler();
-    verify(holder3.handlerRegistration).removeHandler();
-
-    verify(service).getGraphs(eq("module1"), eq(5), eq(2014), asyncCaptor.capture());
-    verify(historyAccessor).newItem("!graph?benchmark=module1&w=5&y=2014&rids=[linux_chrome]", false);
-  }
-
-  @Test
-  public void testRenderDataEmptyDataSet() {
-
-    when(historyAccessor.getToken()).thenReturn("!graph?benchmark=module1&w=5&y=2014&rids=[linux_ff, linux_chrome]");
-
-    composite.start();
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(false);
-    verify(composite.graphWidget).init(runableCaptor.capture());
-    verify(service).getGraphs(eq("module1"), eq(5), eq(2014), asyncCaptor.capture());
-
-    verifyNoMoreInteractions(composite.container);
-
-    BenchmarkResultsTable result = BenchmarkResultsTable.create("module1", "someWeekName1", 2014, 5,
-        Collections.<String>emptyList(),
-        Collections.<String>emptyList(),
-        Collections.<double[]>emptyList());
-
-    runableCaptor.getValue().run();
-    asyncCaptor.getValue().onSuccess(result);
-
-    verify(composite.checkBoxContainer).clear();
-    verify(composite.container).setVisible(true);
-    verify(composite.weekLabel).setText("someWeekName1");
-
-    verifyNoMoreInteractions(checkBoxProvider);
-
-    verify(composite.graphWidget).clear();
-    verifyNoMoreInteractions(composite.graphWidget);
-  }
-
-  @Test
-  public void testForwardButton() {
-
-    when(historyAccessor.getToken()).thenReturn("!graph?benchmark=module1&w=5&y=2014&rids=[linux_ff, linux_chrome]");
-
-    composite.start();
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(false);
-    verify(composite.graphWidget).init(runableCaptor.capture());
-    verify(service).getGraphs(eq("module1"), eq(5), eq(2014), asyncCaptor.capture());
-
-    verifyNoMoreInteractions(composite.container);
-
-    BenchmarkResultsTable result = BenchmarkResultsTable.create("module1", "someWeek1", 2014, 5,
-        Arrays.asList("commit1", "commit2", "commit3"),
-        Arrays.asList("linux_ff", "linux_chrome", "win_ie11"),
-        Arrays.asList(new double[] {1, 2, 3}, new double[] {4, 5, 6}, new double[] {7, 8, 9}));
-
-    CheckboxHolder holder1 = createMockedCheckBox();
-    CheckboxHolder holder2 = createMockedCheckBox();
-    CheckboxHolder holder3 = createMockedCheckBox();
-    CheckboxHolder holder4 = createMockedCheckBox();
-    CheckboxHolder holder5 = createMockedCheckBox();
-    CheckboxHolder holder6 = createMockedCheckBox();
-
-    when(checkBoxProvider.get()).thenReturn(holder1.checkBox, holder2.checkBox, holder3.checkBox,
-        holder4.checkBox, holder5.checkBox, holder6.checkBox);
-
-    when(composite.graphWidget.createData()).thenReturn(data);
-    when(composite.graphWidget.createOptions()).thenReturn(options);
-
-    // Test that we do not render before the visualization api is loaded
-    asyncCaptor.getValue().onSuccess(result);
-    verifyNoMoreInteractions(composite.container);
-
-    runableCaptor.getValue().run();
-    verify(composite.loadingLabel).setVisible(false);
-
-
-    reset(composite.loadingLabel, composite.errorLabel, composite.container);
-
-    // press the forward button
-    composite.onForwardButtonClicked(null);
-    verify(historyAccessor).newItem(
-        "!graph?benchmark=module1&w=6&y=2014&rids=[linux_chrome, linux_ff]", false);
-
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(false);
-    verify(service).getGraphs(eq("module1"), eq(6), eq(2014), asyncCaptor.capture());
-
-
-    result = BenchmarkResultsTable.create("module1", "someWeek1", 2014, 6,
-        Arrays.asList("commit1", "commit2", "commit3"),
-        Arrays.asList("linux_ff", "linux_chrome", "win_ie11"),
-        Arrays.asList(new double[] {1, 2, 3}, new double[] {4, 5, 6}, new double[] {7, 8, 9}));
-
-    asyncCaptor.getValue().onSuccess(result);
-
-    verify(holder1.handlerRegistration).removeHandler();
-    verify(holder2.handlerRegistration).removeHandler();
-    verify(holder3.handlerRegistration).removeHandler();
-    reset(composite.loadingLabel, composite.errorLabel, composite.container, service);
-
-    // press the back button
-    composite.onBackButtonClicked(null);
-
-    verify(historyAccessor).newItem(
-        "!graph?benchmark=module1&w=5&y=2014&rids=[linux_chrome, linux_ff]", false);
-
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.container).setVisible(false);
-    verify(service).getGraphs(eq("module1"), eq(5), eq(2014), asyncCaptor.capture());
-
-
-    result = BenchmarkResultsTable.create("module1", "someWeek1", 2014, 6,
-        Arrays.asList("commit1", "commit2", "commit3"),
-        Arrays.asList("linux_ff", "linux_chrome", "win_ie11"),
-        Arrays.asList(new double[] {1, 2, 3}, new double[] {4, 5, 6}, new double[] {7, 8, 9}));
-
-    asyncCaptor.getValue().onSuccess(result);
-
-    verify(holder1.handlerRegistration).removeHandler();
-    verify(holder2.handlerRegistration).removeHandler();
-    verify(holder3.handlerRegistration).removeHandler();
-  }
-
-  private CheckboxHolder createMockedCheckBox() {
-    CheckBox checkBox = mock(CheckBox.class);
-    HandlerRegistration hr = mock(HandlerRegistration.class);
-    when(checkBox.addValueChangeHandler(Mockito.<ValueChangeHandler<Boolean>> anyObject()))
-    .thenReturn(hr);
-    when(checkBoxProvider.get()).thenReturn(checkBox);
-    return new CheckboxHolder(checkBox, hr);
-  }
-}
diff --git a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewCompositeTest.java b/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewCompositeTest.java
deleted file mode 100644
index 2b70ba5..0000000
--- a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/client/ui/ModuleOverviewCompositeTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.client.ui;
-
-import com.google.gwt.benchmark.dashboard.shared.service.DashboardServiceAsync;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.i18n.client.NumberFormat;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwtmockito.GwtMockitoTestRunner;
-import com.google.inject.Provider;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Test for {@link ModuleOverviewComposite}.
- */
-@RunWith(GwtMockitoTestRunner.class)
-public class ModuleOverviewCompositeTest {
-
-  private ModuleOverviewComposite composite;
-
-  @Mock private DashboardServiceAsync service;
-  @Mock private NumberFormat numberFormat;
-  @Mock private Provider<Label> labelProvider;
-  @Mock HistoryAccessor historyAccessor;
-
-  @Captor private ArgumentCaptor<AsyncCallback<ArrayList<String>>> asyncCaptor;
-  @Captor private ArgumentCaptor<ClickHandler> clickHandlerCaptor1;
-  @Captor private ArgumentCaptor<ClickHandler> clickHandlerCaptor2;
-
-  @Before
-  public void setup() {
-    composite = new ModuleOverviewComposite(service, labelProvider, historyAccessor);
-  }
-
-  @Test
-  public void testPresenterAddsViewAndInitializedView() {
-    composite.start();
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.contentContainer).setVisible(false);
-  }
-
-  @Test
-  public void testRenderDataWhileServerIsNotRunning() {
-    composite.start();
-
-    verify(composite.loadingLabel).setVisible(true);
-    verify(composite.errorLabel).setVisible(false);
-    verify(composite.contentContainer).setVisible(false);
-
-    Label label1 = mock(Label.class);
-    Label label2 = mock(Label.class);
-    when(labelProvider.get()).thenReturn(label1, label2);
-
-    verify(service).getLatestBenchmarkNames(asyncCaptor.capture());
-
-    AsyncCallback<ArrayList<String>> asyncCallback = asyncCaptor.getValue();
-    asyncCallback.onSuccess(new ArrayList<>(Arrays.asList("module1", "module2")));
-
-    verify(composite.loadingLabel).setVisible(false);
-    verify(composite.contentContainer).setVisible(true);
-    verify(composite.contentContainer).clear();
-    verify(composite.contentContainer).add(label1);
-    verify(composite.contentContainer).add(label2);
-
-    verify(label1).setText("module1");
-    verify(label1).addClickHandler(clickHandlerCaptor1.capture());
-    verify(label2).setText("module2");
-    verify(label2).addClickHandler(clickHandlerCaptor2.capture());
-
-    verifyNoMoreInteractions(composite.contentContainer, composite.errorLabel,
-     composite.loadingLabel, label1, label2);
-
-    clickHandlerCaptor1.getValue().onClick(null);
-    verify(historyAccessor).newItem("!graph?benchmark=module1", true);
-
-    clickHandlerCaptor2.getValue().onClick(null);
-    verify(historyAccessor).newItem("!graph?benchmark=module2", true);
-  }
-}
diff --git a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/controller/AuthControllerTest.java b/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/controller/AuthControllerTest.java
deleted file mode 100644
index 1a50aeb..0000000
--- a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/controller/AuthControllerTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.controller;
-
-import com.google.appengine.api.datastore.DatastoreService;
-import com.google.appengine.api.datastore.DatastoreServiceFactory;
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.KeyFactory;
-import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
-import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Test for {@link AuthController}.
- */
-public class AuthControllerTest {
-
-  private final LocalServiceTestHelper helper =
-      new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
-
-  @Before
-  public void setUp() {
-    helper.setUp();
-  }
-
-  @After
-  public void tearDown() {
-    helper.tearDown();
-  }
-
-  @Test
-  public void testValidValue() throws ControllerException {
-    // empty datastore should always return false
-    Assert.assertFalse(new AuthController().validateAuth("someauth1"));
-
-    // prepare datastore
-    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
-    Entity entity = new Entity(KeyFactory.createKey("Auth", "auth"));
-    entity.setProperty("value", "someauth2");
-    datastore.put(entity);
-
-    // now see if auth validation works
-    Assert.assertTrue(new AuthController().validateAuth("someauth2"));
-    Assert.assertFalse(new AuthController().validateAuth("invalidAuth"));
-
-    // change the auth
-    new AuthController().updateAuth("changed auth");
-
-    // does the change propagate?
-    Assert.assertTrue(new AuthController().validateAuth("changed auth"));
-    Assert.assertFalse(new AuthController().validateAuth("invalidAuth1"));
-
-    // test for two short auths
-    try {
-      new AuthController().updateAuth("12345");
-      Assert.fail("exception did not occur (auth too short)");
-    } catch (ControllerException ignored) {
-    }
-
-    // verify that it did not change
-    Assert.assertTrue(new AuthController().validateAuth("changed auth"));
-  }
-
-}
diff --git a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/controller/BenchmarkControllerTest.java b/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/controller/BenchmarkControllerTest.java
deleted file mode 100644
index a562bc2..0000000
--- a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/controller/BenchmarkControllerTest.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.controller;
-
-import static com.google.appengine.api.datastore.FetchOptions.Builder.withLimit;
-
-import com.google.appengine.api.datastore.DatastoreService;
-import com.google.appengine.api.datastore.DatastoreServiceFactory;
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.EntityNotFoundException;
-import com.google.appengine.api.datastore.FetchOptions;
-import com.google.appengine.api.datastore.Key;
-import com.google.appengine.api.datastore.Query;
-import com.google.appengine.api.taskqueue.dev.LocalTaskQueue;
-import com.google.appengine.api.taskqueue.dev.QueueStateInfo;
-import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
-import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
-import com.google.appengine.tools.development.testing.LocalTaskQueueTestConfig;
-import com.google.gwt.benchmark.common.shared.json.BenchmarkRunJson;
-import com.google.gwt.benchmark.dashboard.server.domain.BenchmarkGraph;
-import com.google.gwt.benchmark.dashboard.server.domain.BenchmarkResult;
-import com.google.gwt.benchmark.dashboard.server.domain.BenchmarkRun;
-import com.google.gwt.benchmark.dashboard.server.servlets.AddBenchmarkResultServletTest;
-import com.google.gwt.benchmark.dashboard.shared.service.dto.BenchmarkResultsTable;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Test for {@link BenchmarkController}.
- */
-public class BenchmarkControllerTest {
-
-  private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
-      new LocalDatastoreServiceTestConfig(), new LocalTaskQueueTestConfig().setQueueXmlPath(
-          System.getProperty("user.dir") + "/src/test/resources/queue.xml"));
-
-  @Before
-  public void setUp() {
-    helper.setUp();
-  }
-
-  @After
-  public void tearDown() {
-    helper.tearDown();
-  }
-
-  @Test
-  public void testControllerPersists() throws ControllerException, EntityNotFoundException {
-
-    BenchmarkRunJson benchmarkRunJSON = AddBenchmarkResultServletTest.buildBenchmarkRunJSON();
-
-    BenchmarkController controller = new BenchmarkController();
-
-    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
-
-    Assert.assertEquals(0, ds.prepare(new Query(BenchmarkRun.NAME)).countEntities(withLimit(10)));
-    Assert.assertEquals(0, ds.prepare(new Query(BenchmarkGraph.NAME)).countEntities(withLimit(10)));
-    Assert.assertEquals(0,
-        ds.prepare(new Query(BenchmarkResult.NAME)).countEntities(withLimit(10)));
-    controller.addBenchmarkResult(benchmarkRunJSON);
-
-    Assert.assertEquals(1, ds.prepare(new Query(BenchmarkRun.NAME)).countEntities(withLimit(10)));
-
-    String commitId = benchmarkRunJSON.getCommitId();
-
-    long runtime = Math.round(benchmarkRunJSON.getCommitTimeMsEpoch());
-
-    Key benchmarkRunKey = BenchmarkRun.createKey(commitId);
-    Key module1FireFox = BenchmarkResult.createKey(benchmarkRunKey, "module1", "firefox_linux");
-    Key module1Chrome = BenchmarkResult.createKey(benchmarkRunKey, "module1", "chrome_linux");
-    Key module2FireFox = BenchmarkResult.createKey(benchmarkRunKey, "module2", "firefox_linux");
-    Key module2Chrome = BenchmarkResult.createKey(benchmarkRunKey, "module2", "chrome_linux");
-
-    Entity entity = ds.get(benchmarkRunKey);
-
-    BenchmarkRun benchmarkRun = new BenchmarkRun(entity);
-
-    Assert.assertEquals(commitId, benchmarkRun.getCommitId());
-    Assert.assertEquals(runtime, benchmarkRun.getCommitTimeMsEpoch());
-
-    Assert.assertEquals(2, benchmarkRun.getRunnerIds().size());
-    Assert.assertTrue(benchmarkRun.getRunnerIds().contains("firefox_linux"));
-    Assert.assertTrue(benchmarkRun.getRunnerIds().contains("chrome_linux"));
-
-    Assert.assertEquals(4, benchmarkRun.getResults().size());
-    Assert.assertTrue(benchmarkRun.getResults().contains(module1FireFox));
-    Assert.assertTrue(benchmarkRun.getResults().contains(module1Chrome));
-    Assert.assertTrue(benchmarkRun.getResults().contains(module2FireFox));
-    Assert.assertTrue(benchmarkRun.getResults().contains(module2Chrome));
-
-    entity = ds.get(module1FireFox);
-    BenchmarkResult benchmarkResult = new BenchmarkResult(entity);
-    Assert.assertEquals("module1", benchmarkResult.getBenchmarkName());
-    Assert.assertEquals("firefox_linux", benchmarkResult.getRunnerId());
-    Assert.assertEquals(3, benchmarkResult.getRunsPerSecond(), 0.0001);
-
-    entity = ds.get(module1Chrome);
-    benchmarkResult = new BenchmarkResult(entity);
-    Assert.assertEquals("module1", benchmarkResult.getBenchmarkName());
-    Assert.assertEquals("chrome_linux", benchmarkResult.getRunnerId());
-    Assert.assertEquals(4, benchmarkResult.getRunsPerSecond(), 0.0001);
-
-    entity = ds.get(module2FireFox);
-    benchmarkResult = new BenchmarkResult(entity);
-    Assert.assertEquals("module2", benchmarkResult.getBenchmarkName());
-    Assert.assertEquals("firefox_linux", benchmarkResult.getRunnerId());
-    Assert.assertEquals(3, benchmarkResult.getRunsPerSecond(), 0.0001);
-
-    entity = ds.get(module2Chrome);
-    benchmarkResult = new BenchmarkResult(entity);
-    Assert.assertEquals("module2", benchmarkResult.getBenchmarkName());
-    Assert.assertEquals("chrome_linux", benchmarkResult.getRunnerId());
-    Assert.assertEquals(4, benchmarkResult.getRunsPerSecond(), 0.0001);
-
-    LocalTaskQueue taskQueue = LocalTaskQueueTestConfig.getLocalTaskQueue();
-
-    Map<String, QueueStateInfo> queueStateInfo = taskQueue.getQueueStateInfo();
-    QueueStateInfo info = queueStateInfo.get("graph-queue");
-    Assert.assertEquals(4, info.getCountTasks());
-  }
-
-  @Test
-  public void testGraphUpdate() throws ControllerException {
-    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
-    long commitTime_20th_march_2014 = 1400610555950L;
-    long oneWeekInMs = 604800000L;
-
-    List<String> runnerIds = Arrays.asList("linux_ff", "linux_chrome");
-
-    // put in one entity for the current week
-    BenchmarkRun benchmarkRun = new BenchmarkRun("commitId0", commitTime_20th_march_2014);
-    benchmarkRun.setRunnerIds(runnerIds);
-    BenchmarkResult benchmarkResult1 =
-        new BenchmarkResult(benchmarkRun.getKey(), "module1", "linux_ff");
-    benchmarkResult1.setRunsPerSecond(100);
-    ds.put(benchmarkResult1.getEntity());
-    BenchmarkResult benchmarkResult2 =
-        new BenchmarkResult(benchmarkRun.getKey(), "module1", "linux_chrome");
-    benchmarkResult2.setRunsPerSecond(200);
-    ds.put(benchmarkResult2.getEntity());
-    BenchmarkResult benchmarkResult3 =
-        new BenchmarkResult(benchmarkRun.getKey(), "module2", "linux_ff");
-    benchmarkResult3.setRunsPerSecond(300);
-    ds.put(benchmarkResult3.getEntity());
-    BenchmarkResult benchmarkResult4 =
-        new BenchmarkResult(benchmarkRun.getKey(), "module2", "linux_chrome");
-    benchmarkResult4.setRunsPerSecond(400);
-    ds.put(benchmarkResult4.getEntity());
-
-    benchmarkRun.setResults(Arrays.asList(benchmarkResult1.getKey(), benchmarkResult2.getKey(),
-        benchmarkResult3.getKey(), benchmarkResult4.getKey()));
-
-    ds.put(benchmarkRun.getEntity());
-
-    // put in another entity for the current week (a little bit later)
-    benchmarkRun = new BenchmarkRun("commitId1", commitTime_20th_march_2014 + 5000L);
-    benchmarkRun.setRunnerIds(runnerIds);
-    benchmarkResult1 = new BenchmarkResult(benchmarkRun.getKey(), "module1", "linux_ff");
-    benchmarkResult1.setRunsPerSecond(101);
-    ds.put(benchmarkResult1.getEntity());
-    benchmarkResult2 = new BenchmarkResult(benchmarkRun.getKey(), "module1", "linux_chrome");
-    benchmarkResult2.setRunsPerSecond(201);
-    ds.put(benchmarkResult2.getEntity());
-    benchmarkResult3 = new BenchmarkResult(benchmarkRun.getKey(), "module2", "linux_ff");
-    benchmarkResult3.setRunsPerSecond(301);
-    ds.put(benchmarkResult3.getEntity());
-    benchmarkResult4 = new BenchmarkResult(benchmarkRun.getKey(), "module2", "linux_chrome");
-    benchmarkResult4.setRunsPerSecond(401);
-    ds.put(benchmarkResult4.getEntity());
-
-    benchmarkRun.setResults(Arrays.asList(benchmarkResult1.getKey(), benchmarkResult2.getKey(),
-        benchmarkResult3.getKey(), benchmarkResult4.getKey()));
-
-    ds.put(benchmarkRun.getEntity());
-
-    // put in one entity the week before
-    benchmarkRun = new BenchmarkRun("commitId2", commitTime_20th_march_2014 - oneWeekInMs);
-    benchmarkRun.setRunnerIds(runnerIds);
-    benchmarkResult1 = new BenchmarkResult(benchmarkRun.getKey(), "module1", "linux_ff");
-    benchmarkResult1.setRunsPerSecond(102);
-    ds.put(benchmarkResult1.getEntity());
-    benchmarkResult2 = new BenchmarkResult(benchmarkRun.getKey(), "module1", "linux_chrome");
-    benchmarkResult2.setRunsPerSecond(202);
-    ds.put(benchmarkResult2.getEntity());
-    benchmarkResult3 = new BenchmarkResult(benchmarkRun.getKey(), "module2", "linux_ff");
-    benchmarkResult3.setRunsPerSecond(302);
-    ds.put(benchmarkResult3.getEntity());
-    benchmarkResult4 = new BenchmarkResult(benchmarkRun.getKey(), "module2", "linux_chrome");
-    benchmarkResult4.setRunsPerSecond(402);
-    ds.put(benchmarkResult4.getEntity());
-
-    benchmarkRun.setResults(Arrays.asList(benchmarkResult1.getKey(), benchmarkResult2.getKey(),
-        benchmarkResult3.getKey(), benchmarkResult4.getKey()));
-
-    ds.put(benchmarkRun.getEntity());
-
-    // put in one entity the week after
-    benchmarkRun = new BenchmarkRun("commitId3", commitTime_20th_march_2014 + oneWeekInMs);
-    benchmarkRun.setRunnerIds(runnerIds);
-    benchmarkResult1 = new BenchmarkResult(benchmarkRun.getKey(), "module1", "linux_ff");
-    benchmarkResult1.setRunsPerSecond(103);
-    ds.put(benchmarkResult1.getEntity());
-    benchmarkResult2 = new BenchmarkResult(benchmarkRun.getKey(), "module1", "linux_chrome");
-    benchmarkResult2.setRunsPerSecond(203);
-    ds.put(benchmarkResult2.getEntity());
-    benchmarkResult3 = new BenchmarkResult(benchmarkRun.getKey(), "module2", "linux_ff");
-    benchmarkResult3.setRunsPerSecond(303);
-    ds.put(benchmarkResult3.getEntity());
-    benchmarkResult4 = new BenchmarkResult(benchmarkRun.getKey(), "module2", "linux_chrome");
-    benchmarkResult4.setRunsPerSecond(403);
-    ds.put(benchmarkResult4.getEntity());
-
-    benchmarkRun.setResults(Arrays.asList(benchmarkResult1.getKey(), benchmarkResult2.getKey(),
-        benchmarkResult3.getKey(), benchmarkResult4.getKey()));
-
-    ds.put(benchmarkRun.getEntity());
-
-    BenchmarkController controller = new BenchmarkController();
-
-    // update the first graph
-    controller.updateGraph(commitTime_20th_march_2014, "module1", "linux_ff");
-    // get an verify
-    Query query = new Query(BenchmarkGraph.NAME);
-    List<Entity> list = ds.prepare(query).asList(FetchOptions.Builder.withDefaults());
-    Assert.assertEquals(1, list.size());
-
-    BenchmarkGraph benchmarkGraph = new BenchmarkGraph(list.get(0));
-    Assert.assertEquals("module1", benchmarkGraph.getModule());
-    Assert.assertEquals("linux_ff", benchmarkGraph.getRunnerId());
-    Assert.assertEquals(2, benchmarkGraph.getCommitIds().size());
-    Assert.assertEquals("commitId0", benchmarkGraph.getCommitIds().get(0));
-    Assert.assertEquals("commitId1", benchmarkGraph.getCommitIds().get(1));
-    Assert.assertEquals(21, benchmarkGraph.getWeek());
-    Assert.assertEquals(2014, benchmarkGraph.getYear());
-    Assert.assertEquals(2, benchmarkGraph.getRunsPerSecond().size());
-    Assert.assertEquals(100, benchmarkGraph.getRunsPerSecond().get(0), 0.0001);
-    Assert.assertEquals(101, benchmarkGraph.getRunsPerSecond().get(1), 0.0001);
-    // delete the entity
-    ds.delete(benchmarkGraph.getEntity().getKey());
-
-    // update the second graph
-    controller.updateGraph(commitTime_20th_march_2014, "module2", "linux_ff");
-    // get an verify
-    query = new Query(BenchmarkGraph.NAME);
-    list = ds.prepare(query).asList(FetchOptions.Builder.withDefaults());
-    Assert.assertEquals(1, list.size());
-
-    benchmarkGraph = new BenchmarkGraph(list.get(0));
-    Assert.assertEquals("module2", benchmarkGraph.getModule());
-    Assert.assertEquals("linux_ff", benchmarkGraph.getRunnerId());
-    Assert.assertEquals(2, benchmarkGraph.getCommitIds().size());
-    Assert.assertEquals("commitId0", benchmarkGraph.getCommitIds().get(0));
-    Assert.assertEquals("commitId1", benchmarkGraph.getCommitIds().get(1));
-    Assert.assertEquals(21, benchmarkGraph.getWeek());
-    Assert.assertEquals(2014, benchmarkGraph.getYear());
-    Assert.assertEquals(2, benchmarkGraph.getRunsPerSecond().size());
-    Assert.assertEquals(300, benchmarkGraph.getRunsPerSecond().get(0), 0.0001);
-    Assert.assertEquals(301, benchmarkGraph.getRunsPerSecond().get(1), 0.0001);
-    // delete the entity
-    ds.delete(benchmarkGraph.getEntity().getKey());
-
-    // update the third graph
-    controller.updateGraph(commitTime_20th_march_2014, "module1", "linux_chrome");
-    // get an verify
-    query = new Query(BenchmarkGraph.NAME);
-    list = ds.prepare(query).asList(FetchOptions.Builder.withDefaults());
-    Assert.assertEquals(1, list.size());
-
-    benchmarkGraph = new BenchmarkGraph(list.get(0));
-    Assert.assertEquals("module1", benchmarkGraph.getModule());
-    Assert.assertEquals("linux_chrome", benchmarkGraph.getRunnerId());
-    Assert.assertEquals(2, benchmarkGraph.getCommitIds().size());
-    Assert.assertEquals("commitId0", benchmarkGraph.getCommitIds().get(0));
-    Assert.assertEquals("commitId1", benchmarkGraph.getCommitIds().get(1));
-    Assert.assertEquals(21, benchmarkGraph.getWeek());
-    Assert.assertEquals(2014, benchmarkGraph.getYear());
-    Assert.assertEquals(2, benchmarkGraph.getRunsPerSecond().size());
-    Assert.assertEquals(200, benchmarkGraph.getRunsPerSecond().get(0), 0.0001);
-    Assert.assertEquals(201, benchmarkGraph.getRunsPerSecond().get(1), 0.0001);
-    // delete the entity
-    ds.delete(benchmarkGraph.getEntity().getKey());
-
-    // update the third graph
-    controller.updateGraph(commitTime_20th_march_2014, "module2", "linux_chrome");
-    // get an verify
-    query = new Query(BenchmarkGraph.NAME);
-    list = ds.prepare(query).asList(FetchOptions.Builder.withDefaults());
-    Assert.assertEquals(1, list.size());
-
-    benchmarkGraph = new BenchmarkGraph(list.get(0));
-    Assert.assertEquals("module2", benchmarkGraph.getModule());
-    Assert.assertEquals("linux_chrome", benchmarkGraph.getRunnerId());
-    Assert.assertEquals(2, benchmarkGraph.getCommitIds().size());
-    Assert.assertEquals("commitId0", benchmarkGraph.getCommitIds().get(0));
-    Assert.assertEquals("commitId1", benchmarkGraph.getCommitIds().get(1));
-    Assert.assertEquals(21, benchmarkGraph.getWeek());
-    Assert.assertEquals(2014, benchmarkGraph.getYear());
-    Assert.assertEquals(2, benchmarkGraph.getRunsPerSecond().size());
-    Assert.assertEquals(400, benchmarkGraph.getRunsPerSecond().get(0), 0.0001);
-    Assert.assertEquals(401, benchmarkGraph.getRunsPerSecond().get(1), 0.0001);
-    // delete the entity
-    ds.delete(benchmarkGraph.getEntity().getKey());
-  }
-
-  @Test
-  public void testGetGraph() {
-
-    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
-
-    // put some graphs in the store
-    BenchmarkGraph benchmarkGraph1 = new BenchmarkGraph("module1", "linux_ff", 4, 2014);
-    benchmarkGraph1.setCommitIds(Arrays.asList("commit1", "commit2", "commit3"));
-    benchmarkGraph1.setRunsPerSecond(Arrays.asList(1.0, 2.0, 3.0));
-    ds.put(benchmarkGraph1.getEntity());
-
-    BenchmarkGraph benchmarkGraph2 = new BenchmarkGraph("module1", "linux_chrome", 4, 2014);
-    benchmarkGraph2.setCommitIds(Arrays.asList("commit1", "commit2", "commit3"));
-    benchmarkGraph2.setRunsPerSecond(Arrays.asList(4.0, 5.0, 6.0));
-    ds.put(benchmarkGraph2.getEntity());
-
-    // another week
-    BenchmarkGraph benchmarkGraph3 = new BenchmarkGraph("module1", "linux_chrome", 5, 2014);
-    benchmarkGraph3.setCommitIds(Arrays.asList("commit4", "commit5", "commit6"));
-    benchmarkGraph3.setRunsPerSecond(Arrays.asList(4.1, 5.1, 6.1));
-    ds.put(benchmarkGraph3.getEntity());
-
-    BenchmarkController controller = new BenchmarkController();
-    BenchmarkResultsTable dto =
-        controller.getGraphs("module1", 4, 2014);
-
-    Assert.assertEquals(2, dto.getColumnCount());
-    Assert.assertEquals(2, dto.getAllRunnerIds().size());
-    Assert.assertTrue(dto.getAllRunnerIds().contains("linux_chrome"));
-    Assert.assertTrue(dto.getAllRunnerIds().contains("linux_ff"));
-
-    Assert.assertEquals(3, dto.getRowCount());
-    Assert.assertEquals(Arrays.asList("commit1", "commit2", "commit3"), dto.getCommitIds());
-    Assert.assertEquals("module1", dto.getBenchmarkName());
-
-    Assert.assertEquals(4.0, dto.getRunsPerSecond(0, 0), 0.0001);
-    Assert.assertEquals(5.0, dto.getRunsPerSecond(0, 1), 0.0001);
-    Assert.assertEquals(6.0, dto.getRunsPerSecond(0, 2), 0.0001);
-    Assert.assertEquals(1.0, dto.getRunsPerSecond(1, 0), 0.0001);
-    Assert.assertEquals(2.0, dto.getRunsPerSecond(1, 1), 0.0001);
-    Assert.assertEquals(3.0, dto.getRunsPerSecond(1, 2), 0.0001);
-  }
-}
diff --git a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/servlets/AddBenchmarkResultServletTest.java b/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/servlets/AddBenchmarkResultServletTest.java
deleted file mode 100644
index 9b99fed..0000000
--- a/dashboard/src/test/java/com/google/gwt/benchmark/dashboard/server/servlets/AddBenchmarkResultServletTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2014 Google 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 com.google.gwt.benchmark.dashboard.server.servlets;
-
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import com.google.gwt.benchmark.common.shared.json.BenchmarkResultJson;
-import com.google.gwt.benchmark.common.shared.json.BenchmarkRunJson;
-import com.google.gwt.benchmark.common.shared.json.JsonFactory;
-import com.google.gwt.benchmark.dashboard.server.controller.AuthController;
-import com.google.gwt.benchmark.dashboard.server.controller.BenchmarkController;
-import com.google.gwt.benchmark.dashboard.server.controller.ControllerException;
-import com.google.gwt.benchmark.dashboard.server.servlets.AddBenchmarkResultServlet;
-import com.google.web.bindery.autobean.shared.AutoBean;
-import com.google.web.bindery.autobean.shared.AutoBeanCodex;
-import com.google.web.bindery.autobean.shared.AutoBeanUtils;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.runners.MockitoJUnitRunner;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Test for {@link AddBenchmarkResultServlet}.
- */
-@RunWith(MockitoJUnitRunner.class)
-public class AddBenchmarkResultServletTest {
-
-  public static BenchmarkRunJson buildBenchmarkRunJSON() {
-    JsonFactory.Factory factory = JsonFactory.get();
-
-    BenchmarkRunJson runJSON = factory.run().as();
-    runJSON.setCommitId("commit1");
-    runJSON.setCommitTimeMsEpoch(1);
-
-    Map<String, List<BenchmarkResultJson>> results = new LinkedHashMap<>();
-    String moduleName = "module1";
-    List<BenchmarkResultJson> list = new ArrayList<>();
-    results.put(moduleName, list);
-
-    BenchmarkResultJson resultJSON = factory.result().as();
-    resultJSON.setBenchmarkName(moduleName);
-    resultJSON.setRunnerId("firefox_linux");
-    resultJSON.setRunsPerSecond(3);
-    list.add(resultJSON);
-
-    BenchmarkResultJson resultJSON1 = factory.result().as();
-    resultJSON1.setBenchmarkName(moduleName);
-    resultJSON1.setRunnerId("chrome_linux");
-    resultJSON1.setRunsPerSecond(4);
-    list.add(resultJSON1);
-
-    String moduleName1 = "module2";
-    List<BenchmarkResultJson> list1 = new ArrayList<>();
-    results.put(moduleName1, list1);
-
-    BenchmarkResultJson resultJSON2 = factory.result().as();
-    resultJSON2.setBenchmarkName(moduleName1);
-    resultJSON2.setRunnerId("firefox_linux");
-    resultJSON2.setRunsPerSecond(3);
-    list1.add(resultJSON2);
-    BenchmarkResultJson resultJSON3 = factory.result().as();
-    resultJSON3.setBenchmarkName(moduleName1);
-    resultJSON3.setRunnerId("chrome_linux");
-    resultJSON3.setRunsPerSecond(4);
-    list1.add(resultJSON3);
-
-    runJSON.setResultByBenchmarkName(results);
-    return runJSON;
-  }
-
-  private AddBenchmarkResultServlet servlet;
-
-  private AutoBean<BenchmarkRunJson> bean;
-  @Mock
-  private BenchmarkController benchmarkController;
-  @Mock
-  private HttpServletResponse response;
-  @Mock
-  private HttpServletRequest request;
-
-  @Mock
-  private AuthController authController;
-
-  @Before
-  public void setup() throws IOException {
-
-    bean = AutoBeanUtils.getAutoBean(buildBenchmarkRunJSON());
-
-    final ByteArrayInputStream byteArrayInputStream =
-        new ByteArrayInputStream(AutoBeanCodex.encode(bean).getPayload().getBytes("UTF-8"));
-    ServletInputStream servletInputStream = new ServletInputStream() {
-
-      @Override
-      public int read() throws IOException {
-        return byteArrayInputStream.read();
-      }
-    };
-    when(request.getInputStream()).thenReturn(servletInputStream);
-
-    servlet = new AddBenchmarkResultServlet(authController, benchmarkController);
-  }
-
-  @Test
-  public void testSuccessfulResultAdd() throws ServletException, IOException, ControllerException {
-    when(request.getHeader("auth")).thenReturn("authtest");
-    when(authController.validateAuth("authtest")).thenReturn(true);
-
-    servlet.doPut(request, response);
-    ArgumentCaptor<BenchmarkRunJson> captor = ArgumentCaptor.forClass(BenchmarkRunJson.class);
-    verify(benchmarkController).addBenchmarkResult(captor.capture());
-    BenchmarkRunJson value = captor.getValue();
-    AutoBean<BenchmarkRunJson> autoBean = AutoBeanUtils.getAutoBean(value);
-    Assert.assertTrue(AutoBeanUtils.deepEquals(bean, autoBean));
-    verify(response).setStatus(HttpServletResponse.SC_OK);
-  }
-
-  @Test
-  public void testBadAuth() throws ServletException, IOException {
-    when(request.getHeader("auth")).thenReturn("wrongauth");
-    when(authController.validateAuth("wrongauth")).thenReturn(false);
-    servlet.doPut(request, response);
-    verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-  }
-
-  @Test
-  public void testBadController() throws ServletException, IOException, ControllerException {
-    when(request.getHeader("auth")).thenReturn("authtest");
-    when(authController.validateAuth("authtest")).thenReturn(true);
-    doThrow(new ControllerException("test", null)).when(benchmarkController).addBenchmarkResult(
-        Mockito.<BenchmarkRunJson> anyObject());
-    servlet.doPut(request, response);
-    verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-  }
-}
diff --git a/dashboard/src/test/resources/queue.xml b/dashboard/src/test/resources/queue.xml
deleted file mode 100644
index a1762be..0000000
--- a/dashboard/src/test/resources/queue.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<queue-entries>
-  <queue>
-    <name>graph-queue</name>
-    <rate>5/s</rate>
-    <bucket-size>40</bucket-size>
-  </queue>
-</queue-entries>
\ No newline at end of file
diff --git a/launcher/pom.xml b/launcher/pom.xml
index 45045a8..43e6031 100644
--- a/launcher/pom.xml
+++ b/launcher/pom.xml
@@ -29,6 +29,44 @@
       <version>1.0-SNAPSHOT</version>
       <type>war</type>
     </dependency>
+    <dependency>
+      <groupId>com.google.gwt.benchmark</groupId>
+      <artifactId>gwt-benchmark-common</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.api-client</groupId>
+      <artifactId>google-api-client-jackson2</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.api-client</groupId>
+      <artifactId>google-api-client</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.oauth-client</groupId>
+      <artifactId>google-oauth-client</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.oauth-client</groupId>
+      <artifactId>google-oauth-client-java6</artifactId>
+      <version>1.20.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>18.0</version>
+    </dependency>
+    <!-- explicitly add the dependency here since the google api pulls in an old 
+      version -->
+    <!-- which does not work with webdriver -->
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <version>4.2.1</version>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/launcher/src/main/java/com/google/gwt/benchmark/launcher/OAuthWriter.java b/launcher/src/main/java/com/google/gwt/benchmark/launcher/OAuthWriter.java
new file mode 100644
index 0000000..d953424
--- /dev/null
+++ b/launcher/src/main/java/com/google/gwt/benchmark/launcher/OAuthWriter.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 Google 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 com.google.gwt.benchmark.launcher;
+
+import com.google.api.client.util.Charsets;
+import com.google.common.io.Files;
+import com.google.gwt.benchmark.oauth2.server.OAuthHelper;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+
+/**
+ * A command line utility to perform OAuth and save credentials.
+ */
+public class OAuthWriter {
+
+  public static void main(String[] args) {
+
+    if (args.length != 2) {
+      System.err.println("Wrong number of arguments");
+      printUsageAndExit();
+    }
+
+    String clientSecretJson = loadClientSecret(args[1]);
+
+    File oauthDirectoryHandle = new File(args[0]);
+    if (!oauthDirectoryHandle.isDirectory()) {
+      System.err.println("OAuth directory does not point to a directory" + args[0]);
+      printUsageAndExit();
+    }
+
+    try {
+      OAuthHelper.authorize(oauthDirectoryHandle, clientSecretJson);
+    } catch (IOException | GeneralSecurityException e) {
+      System.err.println("OAuth failed");
+      e.printStackTrace();
+      System.exit(-1);
+    }
+  }
+
+  private static void printUsageAndExit() {
+    System.err.println("// TODO(dankurka): Print usage");
+    System.exit(-1);
+  }
+
+  private static String loadClientSecret(String clientSecretFileName) {
+    try {
+      return Files.toString(new File(clientSecretFileName), Charsets.UTF_8);
+    } catch (IOException e) {
+      System.err.println("Can not read clientSecrets: " + clientSecretFileName);
+      e.printStackTrace(System.err);
+      System.exit(-1);
+    }
+    throw new IllegalStateException();
+  }
+}
diff --git a/oauth.sh b/oauth.sh
new file mode 100755
index 0000000..432a5c0
--- /dev/null
+++ b/oauth.sh
@@ -0,0 +1,14 @@
+#!/bin/bash -e
+
+if [[ -z "${1}" || -z "${2}" ]]; then
+  echo "usage: oauth.sh commitId <persistenceDir> <client_secret.json>" >&2
+  exit 1
+fi
+
+echo "Starting build"
+mvn -q install > /dev/null
+pushd launcher
+trap '{ echo "Hey, you pressed Ctrl-C.  Time to quit." ; popd; exit 0; }' INT
+echo "Starting oauth tool"
+mvn exec:java -Dexec.mainClass=com.google.gwt.benchmark.launcher.OAuthWriter -Dexec.args="${1} ${2}"
+
diff --git a/pom.xml b/pom.xml
index 2d27081..958d956 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,6 +15,5 @@
         <module>common</module>
         <module>compileserver</module>
         <module>launcher</module>
-        <module>dashboard</module>
     </modules>
 </project>
diff --git a/scripts/ui_compileserver b/scripts/ui_compileserver
new file mode 100755
index 0000000..8156e95
--- /dev/null
+++ b/scripts/ui_compileserver
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+ssh gwt@gwt-lab-compileserver -L 11100:localhost:8080 -f sleep 10; \
+google-chrome http://localhost:11100
diff --git a/start.sh b/start.sh
index 5cf38d6..4592f8e 100755
--- a/start.sh
+++ b/start.sh
@@ -1,10 +1,11 @@
 #!/bin/bash -e
 
 echo "Starting build"
-mvn -q install > /dev/null
+mvn -q clean install > /dev/null
 pushd launcher
 trap '{ echo "Hey, you pressed Ctrl-C.  Time to quit." ; popd; exit 0; }' INT
 echo "Starting server"
-mvn -q exec:java -Dexec.mainClass=com.google.gwt.benchmark.launcher.Launcher \
-    -DconfigFile=../../compileserver_config
+mvn exec:exec \
+    -Dexec.executable="java" \
+    -Dexec.args="-classpath %classpath -DconfigFile=../../compileserver_config -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5555 com.google.gwt.benchmark.launcher.Launcher"