package org.opentripplanner.standalone; import java.io.File; import java.io.IOException; import java.net.ServerSocket; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.opentripplanner.routing.impl.GraphServiceFileImpl; import com.beust.jcommander.IParameterValidator; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; /** * This is a JCommander-annotated class that holds parameters for OTP stand-alone mode. * These parameters can be parsed from the command line, or provided in a file using Jcommander's * at-symbol syntax (see http://jcommander.org/#Syntax). When stand-alone OTP is started as a * daemon, parameters are loaded from such a file, located by default in '/etc/opentripplanner.cfg'. * * Note that JCommander-annotated parameters can be any type that can be constructed from a string. * This module also contains classes for validating parameters. * See: http://jcommander.org/#Parameter_validation * * Some parameter fields are not initialized so when inferring other parameters, we can check for * null and see whether they were specified on the command line. * * @author abyrd */ public class CommandLineParameters { private static final int DEFAULT_PORT = 8080; private static final String DEFAULT_STATIC_DIRECTORY = "/var/otp/static"; private static final String DEFAULT_GRAPH_DIRECTORY = "/var/otp/graphs"; private static final String DEFAULT_CACHE_DIRECTORY = "/var/otp/cache"; private static final String DEFAULT_ROUTER_ID = ""; /* Options for the command itself, rather than build or server sub-tasks. */ @Parameter(names = { "-h", "--help"}, help = true, description = "Print this help message and exit") boolean help; @Parameter(names = { "-v", "--verbose" }, description = "Verbose output") boolean verbose; /* Options for the graph builder sub-task. */ @Parameter(names = {"-b", "--build"}, validateWith = ReadableDirectory.class, description = "build graphs at specified paths", variableArity = true) public List<File> build; @Parameter(names = { "-c", "--cache"}, validateWith = ReadWriteDirectory.class, description = "the directory under which to cache OSM and NED tiles") String cacheDirectory; @Parameter(names = { "-e", "--elevation"}, description = "download and use elevation data for the graph") boolean elevation; @Parameter(names = { "-m", "--inMemory"}, description = "pass the graph to the server in-memory after building it, without saving to disk") boolean inMemory; @Parameter(names = {"--noTransit"}, description = "skip all transit input files (GTFS)") boolean noTransit; @Parameter(names = {"--useTransfersTxt"}, description = "use transfers.txt file for the gtfsBundle (GTFS)") boolean useTransfersTxt; @Parameter(names = {"--noStreets"}, description = "skip all street input files (OSM)") boolean noStreets; /* Options for the server sub-task. */ @Parameter( names = { "-a", "--analyst"}, description = "enable OTP Analyst extensions") boolean analyst; @Parameter( names = { "-g", "--graphs"}, validateWith = ReadableDirectory.class, description = "path to graph directory") String graphDirectory; @Parameter( names = { "-p", "--port"}, validateWith = AvailablePort.class, description = "server port") Integer port; @Parameter( names = { "-r", "--router"}, validateWith = RouterId.class, description = "default router ID") String defaultRouterId; @Parameter( names = { "-s", "--server"}, description = "run a server") boolean server = false; @Parameter( names = { "-t", "--static"}, description = "path to static content") String staticDirectory; @Parameter( validateWith = ReadableFile.class, // the remaining parameters in one array description = "files") List<File> files = new ArrayList<File>(); /** Set some convenience parameters based on other parameters' values. */ public void infer () { server |= ( inMemory || port != null ); server |= ( defaultRouterId != null || graphDirectory != null || staticDirectory != null ); if (graphDirectory == null) graphDirectory = DEFAULT_GRAPH_DIRECTORY; if (defaultRouterId == null) defaultRouterId = DEFAULT_ROUTER_ID; if (staticDirectory == null) staticDirectory = DEFAULT_STATIC_DIRECTORY; if (cacheDirectory == null) cacheDirectory = DEFAULT_CACHE_DIRECTORY; if (port == null) port = DEFAULT_PORT; new AvailablePort().validate(port); } public static class ReadableFile implements IParameterValidator { @Override public void validate(String name, String value) throws ParameterException { File file = new File(value); if ( ! file.isFile()) { String msg = String.format("%s: '%s' is not a file.", name, value); throw new ParameterException(msg); } if ( ! file.canRead()) { String msg = String.format("%s: file '%s' is not readable.", name, value); throw new ParameterException(msg); } } } public static class ReadableDirectory implements IParameterValidator { @Override public void validate(String name, String value) throws ParameterException { File file = new File(value); if ( ! file.isDirectory()) { String msg = String.format("%s: '%s' is not a directory.", name, value); throw new ParameterException(msg); } if ( ! file.canRead()) { String msg = String.format("%s: directory '%s' is not readable.", name, value); throw new ParameterException(msg); } } } public static class ReadWriteDirectory implements IParameterValidator { @Override public void validate(String name, String value) throws ParameterException { new ReadableDirectory().validate(name, value); File file = new File(value); if ( ! file.canWrite()) { String msg = String.format("%s: directory '%s' is not writable.", name, value); throw new ParameterException(msg); } } } public static class PositiveInteger implements IParameterValidator { @Override public void validate(String name, String value) throws ParameterException { Integer i = Integer.parseInt(value); if ( i <= 0 ) { String msg = String.format("%s must be a positive integer.", name); throw new ParameterException(msg); } } } public static class AvailablePort implements IParameterValidator { @Override public void validate(String name, String value) throws ParameterException { new PositiveInteger().validate(name, value); int port = Integer.parseInt(value); this.validate(port); } public void validate(int port) throws ParameterException { ServerSocket socket = null; boolean portUnavailable = false; String reason = null; try { socket = new ServerSocket(port); } catch (IOException e) { portUnavailable = true; reason = e.getMessage(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { // will not be thrown } } } if ( portUnavailable ) { String msg = String.format(": port %d is not available. %s.", port, reason); throw new ParameterException(msg); } } } public static class RouterId implements IParameterValidator { @Override public void validate(String name, String value) throws ParameterException { Pattern routerIdPattern = GraphServiceFileImpl.routerIdPattern; Matcher m = routerIdPattern.matcher(value); if ( ! m.matches()) { String msg = String.format("%s: '%s' is not a valid router ID.", name, value); throw new ParameterException(msg); } } } }