/**
   * Return the graph for the given unique identifier for graph builder inputs on S3. If this is the
   * same as the last graph built, just return the pre-built graph. If not, build the graph from the
   * inputs, fetching them from S3 to the local cache as needed.
   */
  public synchronized Graph getGraph(String graphId) {

    LOG.info("Finding a graph for ID {}", graphId);

    if (graphId.equals(currGraphId)) {
      LOG.info("GraphID has not changed. Reusing the last graph that was built.");
      return currGraph;
    }

    // The location of the inputs that will be used to build this graph
    File graphDataDirectory = new File(GRAPH_CACHE_DIR, graphId);

    // If we don't have a local copy of the inputs, fetch graph data as a ZIP from S3 and unzip it
    if (!graphDataDirectory.exists() || graphDataDirectory.list().length == 0) {
      LOG.info("Downloading graph input files.");
      graphDataDirectory.mkdirs();
      S3Object graphDataZipObject = s3.getObject(graphBucket, graphId + ".zip");
      ZipInputStream zis = new ZipInputStream(graphDataZipObject.getObjectContent());
      try {
        ZipEntry entry;
        while ((entry = zis.getNextEntry()) != null) {
          File entryDestination = new File(graphDataDirectory, entry.getName());
          // Are both these mkdirs calls necessary?
          entryDestination.getParentFile().mkdirs();
          if (entry.isDirectory()) entryDestination.mkdirs();
          else {
            OutputStream entryFileOut = new FileOutputStream(entryDestination);
            IOUtils.copy(zis, entryFileOut);
            entryFileOut.close();
          }
        }
        zis.close();
      } catch (Exception e) {
        // TODO delete graph cache dir which is probably corrupted
        LOG.info("Error retrieving graph files", e);
      }
    } else {
      LOG.info("Graph input files were found locally. Using these files from the cache.");
    }

    // Now we have a local copy of these graph inputs. Make a graph out of them.
    CommandLineParameters params = new CommandLineParameters();
    params.build = new File(GRAPH_CACHE_DIR, graphId);
    params.inMemory = true;
    GraphBuilder graphBuilder = GraphBuilder.forDirectory(params, params.build);
    graphBuilder.run();
    Graph graph = graphBuilder.getGraph();
    graph.routerId = graphId;
    graph.index(new DefaultStreetVertexIndexFactory());
    graph.index.clusterStopsAsNeeded();
    this.currGraphId = graphId;
    this.currGraph = graph;
    return graph;
  }
  public static void main(String[] args) {
    /* Parse and validate command line parameters */
    CommandLineParameters params = new CommandLineParameters();
    try {
      JCommander jc = new JCommander(params, args);
      if (params.help) {
        jc.setProgramName("java -Xmx[several]G -jar otp.jar");
        jc.usage();
        System.exit(0);
      }
      params.infer();
    } catch (ParameterException pex) {
      LOG.error("Parameter error: {}", pex.getMessage());
      System.exit(1);
    }

    OTPConfigurator configurator = new OTPConfigurator(params);

    // start graph builder, if asked for
    GraphBuilderTask graphBuilder = configurator.builderFromParameters();
    if (graphBuilder != null) {
      graphBuilder.run();
      // Inform configurator which graph is to be used for in-memory handoff.
      if (params.inMemory) configurator.makeGraphService(graphBuilder.getGraph());
    }

    // start visualizer, if asked for
    GraphVisualizer graphVisualizer = configurator.visualizerFromParameters();
    if (graphVisualizer != null) {
      graphVisualizer.run();
    }

    // start web server, if asked for
    GrizzlyServer grizzlyServer = configurator.serverFromParameters();
    if (grizzlyServer != null) {
      while (true) { // Loop to restart server on uncaught fatal exceptions.
        try {
          grizzlyServer.run();
          return;
        } catch (Throwable throwable) {
          throwable.printStackTrace();
          LOG.error(
              "An uncaught {} occurred inside OTP. Restarting server.",
              throwable.getClass().getSimpleName());
        }
      }
    }

    if (graphBuilder == null && graphVisualizer == null && grizzlyServer == null) {
      LOG.info("Nothing to do. Use --help to see available tasks.");
      ;
    }
  }
  public CommandLineParameters clone() {
    CommandLineParameters ret;
    try {
      ret = (CommandLineParameters) super.clone();
    } catch (CloneNotSupportedException e) {
      return null;
    }

    if (this.routerIds != null) {
      ret.routerIds = Lists.newArrayList();
      ret.routerIds.addAll(this.routerIds);
    }

    return ret;
  }