diff --git a/src/main/java/bdv/server/BigDataServer.java b/src/main/java/bdv/server/BigDataServer.java index df2d33ed598ed918452a4859a28d8820ba2c62da..626d131267a7250819b2baf600d89cfa29412efb 100644 --- a/src/main/java/bdv/server/BigDataServer.java +++ b/src/main/java/bdv/server/BigDataServer.java @@ -6,11 +6,11 @@ import org.apache.commons.cli.*; import org.apache.commons.lang.StringUtils; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.ConnectorStatistics; +import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.HandlerCollection; -import org.eclipse.jetty.server.handler.RequestLogHandler; import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -61,7 +61,8 @@ public class BigDataServer { hostname = "localhost"; } - return new Parameters( port, hostname, new HashMap< String, String >() ); + final boolean enableManagerContext = false; + return new Parameters( port, hostname, new HashMap< String, String >(), enableManagerContext ); } public static void main( final String[] args ) throws Exception @@ -72,6 +73,8 @@ public class BigDataServer if ( params == null ) return; + final String thumbnailsDirectoryName = getThumbnailDirectoryPath( params ); + // Threadpool for multiple connections final Server server = new Server( new QueuedThreadPool( 1000, 10 ) ); @@ -81,29 +84,32 @@ public class BigDataServer connector.setPort( params.getPort() ); LOG.info( "Set connectors: " + connector ); server.setConnectors( new Connector[] { connector } ); - - // Add Statistics bean to the connector - final ConnectorStatistics connectorStats = new ConnectorStatistics(); - connector.addBean( connectorStats ); - final String baseURL = "http://" + server.getURI().getHost() + ":" + params.getPort(); - LOG.info( "Server Base URL: " + baseURL ); // Handler initialization - final StatisticsHandler statHandler = new StatisticsHandler(); - final HandlerCollection handlers = new HandlerCollection(); - final ContextHandlerCollection datasetHandlers = createHandlers( baseURL, params.getDatasets() ); + final ContextHandlerCollection datasetHandlers = createHandlers( baseURL, params.getDatasets(), thumbnailsDirectoryName ); handlers.addHandler( datasetHandlers ); - handlers.addHandler( new ManagerHandler( baseURL, server, connectorStats, statHandler, datasetHandlers ) ); - handlers.addHandler( new RequestLogHandler() ); handlers.addHandler( new JsonDatasetListHandler( server, datasetHandlers ) ); - statHandler.setHandler( handlers ); + Handler handler = handlers; + if ( params.enableManagerContext() ) + { + // Add Statistics bean to the connector + final ConnectorStatistics connectorStats = new ConnectorStatistics(); + connector.addBean( connectorStats ); + + // create StatisticsHandler wrapper and ManagerHandler + final StatisticsHandler statHandler = new StatisticsHandler(); + handlers.addHandler( new ManagerHandler( baseURL, server, connectorStats, statHandler, datasetHandlers, thumbnailsDirectoryName ) ); + statHandler.setHandler( handlers ); + handler = statHandler; + } - LOG.info( "Set handler: " + statHandler ); - server.setHandler( statHandler ); + LOG.info( "Set handler: " + handler ); + server.setHandler( handler ); + LOG.info( "Server Base URL: " + baseURL ); LOG.info( "BigDataServer starting" ); server.start(); server.join(); @@ -123,11 +129,14 @@ public class BigDataServer */ private final Map< String, String > datasetNameToXml; - Parameters( final int port, final String hostname, final Map< String, String > datasetNameToXml ) + private final boolean enableManagerContext; + + Parameters( final int port, final String hostname, final Map< String, String > datasetNameToXml, final boolean enableManagerContext ) { this.port = port; this.hostname = hostname; this.datasetNameToXml = datasetNameToXml; + this.enableManagerContext = enableManagerContext; } public int getPort() @@ -140,6 +149,11 @@ public class BigDataServer return hostname; } + public String getThumbnailDirectory() + { + return null; + } + /** * Get datasets. * @@ -149,6 +163,11 @@ public class BigDataServer { return datasetNameToXml; } + + public boolean enableManagerContext() + { + return enableManagerContext; + } } @SuppressWarnings( "static-access" ) @@ -182,6 +201,10 @@ public class BigDataServer .withArgName( "FILE" ) .create( "d" ) ); + options.addOption( OptionBuilder + .withDescription( "enable statistics and manager context. EXPERIMENTAL!" ) + .create( "m" ) ); + try { final CommandLineParser parser = new BasicParser(); @@ -197,6 +220,10 @@ public class BigDataServer final HashMap< String, String > datasets = new HashMap< String, String >( defaultParameters.getDatasets() ); + boolean enableManagerContext = false; + if ( cmd.hasOption( "m" ) ) + enableManagerContext = true; + if ( cmd.hasOption( "d" ) ) { // process the file given with "-d" @@ -243,7 +270,7 @@ public class BigDataServer if ( datasets.isEmpty() ) throw new IllegalArgumentException( "Dataset list is empty." ); - return new Parameters( port, serverName, datasets ); + return new Parameters( port, serverName, datasets, enableManagerContext ); } catch ( final ParseException | IllegalArgumentException e ) { @@ -267,7 +294,39 @@ public class BigDataServer LOG.info( "Dataset added: {" + name + ", " + xmlpath + "}" ); } - private static ContextHandlerCollection createHandlers( final String baseURL, final Map< String, String > dataSet ) throws SpimDataException, IOException + private static String getThumbnailDirectoryPath( final Parameters params ) throws IOException + { + final String thumbnailDirectoryName = params.getThumbnailDirectory(); + if ( thumbnailDirectoryName != null ) + { + Path thumbnails = Paths.get( thumbnailDirectoryName ); + if ( ! Files.exists( thumbnails ) ) + { + try + { + thumbnails = Files.createDirectories( thumbnails ); + return thumbnails.toFile().getAbsolutePath(); + } + catch ( final IOException e ) + { + LOG.warn( e.getMessage() ); + LOG.warn( "Could not create thumbnails directory \"" + thumbnailDirectoryName + "\".\n Trying to create temporary directory." ); + } + } + else + { + if ( ! Files.isDirectory( thumbnails ) ) + LOG.warn( "Thumbnails directory \"" + thumbnailDirectoryName + "\" is not a directory.\n Trying to create temporary directory." ); + else + return thumbnails.toFile().getAbsolutePath(); + } + } + final Path thumbnails = Files.createTempDirectory( "thumbnails" ); + thumbnails.toFile().deleteOnExit(); + return thumbnails.toFile().getAbsolutePath(); + } + + private static ContextHandlerCollection createHandlers( final String baseURL, final Map< String, String > dataSet, final String thumbnailsDirectoryName ) throws SpimDataException, IOException { final ContextHandlerCollection handlers = new ContextHandlerCollection(); @@ -276,7 +335,7 @@ public class BigDataServer final String name = entry.getKey(); final String xmlpath = entry.getValue(); final String context = "/" + name; - final CellHandler ctx = new CellHandler( baseURL + context + "/", xmlpath ); + final CellHandler ctx = new CellHandler( baseURL + context + "/", xmlpath, name, thumbnailsDirectoryName ); ctx.setContextPath( context ); handlers.addHandler( ctx ); } diff --git a/src/main/java/bdv/server/CellHandler.java b/src/main/java/bdv/server/CellHandler.java index c33c8109d36c3c06da36f99f6b1c59be9021ea4f..6125bc9123ee6cbb46a42fbec8e965d3d1641d7c 100644 --- a/src/main/java/bdv/server/CellHandler.java +++ b/src/main/java/bdv/server/CellHandler.java @@ -78,7 +78,12 @@ public class CellHandler extends ContextHandler */ private final String settingsXmlString; - public CellHandler( final String baseUrl, final String xmlFilename ) throws SpimDataException, IOException + /** + * Full path to thumbnail png. + */ + private final String thumbnailFilename; + + public CellHandler( final String baseUrl, final String xmlFilename, final String datasetName, final String thumbnailsDirectory ) throws SpimDataException, IOException { final XmlIoSpimDataMinimal io = new XmlIoSpimDataMinimal(); final SpimDataMinimal spimData = io.load( xmlFilename ); @@ -97,7 +102,7 @@ public class CellHandler extends ContextHandler datasetXmlString = buildRemoteDatasetXML( io, spimData, baseUrl ); metadataJson = buildMetadataJsonString( imgLoader, seq ); settingsXmlString = buildSettingsXML( baseFilename ); - createThumbnail( spimData, baseFilename ); + thumbnailFilename = createThumbnail( spimData, baseFilename, datasetName, thumbnailsDirectory ); } @Override @@ -170,9 +175,7 @@ public class CellHandler extends ContextHandler private void provideThumbnail( final Request baseRequest, final HttpServletResponse response ) throws IOException { - // Just check it once - final Path path = Paths.get( baseFilename + ".png" ); - + final Path path = Paths.get( thumbnailFilename ); if ( Files.exists( path ) ) { final byte[] imageData = Files.readAllBytes(path); @@ -270,19 +273,25 @@ public class CellHandler extends ContextHandler /** * Create PNG thumbnail file named "{@code <baseFilename>.png}". */ - private static void createThumbnail( final SpimDataMinimal spimData, final String baseFilename ) + private static String createThumbnail( final SpimDataMinimal spimData, final String baseFilename, final String datasetName, final String thumbnailsDirectory ) { - final File thumbnailFile = new File( baseFilename + ".png" ); - final BufferedImage bi = ThumbnailGenerator.makeThumbnail( spimData, baseFilename, Constants.THUMBNAIL_WIDTH, Constants.THUMBNAIL_HEIGHT ); - try - { - ImageIO.write( bi, "png", thumbnailFile ); - } - catch ( final IOException e ) + final String thumbnailFileName = thumbnailsDirectory + "/" + datasetName + ".png"; + final File thumbnailFile = new File( thumbnailFileName ); + if ( !thumbnailFile.isFile() ) // do not recreate thumbnail if it + // already exists { - LOG.warn( "Could not create thumbnail png for dataset \"" + baseFilename + "\"" ); - LOG.warn( e.getMessage() ); + final BufferedImage bi = ThumbnailGenerator.makeThumbnail( spimData, baseFilename, Constants.THUMBNAIL_WIDTH, Constants.THUMBNAIL_HEIGHT ); + try + { + ImageIO.write( bi, "png", thumbnailFile ); + } + catch ( final IOException e ) + { + LOG.warn( "Could not create thumbnail png for dataset \"" + baseFilename + "\"" ); + LOG.warn( e.getMessage() ); + } } + return thumbnailFileName; } /** diff --git a/src/main/java/bdv/server/ManagerHandler.java b/src/main/java/bdv/server/ManagerHandler.java index ef23e507a0e1545577fbef48fb6bcefef4964747..f5e959aa9bd2dbf390505dcb105772235886d8f4 100644 --- a/src/main/java/bdv/server/ManagerHandler.java +++ b/src/main/java/bdv/server/ManagerHandler.java @@ -23,6 +23,10 @@ import java.io.PrintWriter; import java.net.URISyntaxException; import java.text.DecimalFormat; +/** + * @author HongKee Moon <moon@mpi-cbg.de> + * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> + */ public class ManagerHandler extends ContextHandler { private static final org.eclipse.jetty.util.log.Logger LOG = Log.getLogger( ManagerHandler.class ); @@ -43,13 +47,23 @@ public class ManagerHandler extends ContextHandler private long sizeDataSets = 0; - public ManagerHandler( final String baseURL, final Server server, final ConnectorStatistics connectorStats, final StatisticsHandler statHandler, final ContextHandlerCollection handlers ) throws IOException, URISyntaxException + private final String thumbnailsDirectoryName; + + public ManagerHandler( + final String baseURL, + final Server server, + final ConnectorStatistics connectorStats, + final StatisticsHandler statHandler, + final ContextHandlerCollection handlers, + final String thumbnailsDirectoryName ) + throws IOException, URISyntaxException { this.baseURL = baseURL; this.server = server; this.handlers = handlers; this.statHandler = statHandler; this.connectorStats = connectorStats; + this.thumbnailsDirectoryName = thumbnailsDirectoryName; setContextPath( "/" + Constants.MANAGER_CONTEXT_NAME ); } @@ -77,7 +91,6 @@ public class ManagerHandler extends ContextHandler { return; } - } public String getByteSizeString( final long size ) @@ -103,11 +116,8 @@ public class ManagerHandler extends ContextHandler private String getHtml() { - // manager.st should be under {WorkingFolder}/templates/ - final StringTemplateGroup templates = - new StringTemplateGroup( "manager", "templates" ); - - final StringTemplate t = templates.getInstanceOf( "manager" ); + final StringTemplateGroup templates = new StringTemplateGroup( "manager" ); + final StringTemplate t = templates.getInstanceOf( "templates/manager" ); t.setAttribute( "bytesSent", getByteSizeString( statHandler.getResponsesBytesTotal() ) ); t.setAttribute( "msgPerSec", connectorStats.getMessagesOutPerSecond() ); @@ -136,16 +146,12 @@ public class ManagerHandler extends ContextHandler final StringBuilder sb = new StringBuilder(); for ( final Handler handler : server.getChildHandlersByClass( CellHandler.class ) ) { - CellHandler contextHandler = null; - if ( handler instanceof CellHandler ) - { - sb.append( "<tr>\n<th>" ); - contextHandler = ( CellHandler ) handler; - sb.append( contextHandler.getContextPath() + "</th>\n<td>" ); - sb.append( contextHandler.getXmlFile() + "</td>\n</tr>\n" ); - noDataSets++; - sizeDataSets += new File( contextHandler.getXmlFile().replace( ".xml", ".h5" ) ).length(); - } + sb.append( "<tr>\n<th>" ); + final CellHandler contextHandler = ( CellHandler ) handler; + sb.append( contextHandler.getContextPath() + "</th>\n<td>" ); + sb.append( contextHandler.getXmlFile() + "</td>\n</tr>\n" ); + noDataSets++; + sizeDataSets += new File( contextHandler.getXmlFile().replace( ".xml", ".h5" ) ).length(); } contexts = sb.toString(); } @@ -154,25 +160,45 @@ public class ManagerHandler extends ContextHandler private void deploy( final String datasetName, final String fileLocation, final Request baseRequest, final HttpServletResponse response ) throws IOException { LOG.info( "Add new context: " + datasetName ); - CellHandler ctx = null; - try + final String context = "/" + datasetName; + + boolean alreadyExists = false; + for ( final Handler handler : server.getChildHandlersByClass( CellHandler.class ) ) { - ctx = new CellHandler( baseURL + datasetName + "/", fileLocation ); + final CellHandler contextHandler = ( CellHandler ) handler; + if ( context.equals( contextHandler.getContextPath() ) ) + { + LOG.info( "Context " + datasetName + " already exists."); + alreadyExists = true; + break; + } } - catch ( final SpimDataException e ) + + if ( ! alreadyExists ) { - LOG.warn( "Failed to create a CellHandler", e ); - e.printStackTrace(); + CellHandler ctx = null; + try + { + ctx = new CellHandler( baseURL + context + "/", fileLocation, datasetName, thumbnailsDirectoryName ); + } + catch ( final SpimDataException e ) + { + LOG.warn( "Failed to create a CellHandler", e ); + e.printStackTrace(); + } + ctx.setContextPath( context ); + handlers.addHandler( ctx ); } - ctx.setContextPath( "/" + datasetName ); - handlers.addHandler( ctx ); response.setContentType( "text/html" ); response.setStatus( HttpServletResponse.SC_OK ); baseRequest.setHandled( true ); final PrintWriter ow = response.getWriter(); - ow.write( datasetName + " registered." ); + if ( alreadyExists ) + ow.write( datasetName + " already exists. Not registered." ); + else + ow.write( datasetName + " registered." ); ow.close(); } @@ -181,27 +207,24 @@ public class ManagerHandler extends ContextHandler LOG.info( "Remove the context: " + datasetName ); boolean ret = false; + final String context = "/" + datasetName; for ( final Handler handler : server.getChildHandlersByClass( CellHandler.class ) ) { - CellHandler contextHandler = null; - if ( handler instanceof CellHandler ) + final CellHandler contextHandler = ( CellHandler ) handler; + if ( context.equals( contextHandler.getContextPath() ) ) { - contextHandler = ( CellHandler ) handler; - if ( datasetName.equals( contextHandler.getContextPath() ) ) + try + { + contextHandler.stop(); + } + catch ( final Exception e ) { - try - { - contextHandler.stop(); - } - catch ( final Exception e ) - { - e.printStackTrace(); - } - contextHandler.destroy(); - handlers.removeHandler( contextHandler ); - ret = true; - break; + e.printStackTrace(); } + contextHandler.destroy(); + handlers.removeHandler( contextHandler ); + ret = true; + break; } }