From e9be0c97b234f07d926344955b3a19640c83ef69 Mon Sep 17 00:00:00 2001 From: Tobias Pietzsch <tobias.pietzsch@gmail.com> Date: Wed, 11 Feb 2015 17:11:34 +0100 Subject: [PATCH] clean up command-line argument processing: * use Parameters object instead of static variables. * try to get a reasonable default hostname using java.net.InetAddress before falling back to "localhost". * print usage information if there is any problem evaluating the arguments. --- src/main/java/bdv/server/BigDataServer.java | 257 ++++++++++++++------ 1 file changed, 177 insertions(+), 80 deletions(-) diff --git a/src/main/java/bdv/server/BigDataServer.java b/src/main/java/bdv/server/BigDataServer.java index 67d05bd..a906b79 100644 --- a/src/main/java/bdv/server/BigDataServer.java +++ b/src/main/java/bdv/server/BigDataServer.java @@ -1,6 +1,7 @@ package bdv.server; import mpicbg.spim.data.SpimDataException; + import org.apache.commons.cli.*; import org.apache.commons.lang.StringUtils; import org.eclipse.jetty.server.Connector; @@ -15,37 +16,69 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.thread.QueuedThreadPool; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.List; - +import java.util.Map; +import java.util.Map.Entry; + +/** + * Serve XML/HDF5 datasets over HTTP. + * + * <pre> + * usage: BigDataServer [OPTIONS] [NAME XML]... + * Serves one or more XML/HDF5 datasets for remote access over HTTP. + * Provide (NAME XML) pairs on the command line or in a dataset file, where + * NAME is the name under which the dataset should be made accessible and XML + * is the path to the XML file of the dataset. + * -d <FILE> Dataset file: A plain text file specifying one dataset + * per line. Each line is formatted as "NAME <TAB> XML". + * -p <PORT> Listening port. (default: 8080) + * -s <HOSTNAME> Hostname of the server. + * </pre> + * + * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> + * @author HongKee Moon <moon@mpi-cbg.de> + */ public class BigDataServer { - static HashMap< String, String > dataSet = new HashMap<>(); - private static final org.eclipse.jetty.util.log.Logger LOG = Log.getLogger( BigDataServer.class ); - static int port; - - static String serverName; + static Parameters getDefaultParameters() + { + final int port = 8080; + String hostname; + try + { + hostname = InetAddress.getLocalHost().getHostName(); + } + catch ( final UnknownHostException e ) + { + hostname = "localhost"; + } + return new Parameters( port, hostname, new HashMap< String, String >() ); + } public static void main( final String[] args ) throws Exception { - if ( !processOptions( args ) ) - return; - System.setProperty( "org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog" ); + final Parameters params = processOptions( args, getDefaultParameters() ); + if ( params == null ) + return; + // Threadpool for multiple connections final Server server = new Server( new QueuedThreadPool( 1000, 10 ) ); // ServerConnector configuration final ServerConnector connector = new ServerConnector( server ); - connector.setHost( serverName ); - connector.setPort( port ); + connector.setHost( params.getHostname() ); + connector.setPort( params.getPort() ); LOG.info( "Set connectors: " + connector ); server.setConnectors( new Connector[] { connector } ); @@ -53,7 +86,7 @@ public class BigDataServer final ConnectorStatistics connectorStats = new ConnectorStatistics(); connector.addBean( connectorStats ); - final String baseURL = "http://" + server.getURI().getHost() + ":" + port; + final String baseURL = "http://" + server.getURI().getHost() + ":" + params.getPort(); LOG.info( "Server Base URL: " + baseURL ); // Handler initialization @@ -61,7 +94,7 @@ public class BigDataServer final HandlerCollection handlers = new HandlerCollection(); - final ContextHandlerCollection datasetHandlers = createHandlers( baseURL, dataSet ); + final ContextHandlerCollection datasetHandlers = createHandlers( baseURL, params.getDatasets() ); handlers.addHandler( datasetHandlers ); handlers.addHandler( new ManagerHandler( baseURL, server, connectorStats, statHandler, datasetHandlers ) ); handlers.addHandler( new RequestLogHandler() ); @@ -76,107 +109,171 @@ public class BigDataServer server.join(); } - static private boolean processOptions( String[] args ) throws ParseException, IOException + /** + * Server parameters: hostname, port, datasets. + */ + private static class Parameters { - // create Options object - Options options = new Options(); + private final int port; - // add p option - options.addOption( "p", true, "listening port" ); + private final String hostname; - // add s baseurl - options.addOption( "s", true, "server name" ); + /** + * maps from dataset name to dataset xml path. + */ + private final Map< String, String > datasetNameToXml; - // -d or multiple {name name.xml} pairs - options.addOption( "d", true, "dataset file" ); + Parameters( final int port, final String hostname, final Map< String, String > datasetNameToXml ) + { + this.port = port; + this.hostname = hostname; + this.datasetNameToXml = datasetNameToXml; + } + + public int getPort() + { + return port; + } - CommandLineParser parser = new BasicParser(); - CommandLine cmd = parser.parse( options, args ); + public String getHostname() + { + return hostname; + } - // Getting port number option - String portString = cmd.getOptionValue( "p", "8080" ); - port = Integer.parseInt( portString ); + /** + * Get datasets. + * + * @return datasets as a map from dataset name to dataset xml path. + */ + public Map< String, String > getDatasets() + { + return datasetNameToXml; + } + } - // Getting server name option - String serverString = cmd.getOptionValue( "s", "localhost" ); - serverName = serverString; + @SuppressWarnings( "static-access" ) + static private Parameters processOptions( final String[] args, final Parameters defaultParameters ) throws IOException + { + // create Options object + final Options options = new Options(); - String datasetFile = cmd.getOptionValue( "d" ); - if ( cmd.hasOption( "d" ) ) + final String cmdLineSyntax = "BigDataServer [OPTIONS] [NAME XML]..."; + + final String description = + "Serves one or more XML/HDF5 datasets for remote access over HTTP.\n" + + "Provide (NAME XML) pairs on the command line or in a dataset file, where NAME is the name under which the dataset should be made accessible and XML is the path to the XML file of the dataset."; + + options.addOption( OptionBuilder + .withDescription( "Hostname of the server.\n(default: " + defaultParameters.getHostname() + ")" ) + .hasArg() + .withArgName( "HOSTNAME" ) + .create( "s" ) ); + + options.addOption( OptionBuilder + .withDescription( "Listening port.\n(default: " + defaultParameters.getPort() + ")" ) + .hasArg() + .withArgName( "PORT" ) + .create( "p" ) ); + + // -d or multiple {name name.xml} pairs + options.addOption( OptionBuilder + .withDescription( "Dataset file: A plain text file specifying one dataset per line. Each line is formatted as \"NAME <TAB> XML\"." ) + .hasArg() + .withArgName( "FILE" ) + .create( "d" ) ); + + try { - // process the file given with "-d" + final CommandLineParser parser = new BasicParser(); + final CommandLine cmd = parser.parse( options, args ); - // check the file presence - Path path = Paths.get( datasetFile ); + // Getting port number option + final String portString = cmd.getOptionValue( "p", Integer.toString( defaultParameters.getPort() ) ); + final int port = Integer.parseInt( portString ); - if ( Files.notExists( path ) ) - { - LOG.warn( "Dataset list file does not exist. Cannot start BigDataServer." ); - return false; - } - else + // Getting server name option + final String serverString = cmd.getOptionValue( "s", defaultParameters.getHostname() ); + final String serverName = serverString; + + final HashMap< String, String > datasets = new HashMap< String, String >( defaultParameters.getDatasets() ); + + if ( cmd.hasOption( "d" ) ) { + // process the file given with "-d" + final String datasetFile = cmd.getOptionValue( "d" ); + + // check the file presence + final Path path = Paths.get( datasetFile ); + + if ( Files.notExists( path ) ) + throw new IllegalArgumentException( "Dataset list file does not exist." ); + // Process dataset list file - List< String > lines = Files.readAllLines( path, StandardCharsets.UTF_8 ); + final List< String > lines = Files.readAllLines( path, StandardCharsets.UTF_8 ); - for ( String str : lines ) + for ( final String str : lines ) { - String[] tokens = str.split( "\t" ); - if ( StringUtils.isNotEmpty( tokens[ 0 ].trim() ) && StringUtils.isNotEmpty( tokens[ 1 ].trim() ) ) + final String[] tokens = str.split( "\t" ); + if ( tokens.length == 2 && StringUtils.isNotEmpty( tokens[ 0 ].trim() ) && StringUtils.isNotEmpty( tokens[ 1 ].trim() ) ) { - dataSet.put( tokens[ 0 ].trim(), tokens[ 1 ].trim() ); - LOG.info( "Dataset added: {" + tokens[ 0 ].trim() + ", " + tokens[ 1 ].trim() + "}" ); - - if ( Files.notExists( Paths.get( tokens[ 1 ].trim() ) ) ) - { - LOG.warn( "Dataset file does not exist: \"" + tokens[ 1 ].trim() + "\". Cannot start BigDataServer." ); - return false; - } + final String name = tokens[ 0 ].trim(); + final String xmlpath = tokens[ 1 ].trim(); + tryAddDataset( datasets, name, xmlpath ); } else { - LOG.warn( "Invalid dataset entry (will be skipped): {" + str + "}" ); + LOG.warn( "Invalid dataset file line (will be skipped): {" + str + "}" ); } } } - } - else - { - String keyHolder = null; - // process {name, name.xml} pairs - for ( Object s : cmd.getArgList() ) - { - if ( keyHolder != null ) - { - dataSet.put( keyHolder, s.toString() ); - LOG.info( "Dataset added: {" + keyHolder + ", " + s.toString() + "}" ); - keyHolder = null; - } - else - { - keyHolder = s.toString(); - } - } + // process additional {name, name.xml} pairs given on the + // command-line + final String[] leftoverArgs = cmd.getArgs(); + if ( leftoverArgs.length % 2 != 0 ) + throw new IllegalArgumentException( "Dataset list has an error while processing." ); - if ( keyHolder != null ) + for ( int i = 0; i < leftoverArgs.length; i += 2 ) { - LOG.warn( "Dataset list has an error while processing. Cannot start BigDataServer." ); - return false; + final String name = leftoverArgs[ i ]; + final String xmlpath = leftoverArgs[ i + 1 ]; + tryAddDataset( datasets, name, xmlpath ); } + + if ( datasets.isEmpty() ) + throw new IllegalArgumentException( "Dataset list is empty." ); + + return new Parameters( port, serverName, datasets ); + } + catch ( final ParseException | IllegalArgumentException e ) + { + LOG.warn( e.getMessage() ); + final HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp( cmdLineSyntax, description, options, null ); } + return null; + } - return true; + private static void tryAddDataset( final HashMap< String, String > datasetNameToXML, final String name, final String xmlpath ) throws IllegalArgumentException + { + if ( datasetNameToXML.containsKey( name ) ) + throw new IllegalArgumentException( "Duplicate dataset name: \"" + name + "\"" ); + if ( Files.notExists( Paths.get( xmlpath ) ) ) + throw new IllegalArgumentException( "Dataset file does not exist: \"" + xmlpath + "\"" ); + datasetNameToXML.put( name, xmlpath ); + LOG.info( "Dataset added: {" + name + ", " + xmlpath + "}" ); } - static private ContextHandlerCollection createHandlers( final String baseURL, final HashMap< String, String > dataSet ) throws SpimDataException + private static ContextHandlerCollection createHandlers( final String baseURL, final Map< String, String > dataSet ) throws SpimDataException { final ContextHandlerCollection handlers = new ContextHandlerCollection(); - for ( final String key : dataSet.keySet() ) + for ( final Entry< String, String > entry : dataSet.entrySet() ) { - final String context = "/" + key; - final CellHandler ctx = new CellHandler( baseURL + context + "/", dataSet.get( key ) ); + final String name = entry.getKey(); + final String xmlpath = entry.getValue(); + final String context = "/" + name; + final CellHandler ctx = new CellHandler( baseURL + context + "/", xmlpath ); ctx.setContextPath( context ); handlers.addHandler( ctx ); } -- GitLab