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

org.opentripplanner.standalone.OTPMain Maven / Gradle / Ivy

There is a newer version: 2.6.0
Show newest version
package org.opentripplanner.standalone;

import static org.opentripplanner.model.projectinfo.OtpProjectInfo.projectInfo;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
import org.geotools.referencing.factory.DeferredAuthorityFactory;
import org.geotools.util.WeakCollectionCleaner;
import org.opentripplanner.graph_builder.GraphBuilder;
import org.opentripplanner.routing.graph.SerializedGraphObject;
import org.opentripplanner.standalone.config.CommandLineParameters;
import org.opentripplanner.standalone.configure.ConstructApplication;
import org.opentripplanner.standalone.configure.LoadApplication;
import org.opentripplanner.standalone.server.GrizzlyServer;
import org.opentripplanner.transit.raptor.configure.RaptorConfig;
import org.opentripplanner.transit.service.TransitModel;
import org.opentripplanner.updater.configure.UpdaterConfigurator;
import org.opentripplanner.util.OtpAppException;
import org.opentripplanner.util.ThrowableUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

/**
 * This is the main entry point to OpenTripPlanner. It allows both building graphs and starting up
 * an OTP server depending on command line options. OTPMain is a concrete class making it possible
 * to construct one with custom CommandLineParameters and use its graph builder construction method
 * from web services or scripts, not just from the static main function below.
 */
public class OTPMain {

  private static final Logger LOG = LoggerFactory.getLogger(OTPMain.class);

  static {
    // Disable HSQLDB reconfiguration of Java Unified Logging (j.u.l)
    //noinspection AccessOfSystemProperties
    System.setProperty("hsqldb.reconfig_logging", "false");

    // Remove existing handlers attached to the j.u.l root logger
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    // Bridge j.u.l (used by Jersey) to the SLF4J root logger, so all logging goes through the same API
    SLF4JBridgeHandler.install();
  }

  /**
   * ENTRY POINT: This is the main method that is called when running otp.jar from the command
   * line.
   */
  public static void main(String[] args) {
    try {
      CommandLineParameters params = parseAndValidateCmdLine(args);
      OtpStartupInfo.logInfo();
      startOTPServer(params);
    } catch (OtpAppException ae) {
      LOG.error(ae.getMessage(), ae);
      System.exit(100);
    } catch (Exception e) {
      LOG.error("An uncaught error occurred inside OTP: {}", e.getLocalizedMessage(), e);
      System.exit(-1);
    }
  }

  /**
   * Parse and validate command line parameters. If the arguments is invalid the method uses {@code
   * System.exit()} to exit the application.
   */
  private static CommandLineParameters parseAndValidateCmdLine(String[] args) {
    CommandLineParameters params = new CommandLineParameters();
    try {
      // It is tempting to use JCommander's command syntax: http://jcommander.org/#_more_complex_syntaxes_commands
      // But this seems to lead to confusing switch ordering and more difficult subsequent use of the
      // parsed commands, since there will be three separate objects.
      JCommander jc = JCommander.newBuilder().addObject(params).args(args).build();
      if (params.version) {
        System.out.println("OpenTripPlanner " + projectInfo().getVersionString());
        System.exit(0);
      }
      if (params.serializationVersionId) {
        System.out.println(projectInfo().getOtpSerializationVersionId());
        System.exit(0);
      }
      if (params.help) {
        System.out.println("OpenTripPlanner " + projectInfo().getVersionString());
        jc.setProgramName("java -Xmx4G -jar otp.jar");
        jc.usage();
        System.exit(0);
      }
      params.inferAndValidate();
    } catch (ParameterException pex) {
      LOG.error("Parameter error: {}", pex.getMessage());
      System.exit(1);
    }
    return params;
  }

  /**
   * All startup logic is in an instance method instead of the static main method so it is possible
   * to build graphs from web services or scripts, not just from the command line. If options cause
   * an OTP API server to start up, this method will return when the web server shuts down.
   *
   * @throws RuntimeException if an error occurs while loading the graph.
   */
  private static void startOTPServer(CommandLineParameters cli) {
    boolean graphAvailable = false;
    LOG.info(
      "Searching for configuration and input files in {}",
      cli.getBaseDirectory().getAbsolutePath()
    );

    // Init loading phase (Separate DI scope)
    var loadApp = new LoadApplication(cli);
    var config = loadApp.config();

    // Validate data sources, command line arguments and config before loading and
    // processing input data to fail early
    loadApp.validateConfigAndDataSources();

    ConstructApplication app = null;

    /* Load graph from disk if one is not present from build. */
    if (cli.doLoadGraph() || cli.doLoadStreetGraph()) {
      SerializedGraphObject obj = SerializedGraphObject.load(loadApp.getInputGraphDataStore());
      app = loadApp.appConstruction(obj);
      config.updateConfigFromSerializedGraph(obj.buildConfig, obj.routerConfig);
      graphAvailable = true;
    } else {
      app = loadApp.appConstruction();
    }

    /* Start graph builder if requested. */
    if (cli.doBuildStreet() || cli.doBuildTransit()) {
      // Abort building a graph if the file can not be saved
      SerializedGraphObject.verifyTheOutputGraphIsWritableIfDataSourceExist(
        app.graphOutputDataSource()
      );

      GraphBuilder graphBuilder = app.createGraphBuilder();
      if (graphBuilder != null) {
        graphBuilder.run();
        graphAvailable = true;
      } else {
        throw new IllegalStateException("An error occurred while building the graph.");
      }
      // Store graph and config used to build it, also store router-config for easy deployment
      // with using the embedded router config.
      new SerializedGraphObject(
        app.graph(),
        app.transitModel(),
        config.buildConfig(),
        config.routerConfig()
      )
        .save(app.graphOutputDataSource());
      // Log size info for the deduplicator
      LOG.info("Memory optimized {}", app.graph().deduplicator.toString());
    }

    if (!graphAvailable) {
      LOG.error("Nothing to do, no graph loaded or build. Exiting.");
      System.exit(101);
    }

    if (cli.doServe()) {
      startOtpWebServer(cli, app);
    } else {
      LOG.info("Done building graph. Exiting.");
    }
  }

  private static void startOtpWebServer(CommandLineParameters params, ConstructApplication app) {
    // Index graph for travel search
    app.transitModel().index();
    app.graph().index(app.transitModel().getStopModel());

    // publishing the config version info make it available to the APIs
    setOtpConfigVersionsOnServerInfo(app);

    /* Start visualizer if requested. */
    if (params.visualize) {
      app.graphVisualizer().run();
    }

    /* Start web server if requested. */
    // We could start the server first so it can report build/load progress to a load balancer.
    // This would also avoid the awkward call to set the router on the appConstruction after it's constructed.
    // However, currently the server runs in a blocking way and waits for shutdown, so has to run last.
    if (params.doServe()) {
      GrizzlyServer grizzlyServer = app.createGrizzlyServer();

      registerShutdownHookToGracefullyShutDownServer(app.transitModel(), app.raptorConfig());

      // Loop to restart server on uncaught fatal exceptions.
      while (true) {
        try {
          grizzlyServer.run();
          return;
        } catch (Throwable throwable) {
          LOG.error(
            "An uncaught error occurred inside OTP. Restarting server. Error was: {}",
            ThrowableUtils.detailedString(throwable)
          );
        }
        logLocationOfRequestLog(app.routerConfig().requestLogFile());
      }
    }
  }

  /**
   * Shut down this server when evicted or (auto-)reloaded.
   * 
    *
  1. Stop any real-time updater threads.
  2. *
  3. Cleanup various stuff of some used libraries (org.geotools), which depend on the * external client to call them for cleaning-up.
  4. *
*/ private static void registerShutdownHookToGracefullyShutDownServer( TransitModel transitModel, RaptorConfig raptorConfig ) { var hook = new Thread(() -> { LOG.info("OTP shutdown started..."); UpdaterConfigurator.shutdownGraph(transitModel); raptorConfig.shutdown(); WeakCollectionCleaner.DEFAULT.exit(); DeferredAuthorityFactory.exit(); }); Runtime.getRuntime().addShutdownHook(hook); } private static void logLocationOfRequestLog(String requestLogFile) { if (requestLogFile != null) { LOG.info("Logging incoming requests at '{}'", requestLogFile); } else { LOG.info("Incoming requests will not be logged."); } } private static void setOtpConfigVersionsOnServerInfo(ConstructApplication app) { projectInfo().otpConfigVersion = app.otpConfig().configVersion; projectInfo().buildConfigVersion = app.buildConfig().configVersion; projectInfo().routerConfigVersion = app.routerConfig().getConfigVersion(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy