Mercurial > hg > release > thermostat-2.0
changeset 2561:879eb9424133
Add splash screen functionality to thermostat local and gui commands
A splash screen can now be displayed during startup of the thermostat local and gui commands by supplying the "--show-splash" option
Reviewed-by: aazores, neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-November/021550.html
line wrap: on
line diff
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java Tue Jan 17 10:49:00 2017 -0500 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java Wed Jan 18 11:19:51 2017 -0500 @@ -52,10 +52,14 @@ import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Collections; import java.util.List; +import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BorderFactory; @@ -468,9 +472,18 @@ setTitle(title); } + private void deleteSplashScreenStamp() { + try { + Files.deleteIfExists(commonPaths.getUserSplashScreenStampFile().toPath()); + } catch (IOException | SecurityException | NullPointerException e) { + logger.log(Level.WARNING, "Unable to delete splashscreen.stamp", e); + } + } + @Override public void showMainWindow() { try { + deleteSplashScreenStamp(); new EdtHelper().callAndWait(new Runnable() { @Override
--- a/config/src/main/java/com/redhat/thermostat/shared/config/CommonPaths.java Tue Jan 17 10:49:00 2017 -0500 +++ b/config/src/main/java/com/redhat/thermostat/shared/config/CommonPaths.java Wed Jan 18 11:19:51 2017 -0500 @@ -84,6 +84,9 @@ /** A file indicating that Thermostat commands are ready to be run **/ public File getUserSetupCompleteStampFile() throws InvalidConfigurationException; + /** A file indicating the current use of a splash screen **/ + public File getUserSplashScreenStampFile() throws InvalidConfigurationException; + /** A location that contains data that is persisted */ public File getUserPersistentDataDirectory() throws InvalidConfigurationException;
--- a/config/src/main/java/com/redhat/thermostat/shared/config/internal/CommonPathsImpl.java Tue Jan 17 10:49:00 2017 -0500 +++ b/config/src/main/java/com/redhat/thermostat/shared/config/internal/CommonPathsImpl.java Wed Jan 18 11:19:51 2017 -0500 @@ -315,6 +315,11 @@ return setupCompleteStamp; } + @Override + public File getUserSplashScreenStampFile() throws InvalidConfigurationException { + return new File(getUserPersistentDataDirectory(), "splashscreen.stamp"); + } + // TODO add logging files here (see LoggingUtils) // TODO add ssl.properties file here (see SSLConfiguration)
--- a/config/src/test/java/com/redhat/thermostat/shared/config/DirectoryStructureCreatorTest.java Tue Jan 17 10:49:00 2017 -0500 +++ b/config/src/test/java/com/redhat/thermostat/shared/config/DirectoryStructureCreatorTest.java Wed Jan 18 11:19:51 2017 -0500 @@ -250,6 +250,11 @@ throws InvalidConfigurationException { return null; // Only directories need to be created } + + @Override + public File getUserSplashScreenStampFile() throws InvalidConfigurationException{ + return null; // Only directories need to be created + } @Override public File getUserIPCConfigurationFile() throws InvalidConfigurationException {
--- a/distribution/config/commands/gui.properties Tue Jan 17 10:49:00 2017 -0500 +++ b/distribution/config/commands/gui.properties Wed Jan 18 11:19:51 2017 -0500 @@ -14,8 +14,13 @@ description = Launch the GUI client. The GUI client provides a graphical interface to all the features. -usage = gui [-l <level>] +usage = gui [-l <level>] [--show-splash] + +options = AUTO_LOG_OPTION, splashscreen -options = AUTO_LOG_OPTION +splashscreen.long = show-splash +splashscreen.required = false +splashscreen.hasarg = false +splashscreen.description = display a splash screen while launching Thermostat gui environments = cli
--- a/distribution/packaging/shared/desktop/thermostat.desktop Tue Jan 17 10:49:00 2017 -0500 +++ b/distribution/packaging/shared/desktop/thermostat.desktop Wed Jan 18 11:19:51 2017 -0500 @@ -3,5 +3,5 @@ Type=Application Name=${thermostat.desktop.app.name} Comment=A monitoring and serviceability tool for OpenJDK -Exec=${thermostat.home}/bin/thermostat local +Exec=${thermostat.home}/bin/thermostat local --show-splash Icon=${pkg_name}
--- a/distribution/scripts/thermostat Tue Jan 17 10:49:00 2017 -0500 +++ b/distribution/scripts/thermostat Wed Jan 18 11:19:51 2017 -0500 @@ -71,6 +71,7 @@ RUN_IN_BG=0 PID_FILE="" +PATH_TO_SPLASHIMAGE="$THERMOSTAT_HOME/../../packaging/shared/icons/splash-image.png" i=0 j=0 @@ -90,6 +91,13 @@ ;; *) ARGS[$j]="$1" + if [ $1 = "--show-splash" ] && [ $j > 0 ]; then + if [ ${ARGS[$j-1]} = "local" ]; then + JAVA_ARGS+=(-splash:$PATH_TO_SPLASHIMAGE) + elif [ ${ARGS[$j-1]} = "gui" ] && [ ! -f $USER_THERMOSTAT_HOME/data/splashscreen.stamp ]; then + JAVA_ARGS+=(-splash:$PATH_TO_SPLASHIMAGE) + fi + fi j=$((j+1)) shift ;;
--- a/local/command/src/main/java/com/redhat/thermostat/local/command/internal/LocalCommand.java Tue Jan 17 10:49:00 2017 -0500 +++ b/local/command/src/main/java/com/redhat/thermostat/local/command/internal/LocalCommand.java Wed Jan 18 11:19:51 2017 -0500 @@ -40,19 +40,36 @@ import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; import com.redhat.thermostat.common.cli.DependencyServices; +import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.launcher.Launcher; +import java.awt.SplashScreen; +import java.io.File; import java.lang.ProcessBuilder.Redirect; import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; + public class LocalCommand extends AbstractCommand { + private static final Logger logger = LoggingUtils.getLogger(LocalCommand.class); private static final Translate<LocaleResources> t = LocaleResources.createLocalizer(); + private static String SHOW_SPLASH = "--show-splash"; private final DependencyServices dependentServices = new DependencyServices(); private Launcher launcher; private CommonPaths paths; + private boolean splashScreenEnabled; public void run(CommandContext ctx) throws CommandException { this.paths = dependentServices.getRequiredService(CommonPaths.class); @@ -60,6 +77,16 @@ ServiceLauncher serviceLauncher = createServiceLauncher(); serviceLauncher.start(); + if (ctx.getArguments().hasArgument(SHOW_SPLASH)) { + splashScreenEnabled = true; + File stamp = paths.getUserSplashScreenStampFile(); + try { + stamp.createNewFile(); + } catch (IOException | SecurityException e) { + logger.log(Level.WARNING, "Unable to create file splashscreen.stamp", e); + } + } + try { // this blocks runGui(); @@ -83,6 +110,10 @@ throw new CommandException(t.localize(LocaleResources.ERROR_STARTING_GUI)); } + if (isSplashScreenEnabled()) { + closeSplashScreen(); + } + int exitStatus; try { exitStatus = gui.waitFor(); @@ -103,6 +134,34 @@ return pb.start(); } + // package private for testing + boolean isSplashScreenEnabled() { + return splashScreenEnabled; + } + + private void closeSplashScreen() { + try { + WatchService watcher = FileSystems.getDefault().newWatchService(); + Path splashStampPath = paths.getUserSplashScreenStampFile().toPath().getParent(); + splashStampPath.register(watcher, ENTRY_DELETE); + while(paths.getUserSplashScreenStampFile().exists()) { + WatchKey key = watcher.poll(5L, TimeUnit.MINUTES); + for (WatchEvent<?> event : key.pollEvents()) { + if (paths.getUserSplashScreenStampFile().getName().equals(event.context().toString())) { + try { + SplashScreen.getSplashScreen().close(); + } catch (NullPointerException e) { + logger.log(Level.WARNING, "Unable to close SplashScreen!", e); + } + } + } + } + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to complete WatcherService.", e); + SplashScreen.getSplashScreen().close(); + } + } + public void setPaths(CommonPaths paths) { dependentServices.addService(CommonPaths.class, paths); }
--- a/local/command/src/main/resources/com/redhat/thermostat/config/locale/strings.properties Tue Jan 17 10:49:00 2017 -0500 +++ b/local/command/src/main/resources/com/redhat/thermostat/config/locale/strings.properties Wed Jan 18 11:19:51 2017 -0500 @@ -3,4 +3,4 @@ SERVICE_WAIT_INTERRUPTED=Waiting for {0} interrupted ERROR_STARTING_GUI="Error starting thermostat GUI" ERROR_RUNNING_GUI="Error running thermostat GUI" -RUNNING_GUI_INTERRUPTED="Running thermostat GUI interrupted" \ No newline at end of file +RUNNING_GUI_INTERRUPTED="Running thermostat GUI interrupted"
--- a/local/command/src/test/java/com/redhat/thermostat/local/command/internal/LocalCommandTest.java Tue Jan 17 10:49:00 2017 -0500 +++ b/local/command/src/test/java/com/redhat/thermostat/local/command/internal/LocalCommandTest.java Wed Jan 18 11:19:51 2017 -0500 @@ -36,16 +36,20 @@ package com.redhat.thermostat.local.command.internal; +import com.redhat.thermostat.common.cli.Arguments; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; import com.redhat.thermostat.launcher.Launcher; import com.redhat.thermostat.shared.config.CommonPaths; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -56,6 +60,7 @@ import java.io.IOException; public class LocalCommandTest { + private static final String SHOW_SPLASH = "--show-splash"; private LocalCommand cmd; private CommandContext ctxt; private CommonPaths paths; @@ -70,6 +75,59 @@ ctxt = mock(CommandContext.class); } + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test(timeout=1000) + public void testSplashScreenFunctionalityAndClosing() throws CommandException, InterruptedException, IOException { + cmd = createLocalCommandForSplashTests(); + final File stamp = tempFolder.newFile("splashscreen.stamp"); + assertTrue(stamp.exists()); + when(ctxt.getArguments().hasArgument(SHOW_SPLASH)).thenReturn(true); + when(paths.getUserPersistentDataDirectory()).thenReturn(tempFolder.getRoot()); + when(paths.getUserSplashScreenStampFile()).thenReturn(stamp); + Runnable deleteStampFile = new Runnable() { + public void run() { + try { + // sleep so the stamp file is deleted while closeSplashScreen() is watching + Thread.sleep(250L); + stamp.delete(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + new Thread(deleteStampFile).start(); + cmd.run(ctxt); + assertTrue(cmd.isSplashScreenEnabled()); + assertFalse(stamp.exists()); + } + + @Test + public void testNoShowSplashOption() throws CommandException, InterruptedException { + cmd = createLocalCommandForSplashTests(); + cmd.run(ctxt); + assertFalse(cmd.isSplashScreenEnabled()); + } + + private LocalCommand createLocalCommandForSplashTests() throws CommandException, InterruptedException { + cmd = new LocalCommand() { + @Override + ServiceLauncher createServiceLauncher() { + return mock(ServiceLauncher.class); + } + + @Override + Process execProcess(String... command) throws IOException { + return mock(Process.class); + } + }; + when(ctxt.getArguments()).thenReturn(mock(Arguments.class)); + cmd.setPaths(paths); + cmd.setLauncher(launcher); + return cmd; + } + @Test public void testPathsNotSetFailure() throws CommandException { cmd = createLocalCommand(); @@ -113,6 +171,7 @@ final Process mockProcess = mock(Process.class); when(mockProcess.waitFor()).thenReturn(0); + when(ctxt.getArguments()).thenReturn(mock(Arguments.class)); cmd = new LocalCommand() { @Override
--- a/local/distribution/thermostat-plugin.xml Tue Jan 17 10:49:00 2017 -0500 +++ b/local/distribution/thermostat-plugin.xml Wed Jan 18 11:19:51 2017 -0500 @@ -50,6 +50,13 @@ is installed and then start the GUI client as a separate process. On exit, shut them all down. </description> + <options> + <option> + <long>show-splash</long> + <required>false</required> + <description>display a splash screen while launching Thermostat local</description> + </option> + </options> <environments> <environment>cli</environment> </environments>