Super Dev Mode: rename to Outbox and OutboxTable

External visible change: better error checking in SourceHandler
when a module isn't found.

Renamed ModuleState to Outbox and Modules to OutboxTable, and fixed
usages. This is in preparation for implementing fast switching between
browsers. Outboxes won't be one-to-one with modules, so "module"
shouldn't be in the class name anymore.

Bug: issue 8882
Change-Id: I6a0090d99096ff3defed09f75220f5d14b07d360
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
index c490ee4..6c9d808 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
@@ -58,10 +58,10 @@
       PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
       logger.setMaxDetail(options.getLogLevel());
 
-      Modules modules;
+      OutboxTable outboxes;
 
       try {
-        modules = makeModules(options, logger);
+        outboxes = makeOutboxes(options, logger);
       } catch (Throwable t) {
         t.printStackTrace();
         System.out.println("FAIL");
@@ -75,7 +75,7 @@
         try {
           // TODO: actually test recompiling here.
           // (This is just running precompiles repeatedly.)
-          modules.defaultCompileAll(options.getNoPrecompile(), logger);
+          outboxes.defaultCompileAll(options.getNoPrecompile(), logger);
         } catch (Throwable t) {
           t.printStackTrace();
           System.out.println("FAIL");
@@ -116,13 +116,13 @@
     topLogger.setMaxDetail(options.getLogLevel());
 
     TreeLogger startupLogger = topLogger.branch(Type.INFO, "Super Dev Mode starting up");
-    Modules modules = makeModules(options, startupLogger);
+    OutboxTable outboxes = makeOutboxes(options, startupLogger);
 
-    SourceHandler sourceHandler = new SourceHandler(modules);
+    SourceHandler sourceHandler = new SourceHandler(outboxes);
     ProgressTable progressTable = new ProgressTable();
-    JobRunner runner = new JobRunner(progressTable, modules);
+    JobRunner runner = new JobRunner(progressTable, outboxes);
 
-    WebServer webServer = new WebServer(sourceHandler, modules,
+    WebServer webServer = new WebServer(sourceHandler, outboxes,
         runner, progressTable, options.getBindAddress(), options.getPort());
     webServer.start(topLogger);
 
@@ -132,20 +132,20 @@
   /**
    * Configures and compiles all the modules (unless {@link Options#getNoPrecompile} is false).
    */
-  private static Modules makeModules(Options options, TreeLogger logger)
+  private static OutboxTable makeOutboxes(Options options, TreeLogger logger)
       throws IOException, UnableToCompleteException {
 
     File workDir = ensureWorkDir(options);
     logger.log(Type.INFO, "workDir: " + workDir);
 
-    Modules modules = new Modules(options);
+    OutboxTable outboxes = new OutboxTable(options);
     for (String moduleName : options.getModuleNames()) {
       AppSpace appSpace = AppSpace.create(new File(workDir, moduleName));
 
       Recompiler recompiler = new Recompiler(appSpace, moduleName, options);
-      modules.addModuleState(new ModuleState(recompiler, options.getNoPrecompile(), logger));
+      outboxes.addOutbox(new Outbox(recompiler, options.getNoPrecompile(), logger));
     }
-    return modules;
+    return outboxes;
   }
 
   /**
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
index 482d10d..4da9f3f 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
@@ -60,8 +60,7 @@
 
   /**
    * Creates a job to recompile a module.
-   * @param moduleName The name of the module to recompile, suitable for
-   *     passing to {@link Modules#get}.
+   * @param moduleName The client-side name of the module to recompile (after renaming).
    * @param bindingProperties  Properties that uniquely identify a permutation.
    *     (Otherwise, more than one permutation will be compiled.)
    * @param parentLogger  The parent of the logger that will be used for this job.
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobRunner.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobRunner.java
index f0507a3..abdf13c 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobRunner.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobRunner.java
@@ -31,12 +31,12 @@
  */
 public class JobRunner {
   private final ProgressTable table;
-  private final Modules modules;
+  private final OutboxTable outboxes;
   private final ExecutorService executor = Executors.newSingleThreadExecutor();
 
-  JobRunner(ProgressTable table, Modules modules) {
+  JobRunner(ProgressTable table, OutboxTable outboxes) {
     this.table = table;
-    this.modules = modules;
+    this.outboxes = outboxes;
   }
 
   /**
@@ -50,22 +50,22 @@
     executor.submit(new Runnable() {
       @Override
       public void run() {
-        recompile(job, modules);
+        recompile(job, outboxes);
       }
     });
     job.getLogger().log(Type.TRACE, "added job to queue");
   }
 
-  private static void recompile(Job job, Modules modules) {
+  private static void recompile(Job job, OutboxTable outboxes) {
     job.getLogger().log(Type.INFO, "starting job: " + job.getId());
-    ModuleState m = modules.get(job.getModuleName());
-    if (m == null) {
+    Outbox box = outboxes.findOutbox(job);
+    if (box == null) {
       String msg = "skipped a compile job with an unknown module: " + job.getModuleName();
       job.getLogger().log(Type.WARN, msg);
       job.onFinished(new Result(job, null,  new RuntimeException(msg)));
       return;
     }
 
-    m.recompile(job);
+    box.recompile(job);
   }
 }
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Modules.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Modules.java
deleted file mode 100644
index 48cfcbf..0000000
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Modules.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2011 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.dev.codeserver;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.dev.json.JsonArray;
-import com.google.gwt.dev.json.JsonObject;
-import com.google.gwt.thirdparty.guava.common.collect.Maps;
-
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * An in-memory directory of all the modules available on this code server. The {@link WebServer}
- * uses this directory to find the {@link ModuleState} associated with a URL and to list all the
- * modules on the front page.
- */
-class Modules implements Iterable<String> {
-  private final Options options;
-  private final Map<String, ModuleState> moduleStateMap = Maps.newHashMap();
-
-  public Modules(Options options) {
-    this.options = options;
-  }
-
-  /**
-   * Adds a {@link ModuleState} to the map.
-   * @param state the module state to map
-   */
-  public void addModuleState(ModuleState state) {
-    moduleStateMap.put(state.getModuleName(), state);
-  }
-
-  /**
-   * Retrieves a {@link ModuleState} corresponding to a given module name.
-   * This should be the module name after renaming.
-   */
-  public ModuleState get(String moduleName) {
-    // TODO: maybe this lookup should also succeed if passed the module name before renaming?
-    // (I believe currently everything breaks if you change how a module is renamed, requiring
-    // a restart.)
-    return moduleStateMap.get(moduleName);
-  }
-
-  /**
-   * Iterates over the list of modules.
-   */
-  @Override
-  public Iterator<String> iterator() {
-    return moduleStateMap.keySet().iterator();
-  }
-
-  void defaultCompileAll(boolean noPrecompile, TreeLogger logger) throws UnableToCompleteException {
-    for (ModuleState m: moduleStateMap.values()) {
-      m.maybePrecompile(noPrecompile, logger);
-    }
-  }
-
-  /**
-   * Returns a configuration object containing the names of all the modules
-   * and warnings to display to the user.
-   */
-  JsonObject getConfig() {
-    JsonObject config = JsonObject.create();
-    JsonArray moduleNames = new JsonArray();
-    for (String module : this) {
-      moduleNames.add(module);
-    }
-    config.put("moduleNames", moduleNames);
-    config.put("warnings", options.getWarningsAsJson());
-    return config;
-  }
-}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
similarity index 96%
rename from dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java
rename to dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
index c52098a..289e8d5 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
@@ -33,10 +33,10 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
- * Contains everything that the code server knows about a GWT app (module), including how
- * to recompile it and where the compiler output is.
+ * Holds the compiler output for one module.
+ * TODO(skybrian) there will later be a separate Outbox for each set of binding properties.
  */
-class ModuleState {
+class Outbox {
 
   /**
    * The suffix that the GWT compiler uses when writing a sourcemap file.
@@ -46,8 +46,7 @@
   private final AtomicReference<Job.Result> published = new AtomicReference<Job.Result>();
   private final Recompiler recompiler;
 
-  ModuleState(Recompiler recompiler, boolean noPrecompile,
-      TreeLogger logger)
+  Outbox(Recompiler recompiler, boolean noPrecompile, TreeLogger logger)
       throws UnableToCompleteException {
     this.recompiler = recompiler;
     maybePrecompile(noPrecompile, logger);
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/OutboxTable.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/OutboxTable.java
new file mode 100644
index 0000000..9f3e718
--- /dev/null
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/OutboxTable.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2011 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.dev.codeserver;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.json.JsonArray;
+import com.google.gwt.dev.json.JsonObject;
+import com.google.gwt.thirdparty.guava.common.collect.Maps;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * An in-memory table of all the outboxes available on this code server. The {@link WebServer}
+ * uses this directory to find the {@link Outbox} associated with a URL and to list all the
+ * modules on the front page.
+ */
+class OutboxTable {
+  private final Options options;
+
+  /**
+   * A map from client-side module names (after renaming) to its outbox.
+   */
+  private final Map<String, Outbox> outboxes = Maps.newHashMap();
+
+  OutboxTable(Options options) {
+    this.options = options;
+  }
+
+  /**
+   * Adds a {@link Outbox} to the table.
+   */
+  void addOutbox(Outbox outbox) {
+    outboxes.put(outbox.getModuleName(), outbox);
+  }
+
+  /**
+   * Returns the outbox where the output of a compile job will be published,
+   * or null if it can't be found.
+   */
+  Outbox findOutbox(Job job) {
+    return findByModuleName(job.getModuleName());
+  }
+
+  /**
+   * Retrieves a {@link Outbox} corresponding to a given module name.
+   * This should be the module name after renaming.
+   * TODO: remove and use an Outbox id instead.
+   */
+  Outbox findByModuleName(String moduleName) {
+    return outboxes.get(moduleName);
+  }
+
+  /**
+   * Returns the list of known module names (after renaming).
+   */
+  Collection<String> getModuleNames() {
+    return outboxes.keySet();
+  }
+
+  void defaultCompileAll(boolean noPrecompile, TreeLogger logger) throws UnableToCompleteException {
+    for (Outbox box: outboxes.values()) {
+      box.maybePrecompile(noPrecompile, logger);
+    }
+  }
+
+  /**
+   * Returns a configuration object containing the names of all the modules
+   * and warnings to display to the user.
+   */
+  JsonObject getConfig() {
+    JsonObject config = JsonObject.create();
+    JsonArray moduleNames = new JsonArray();
+    for (String module : getModuleNames()) {
+      moduleNames.add(module);
+    }
+    config.put("moduleNames", moduleNames);
+    config.put("warnings", options.getWarningsAsJson());
+    return config;
+  }
+}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
index 2b81781..d737012 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
@@ -21,6 +21,8 @@
 import com.google.gwt.thirdparty.debugging.sourcemap.SourceMapConsumerV3;
 import com.google.gwt.thirdparty.debugging.sourcemap.SourceMapParseException;
 
+import java.io.File;
+
 /**
  * A mapping from Java lines to JavaScript.
  */
@@ -35,9 +37,9 @@
    * Reads a source map from disk and parses it into an in-memory representation.
    * If it can't be loaded, logs a warning and returns an empty source map.
    */
-  static ReverseSourceMap load(TreeLogger logger, ModuleState moduleState) {
+  static ReverseSourceMap load(TreeLogger logger, File sourceMapFile) {
     SourceMapConsumerV3 consumer = new SourceMapConsumerV3();
-    String unparsed = Util.readFileAsString(moduleState.findSourceMapForOnePermutation());
+    String unparsed = Util.readFileAsString(sourceMapFile);
     try {
       consumer.parse(unparsed);
       return new ReverseSourceMap(consumer);
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
index bc57320..d841530 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
@@ -74,10 +74,10 @@
 
   static final String SOURCEROOT_TEMPLATE_VARIABLE = "$sourceroot_goes_here$";
 
-  private Modules modules;
+  private OutboxTable outboxes;
 
-  SourceHandler(Modules modules) {
-    this.modules = modules;
+  SourceHandler(OutboxTable outboxes) {
+    this.outboxes = outboxes;
   }
 
   static boolean isSourceMapRequest(String target) {
@@ -100,6 +100,13 @@
       throw new RuntimeException("invalid request (shouldn't happen): " + target);
     }
 
+    Outbox box = outboxes.findByModuleName(moduleName);
+    if (box == null) {
+      response.sendError(HttpServletResponse.SC_NOT_FOUND);
+      logger.log(TreeLogger.WARN, "unknown module; returned not found for request: " + target);
+      return;
+    }
+
     String rootDir = SOURCEMAP_PATH + moduleName + "/";
     String rest = target.substring(rootDir.length());
 
@@ -109,18 +116,15 @@
       // This URL is no longer used by debuggers (we use the strong name) but is used for testing.
       // It's useful not to need the strong name to download the sourcemap.
       // (But this only works when there is one permutation.)
-      ModuleState moduleState = modules.get(moduleName);
-      sendSourceMap(moduleName, moduleState.findSourceMapForOnePermutation(), request, response,
-          logger);
+      sendSourceMap(moduleName, box.findSourceMapForOnePermutation(), request, response, logger);
     } else if (rest.endsWith("/")) {
       sendFileListPage(moduleName, rest, response, logger);
     } else if (rest.endsWith(".java")) {
-      sendSourceFile(moduleName, rest, request.getQueryString(), response, logger);
+      sendSourceFile(box, rest, request.getQueryString(), response, logger);
     } else {
       String strongName = getStrongNameFromSourcemapFilename(rest);
       if (strongName != null) {
-        ModuleState moduleState = modules.get(moduleName);
-        File sourceMap = moduleState.findSourceMap(strongName).getAbsoluteFile();
+        File sourceMap = box.findSourceMap(strongName).getAbsoluteFile();
         sendSourceMap(moduleName, sourceMap, request, response, logger);
       } else {
         response.sendError(HttpServletResponse.SC_NOT_FOUND);
@@ -205,10 +209,9 @@
    * Sends an HTTP response containing a Java source. It will be sent as plain text by default,
    * or as HTML if the query string is equal to "html".
    */
-  private void sendSourceFile(String moduleName, String sourcePath, String query,
+  private void sendSourceFile(Outbox box, String sourcePath, String query,
       HttpServletResponse response, TreeLogger logger) throws IOException {
-    ModuleState moduleState = modules.get(moduleName);
-    InputStream pageBytes = moduleState.openSourceFile(sourcePath);
+    InputStream pageBytes = box.openSourceFile(sourcePath);
 
     if (pageBytes == null) {
       response.sendError(HttpServletResponse.SC_NOT_FOUND);
@@ -218,7 +221,7 @@
 
     if (query != null && query.equals("html")) {
       BufferedReader reader = new BufferedReader(new InputStreamReader(pageBytes));
-      sendSourceFileAsHtml(moduleName, sourcePath, reader, response, logger);
+      sendSourceFileAsHtml(box, sourcePath, reader, response, logger);
     } else {
       PageUtil.sendStream("text/plain", pageBytes, response);
     }
@@ -229,10 +232,11 @@
    * that have corresponding JavaScript will be highlighted (as determined by reading the
    * source map).
    */
-  private void sendSourceFileAsHtml(String moduleName, String sourcePath, BufferedReader lines,
+  private void sendSourceFileAsHtml(Outbox box, String sourcePath, BufferedReader lines,
       HttpServletResponse response, TreeLogger logger) throws IOException {
 
-    ReverseSourceMap sourceMap = ReverseSourceMap.load(logger, modules.get(moduleName));
+    ReverseSourceMap sourceMap = ReverseSourceMap.load(logger,
+        box.findSourceMapForOnePermutation());
 
     File sourceFile = new File(sourcePath);
 
@@ -276,7 +280,7 @@
   }
 
   private SourceMap loadSourceMap(String moduleName) {
-    ModuleState moduleState = modules.get(moduleName);
-    return SourceMap.load(moduleState.findSourceMapForOnePermutation());
+    Outbox box = outboxes.findByModuleName(moduleName);
+    return SourceMap.load(box.findSourceMapForOnePermutation());
   }
 }
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
index 29d8b6a..9500126 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
@@ -90,7 +90,7 @@
   private static final MimeTypes MIME_TYPES = new MimeTypes();
 
   private final SourceHandler handler;
-  private final Modules modules;
+  private final OutboxTable outboxes;
   private final JobRunner runner;
   private final ProgressTable progressTable;
 
@@ -99,10 +99,10 @@
 
   private Server server;
 
-  WebServer(SourceHandler handler, Modules modules, JobRunner runner,
+  WebServer(SourceHandler handler, OutboxTable outboxes, JobRunner runner,
       ProgressTable progressTable, String bindAddress, int port) {
     this.handler = handler;
-    this.modules = modules;
+    this.outboxes = outboxes;
     this.runner = runner;
     this.progressTable = progressTable;
     this.bindAddress = bindAddress;
@@ -153,7 +153,7 @@
    * Returns the location of the compiler output. (Changes after every recompile.)
    */
   public File getCurrentWarDir(String moduleName) {
-    return modules.get(moduleName).getWarDir();
+    return outboxes.findByModuleName(moduleName).getWarDir();
   }
 
   private void handleRequest(String target, HttpServletRequest request,
@@ -179,14 +179,14 @@
 
     if (target.equals("/")) {
       setHandled(request);
-      JsonObject config = modules.getConfig();
+      JsonObject config = outboxes.getConfig();
       PageUtil.sendJsonAndHtml("config", config, "frontpage.html", response, logger);
       return;
     }
 
     if (target.equals("/dev_mode_on.js")) {
       setHandled(request);
-      JsonObject config = modules.getConfig();
+      JsonObject config = outboxes.getConfig();
       PageUtil
           .sendJsonAndJavaScript("__gwt_codeserver_config", config, "dev_mode_on.js", response,
               logger);
@@ -198,8 +198,8 @@
     if (target.startsWith("/recompile/")) {
       setHandled(request);
       String moduleName = target.substring("/recompile/".length());
-      ModuleState moduleState = modules.get(moduleName);
-      if (moduleState == null) {
+      Outbox outbox = outboxes.findByModuleName(moduleName);
+      if (outbox == null) {
         response.sendError(HttpServletResponse.SC_NOT_FOUND);
         logger.log(TreeLogger.WARN, "not found: " + target);
         return;
@@ -211,11 +211,11 @@
       // cause a spurious recompile, resulting in an unexpected permutation being loaded later.
       //
       // It would be unsafe to allow a configuration property to be changed.
-      Job job = new Job(moduleState.getModuleName(), getBindingProperties(request), logger);
+      Job job = new Job(outbox.getModuleName(), getBindingProperties(request), logger);
       runner.submit(job);
       boolean ok = job.waitForResult().isOk();
 
-      JsonObject config = modules.getConfig();
+      JsonObject config = outboxes.getConfig();
       config.put("status", ok ? "ok" : "failed");
       sendJsonResult(config, request, response, logger);
       return;
@@ -224,7 +224,7 @@
     if (target.startsWith("/log/")) {
       setHandled(request);
       String moduleName = target.substring("/log/".length());
-      File file = modules.get(moduleName).getCompileLog();
+      File file = outboxes.findByModuleName(moduleName).getCompileLog();
       sendLogPage(moduleName, file, response);
       return;
     }
@@ -299,12 +299,12 @@
 
     int secondSlash = target.indexOf('/', 1);
     String moduleName = target.substring(1, secondSlash);
-    ModuleState moduleState = modules.get(moduleName);
+    Outbox outbox = outboxes.findByModuleName(moduleName);
 
-    File file = moduleState.getOutputFile(target);
+    File file = outbox.getOutputFile(target);
     if (!file.isFile()) {
       // perhaps it's compressed
-      file = moduleState.getOutputFile(target + ".gz");
+      file = outbox.getOutputFile(target + ".gz");
       if (!file.isFile()) {
         response.sendError(HttpServletResponse.SC_NOT_FOUND);
         logger.log(TreeLogger.WARN, "not found: " + file.toString());
@@ -332,7 +332,7 @@
 
   private void sendModulePage(String moduleName, HttpServletResponse response, TreeLogger logger)
       throws IOException {
-    ModuleState module = modules.get(moduleName);
+    Outbox module = outboxes.findByModuleName(moduleName);
     if (module == null) {
       response.sendError(HttpServletResponse.SC_NOT_FOUND);
       logger.log(TreeLogger.WARN, "module not found: " + moduleName);
@@ -357,8 +357,8 @@
 
     out.startTag("h1").text("Policy Files").endTag("h1").nl();
 
-    for (String moduleName : modules) {
-      ModuleState module = modules.get(moduleName);
+    for (String moduleName : outboxes.getModuleNames()) {
+      Outbox module = outboxes.findByModuleName(moduleName);
       File manifest = module.getExtraFile("rpcPolicyManifest/manifest.txt");
       if (manifest.isFile()) {
         out.startTag("h2").text(moduleName).endTag("h2").nl();
@@ -415,8 +415,8 @@
       return;
     }
 
-    for (String moduleName : modules) {
-      ModuleState module = modules.get(moduleName);
+    for (String moduleName : outboxes.getModuleNames()) {
+      Outbox module = outboxes.findByModuleName(moduleName);
       File policy = module.getOutputFile(moduleName + "/" + rest);
       if (policy.isFile()) {
         PageUtil.sendFile("text/plain", policy, response);