From 543b1ef60576cc69037caa8fa2ead70fb363e574 Mon Sep 17 00:00:00 2001 From: Emmanuel Bigeon Date: Sat, 3 Dec 2016 20:18:20 -0500 Subject: [PATCH] Fixed thread surviving the application lifespan. +tests in swt. Signed-off-by: Emmanuel Bigeon --- gclc-swt/pom.xml | 2 +- .../gclc/swt/AOutputForwardRunnable.java | 133 ---------------- .../fr/bigeon/gclc/swt/SWTConsoleView.java | 10 +- .../bigeon/gclc/swt/SWTConsoleShellTest.java | 12 +- .../bigeon/gclc/swt/SWTConsoleViewTest.java | 145 ++++++++++++++++++ .../bigeon/gclc/manager/ConsoleManager.java | 3 +- .../gclc/manager/PipedConsoleManager.java | 3 +- .../gclc/manager/SystemConsoleManager.java | 2 + .../tools/AOutputForwardRunnableTest.java | 13 +- 9 files changed, 179 insertions(+), 144 deletions(-) delete mode 100644 gclc-swt/src/main/java/fr/bigeon/gclc/swt/AOutputForwardRunnable.java create mode 100644 gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleViewTest.java diff --git a/gclc-swt/pom.xml b/gclc-swt/pom.xml index 8f72257..30864a1 100644 --- a/gclc-swt/pom.xml +++ b/gclc-swt/pom.xml @@ -51,7 +51,7 @@ fr.bigeon gclc - 1.3.1 + 1.3.2-SNAPSHOT fr.bigeon diff --git a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/AOutputForwardRunnable.java b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/AOutputForwardRunnable.java deleted file mode 100644 index a3fbdf8..0000000 --- a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/AOutputForwardRunnable.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright E. Bigeon (2015) - * - * emmanuel@bigeon.fr - * - * This software is a computer program whose purpose is to - * provide a swt window for console applications. - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ -/** - * gclc:fr.bigeon.gclc.tools.AOutputForwardRunnable.java - * Created on: Dec 1, 2016 - */ -package fr.bigeon.gclc.swt; - -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import fr.bigeon.gclc.manager.PipedConsoleManager; - -/** An incomplete implematation used to forward messages from a piped console. - *

- * This forwarding can be interrupted without closing the piped manager. - * - * @author Emmanuel Bigeon - * @deprecated since version 1.3.2 of gclc, this class has been integrated in - * the main content. */ -@Deprecated -public abstract class AOutputForwardRunnable implements Runnable { - - /** The class logger */ - private static final Logger LOGGER = Logger - .getLogger(AOutputForwardRunnable.class.getName()); - /** The default timeout (one tenth of second). */ - private static final long DEFAULT_TIMEOUT = 100; - /** The manager. */ - private final PipedConsoleManager manager; - /** The timeout */ - private final long timeout; - - /** Create a forward runnable with the given timeout. - *

- * Short timeout will be very responsive to the application actual messages, - * but may use computation time if the application is not verbose. Long - * timeout will save computation time, but will read batches of messages at - * once if the application is verbose. The right length for the timeout is - * likely to depend on the application and the use of it. - *

- * If you do not know what timeout length to use, please use the - * {@link #AOutputForwardRunnable(PipedConsoleManager)} constructor. - * - * @param manager the manager - * @param timeout the timeout between message requests. */ - public AOutputForwardRunnable(PipedConsoleManager manager, long timeout) { - super(); - this.manager = manager; - this.timeout = timeout; - } - - /** Create a forwarding runnable. - * - * @param manager the manager */ - public AOutputForwardRunnable(PipedConsoleManager manager) { - super(); - this.manager = manager; - timeout = DEFAULT_TIMEOUT; - } - - @Override - public void run() { - try { - while (isRunning()) { - while (isRunning() && !manager.available()) { - waitASec(); - } - if (!isRunning()) { - return; - } - String m = manager.readNextLine(); - forwardLine(m); - } - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Unexpected problem in manager", //$NON-NLS-1$ - e); - } - } - - /** @param m the line to forward */ - protected abstract void forwardLine(String m); - - /** @return if the thread should keep running */ - protected abstract boolean isRunning(); - - /** a method to wait some time */ - protected void waitASec() { - try { - synchronized (this) { - wait(timeout); - } - } catch (InterruptedException e) { - LOGGER.log(Level.SEVERE, "Interrupted wait", //$NON-NLS-1$ - e); - return; - } - } - -} diff --git a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleView.java b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleView.java index b5749c3..21fdd3b 100644 --- a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleView.java +++ b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleView.java @@ -53,6 +53,7 @@ import org.eclipse.swt.widgets.Text; import fr.bigeon.gclc.ConsoleApplication; import fr.bigeon.gclc.manager.PipedConsoleManager; +import fr.bigeon.gclc.tools.AOutputForwardRunnable; /** A SWT component to connect to gclc {@link ConsoleApplication} *

@@ -62,14 +63,13 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO { /** The local implementation of the forwarding runnable * * @author Emmanuel Bigeon */ - @SuppressWarnings("deprecation") - private final class ToSWTConsoleForwarRunnable + private final class ToSWTConsoleForwardRunnable extends AOutputForwardRunnable { /** The running status */ private boolean running = true; /** @param manager the manager */ - public ToSWTConsoleForwarRunnable(PipedConsoleManager manager) { + public ToSWTConsoleForwardRunnable(PipedConsoleManager manager) { super(manager); } @@ -99,7 +99,7 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO { /** The actual manager */ private PipedConsoleManager manager; /** The forwarding runnable */ - private ToSWTConsoleForwarRunnable forward; + private ToSWTConsoleForwardRunnable forward; /** Create the composite. * @@ -135,7 +135,7 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO { if (forward != null) { forward.setRunning(false); } - forward = new ToSWTConsoleForwarRunnable(manager); + forward = new ToSWTConsoleForwardRunnable(manager); Thread th = new Thread(forward, "gclcToSWT"); //$NON-NLS-1$ th.start(); } diff --git a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java index 5bcc1cf..d690510 100644 --- a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java +++ b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java @@ -39,7 +39,9 @@ package fr.bigeon.gclc.swt; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.IOException; @@ -60,7 +62,7 @@ import fr.bigeon.gclc.exception.InvalidCommandName; public class SWTConsoleShellTest { protected static final long TWO_SECONDS = 2000; - private static final Display DISPLAY = new Display(); + private static final Display DISPLAY = Display.getDefault(); @Test public void testConsoleClose() { @@ -171,6 +173,7 @@ public class SWTConsoleShellTest { shell.dispose(); } }); + } }); applThread.start(); @@ -180,6 +183,13 @@ public class SWTConsoleShellTest { DISPLAY.sleep(); } } + swtConsole.setPrompt(">"); + try { + swtConsole.prompt(); + fail("Prompting when closed should fail!"); + } catch (IOException e) { + assertNotNull(e); + } // DISPLAY.dispose(); assertTrue(appl.getManager().isClosed()); Thread.sleep(TWO_SECONDS); diff --git a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleViewTest.java b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleViewTest.java new file mode 100644 index 0000000..c9384d8 --- /dev/null +++ b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleViewTest.java @@ -0,0 +1,145 @@ +/* + * Copyright E. Bigeon (2015) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a swt window for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc-swt:fr.bigeon.gclc.swt.SWTConsoleShellTest.java + * Created on: Jun 8, 2016 + */ +package fr.bigeon.gclc.swt; + +import java.io.IOException; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.junit.Test; + +import fr.bigeon.gclc.ConsoleApplication; +import fr.bigeon.gclc.command.Command; +import fr.bigeon.gclc.command.ExitCommand; +import fr.bigeon.gclc.exception.InvalidCommandName; +import fr.bigeon.gclc.manager.PipedConsoleManager; + +/**

+ * TODO + * + * @author Emmanuel Bigeon */ +@SuppressWarnings({"javadoc", "static-method", "nls", "deprecation"}) +public class SWTConsoleViewTest { + + protected static final long TWO_SECONDS = 2000; + private static final Display DISPLAY = Display.getDefault(); + + @Test + public void test() { + final Shell shell = new Shell(DISPLAY); + final SWTConsoleView swtConsole = new SWTConsoleView(shell, SWT.NONE); + try (PipedConsoleManager manager = new PipedConsoleManager()) { + swtConsole.setManager(manager); + final ConsoleApplication appl = new ConsoleApplication(manager, + "Hello", "See you"); + appl.add(new ExitCommand("exit", appl)); + appl.add(new Command("long") { + + @Override + public String tip() { + return "a long running command"; + } + + @Override + public void execute(String... args) { + try { + Thread.sleep(TWO_SECONDS); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); +// shell.pack(); + shell.open(); + Thread applThread = new Thread(new Runnable() { + + @Override + public void run() { + appl.start(); + } + }); + Thread testThread = new Thread(new Runnable() { + + @Override + public void run() { + try { + Thread.sleep(TWO_SECONDS); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + swtConsole.setText("test"); //$NON-NLS-1$ + swtConsole.validateCommand(); + } + }); + try { + Thread.sleep(TWO_SECONDS); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + shell.dispose(); + } + }); + } + }); + applThread.start(); + testThread.start(); + while (!shell.isDisposed()) { + if (!DISPLAY.readAndDispatch()) { + DISPLAY.sleep(); + } + } + } catch (InvalidCommandName e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } +} diff --git a/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java index 0fcf97b..9a0d060 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java @@ -48,7 +48,7 @@ import java.io.InterruptedIOException; * a message, in that case, the usual behavior is to prompt normally. * * @author Emmanuel BIGEON */ -public interface ConsoleManager { +public interface ConsoleManager extends AutoCloseable { /** @return the prompt prefix */ String getPrompt(); @@ -89,6 +89,7 @@ public interface ConsoleManager { /** Closes the manager. * * @throws IOException if the close raised an exception */ + @Override void close() throws IOException; /** @return if the manager is closed. */ diff --git a/gclc/src/main/java/fr/bigeon/gclc/manager/PipedConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/manager/PipedConsoleManager.java index 1fc95a0..ef6a428 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/PipedConsoleManager.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/PipedConsoleManager.java @@ -54,7 +54,7 @@ import java.nio.charset.Charset; * * @author Emmanuel Bigeon */ public final class PipedConsoleManager - implements ConsoleManager, AutoCloseable { + implements ConsoleManager { /** The encoding between streams */ private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$ @@ -91,6 +91,7 @@ public final class PipedConsoleManager Thread th = new Thread(writing, "write"); //$NON-NLS-1$ th.start(); th = new Thread(reading, "read"); //$NON-NLS-1$ + th.setDaemon(true); th.start(); } diff --git a/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java index ca7bbcf..6b839f6 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java @@ -88,6 +88,7 @@ public final class SystemConsoleManager implements ConsoleManager { // NOSONAR this.in = new BufferedReader(new InputStreamReader(in, charset)); reading = new ReadingRunnable(this.in); promptThread = new Thread(reading, "prompt"); //$NON-NLS-1$ + promptThread.setDaemon(true); promptThread.start(); } @@ -156,6 +157,7 @@ public final class SystemConsoleManager implements ConsoleManager { // NOSONAR public void close() throws IOException { closed = true; reading.setRunning(false); + promptThread.interrupt(); } /* (non-Javadoc) diff --git a/gclc/src/test/java/fr/bigeon/gclc/tools/AOutputForwardRunnableTest.java b/gclc/src/test/java/fr/bigeon/gclc/tools/AOutputForwardRunnableTest.java index 35d7ba6..ebaa48e 100644 --- a/gclc/src/test/java/fr/bigeon/gclc/tools/AOutputForwardRunnableTest.java +++ b/gclc/src/test/java/fr/bigeon/gclc/tools/AOutputForwardRunnableTest.java @@ -58,7 +58,7 @@ public class AOutputForwardRunnableTest { * @author Emmanuel Bigeon */ private final class AOutputForwardTestRunnable extends AOutputForwardRunnable { - private int count = 1; + private int count = 2; private String message; /** @param manager */ @@ -117,11 +117,20 @@ public class AOutputForwardRunnableTest { manager); Thread th = new Thread(runnable, "forward"); - th.start(); manager.println("before"); + th.start(); assertEquals("before", runnable.getMessage()); + synchronized (this) { + wait(3000); + } + manager.println("after"); + assertEquals("after", runnable.getMessage()); + synchronized (this) { + wait(3000); + } + th.join(); } catch (IOException | InterruptedException e) {