diff --git a/gclc-socket/pom.xml b/gclc-socket/pom.xml index 44dd90f..02f2e47 100644 --- a/gclc-socket/pom.xml +++ b/gclc-socket/pom.xml @@ -70,7 +70,7 @@ of Emmanuel Bigeon. --> 4.0.0 gclc-socket - 1.0.7-SNAPSHOT + 1.1.0-SNAPSHOT jar http://www.bigeon.fr/emmanuel @@ -87,7 +87,7 @@ of Emmanuel Bigeon. --> fr.bigeon gclc - 1.2.6 + 1.3.1-SNAPSHOT fr.bigeon diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java index 9ff565e..255f76c 100644 --- a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java +++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java @@ -38,6 +38,9 @@ */ package fr.bigeon.gclc.socket; +import java.util.logging.Level; +import java.util.logging.Logger; + import fr.bigeon.gclc.ConsoleApplication; /** A runnable class that will actually have the application running. @@ -45,27 +48,45 @@ import fr.bigeon.gclc.ConsoleApplication; * @author Emmanuel Bigeon */ public class ConsoleRunnable implements Runnable { + private static final long TIMEOUT = 100; + private static final Logger LOGGER = Logger + .getLogger(ConsoleRunnable.class.getName()); /** The actual application */ private final ConsoleApplication app; - /** The synchronization object */ - private final Object promptingLock; + private final Object lock = new Object(); + /** the state of this runnable */ + private boolean running = true; + private boolean startReq; - /** @param app the application - * @param promptingLock the synchronization object */ - public ConsoleRunnable(ConsoleApplication app, Object promptingLock) { + /** @param app the application */ + public ConsoleRunnable(ConsoleApplication app) { super(); this.app = app; - this.promptingLock = promptingLock; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { - app.start(); - synchronized (promptingLock) { - // release all waiting elements before ending - promptingLock.notifyAll(); + while (running) { + synchronized (lock) { + while (running && !startReq) { + try { + lock.wait(TIMEOUT); + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, + "Console application runnable interrupted wildly!", + e); + return; + } + } + startReq = false; + if (!running) { + return; + } + lock.notify(); + } + app.start(); } } @@ -75,8 +96,33 @@ public class ConsoleRunnable implements Runnable { } /** @return if the application is running */ - public boolean isRunning() { + public boolean isApplicationRunning() { return app.isRunning(); } + /** @param running the running to set */ + public void setRunning(boolean running) { + synchronized (lock) { + this.running = running; + } + } + + /** @return the running */ + public boolean isRunning() { + synchronized (lock) { + return running; + } + } + + public void restart() { + synchronized (lock) { + startReq = true; + lock.notify(); + try { + lock.wait(TIMEOUT); + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, "Restart wait interrupted!", e); + } + } + } } diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleApplicationShell.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleApplicationShell.java index f7ef0e3..022ebb3 100644 --- a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleApplicationShell.java +++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleApplicationShell.java @@ -52,6 +52,8 @@ import java.util.logging.Logger; import fr.bigeon.gclc.ConsoleApplication; import fr.bigeon.gclc.manager.ConsoleManager; +import fr.bigeon.gclc.manager.PipedConsoleManager; +import fr.bigeon.gclc.manager.ReadingRunnable; import fr.bigeon.smu.StringEncoder; /** This is a socket communicating console consoleManager @@ -86,6 +88,8 @@ public class SocketConsoleApplicationShell implements Runnable { /** The class logger */ private static final Logger LOGGER = Logger .getLogger(SocketConsoleApplicationShell.class.getName()); + protected static final long ONE_TENTH_OF_SECOND = 100; + private static final long ONE_SEC = 100; /** The listening port */ private final int port; /** The input */ @@ -96,12 +100,12 @@ public class SocketConsoleApplicationShell implements Runnable { private final String close; /** The running status */ private boolean running; - /** An object to lock on for prompt */ - private final Object promptingLock = new Object(); +// /** The console manager implementation */ +// private final ThreadedServerConsoleManager consoleManager = new ThreadedServerConsoleManager( +// ENCODER, promptingLock); /** The console manager implementation */ - private final ThreadedServerConsoleManager consoleManager = new ThreadedServerConsoleManager( - ENCODER, promptingLock); + private final PipedConsoleManager consoleManager; /** The auto close flag. if this is true, every request closes the session * after its call */ private final boolean autoClose; @@ -118,14 +122,17 @@ public class SocketConsoleApplicationShell implements Runnable { * @param port the port to listen to * @param close the session closing command * @param applicationShutdown the appication shut down command - * @param charset the charset for communication */ + * @param charset the charset for communication + * @throws IOException if the manager could not be created */ public SocketConsoleApplicationShell(int port, String close, - String applicationShutdown, Charset charset) { + String applicationShutdown, Charset charset) throws IOException { this.port = port; this.close = close; this.applicationShutdown = applicationShutdown; this.autoClose = false; this.charset = charset; + // + consoleManager = new PipedConsoleManager(); } /** Create a socket application shell which will listen on the given port @@ -135,14 +142,17 @@ public class SocketConsoleApplicationShell implements Runnable { * @param autoClose if the session must be closed once the request has been * sent * @param applicationShutdown the appication shut down command - * @param charset the charset for communication */ + * @param charset the charset for communication + * @throws IOException if the manager could not be created */ public SocketConsoleApplicationShell(int port, boolean autoClose, - String applicationShutdown, Charset charset) { + String applicationShutdown, Charset charset) throws IOException { this.port = port; this.autoClose = autoClose; this.applicationShutdown = applicationShutdown; this.close = autoClose ? null : "close"; //$NON-NLS-1$ this.charset = charset; + // + consoleManager = new PipedConsoleManager(); } /* (non-Javadoc) @@ -161,7 +171,7 @@ public class SocketConsoleApplicationShell implements Runnable { charset); BufferedReader inBuf = new BufferedReader(isr)) { consoleInput.connect(outStream); - consoleManager.setInput(inBuf); +// consoleManager.setInput(inBuf); runSokectServer(writer); // Close the application // Pass command to application @@ -181,10 +191,9 @@ public class SocketConsoleApplicationShell implements Runnable { /** @param writer the writer to the application * @throws IOException if the communication with the client failed */ private void runSokectServer(BufferedWriter writer) throws IOException { - final ConsoleRunnable runnable = new ConsoleRunnable(app, - promptingLock); - Thread appThOld = null; - Thread appThNext = new Thread(runnable); + final ConsoleRunnable runnable = new ConsoleRunnable(app); + Thread appThNext = new Thread(runnable, "gclc-ctrl"); + appThNext.start(); while (running) { LOGGER.info("Opening client"); //$NON-NLS-1$ try (Socket clientSocket = serverSocket.accept(); @@ -195,19 +204,25 @@ public class SocketConsoleApplicationShell implements Runnable { BufferedReader in = new BufferedReader(isr);) { // this is not threaded to avoid several clients at the same // time - consoleManager.setOutput(out); - // Initiate application - if (appThOld == null || !appThOld.isAlive() || - !runnable.isRunning()) { - appThNext.start(); - // Prepare next start - appThOld = appThNext; - appThNext = new Thread(runnable, "gclc-ctrl"); //$NON-NLS-1$ + // Initiate application + if (!runnable.isApplicationRunning()) { + LOGGER.info("Start application"); + runnable.restart(); + synchronized (this) { + try { + wait(ONE_SEC); + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, + "Interruption in application start", e); + } + } } else { + LOGGER.info("Reconnect to application"); out.println("Reconnected"); //$NON-NLS-1$ + out.println(consoleManager.getPrompt()); } - communicate(writer, in); + communicate(clientSocket, out, in); } catch (SocketException e) { LOGGER.log(Level.INFO, "Socket closed"); //$NON-NLS-1$ LOGGER.log(Level.FINE, @@ -216,6 +231,8 @@ public class SocketConsoleApplicationShell implements Runnable { } LOGGER.info("Closing client"); //$NON-NLS-1$ } + runnable.setRunning(false); + consoleManager.type(applicationShutdown); LOGGER.info("Out client"); //$NON-NLS-1$ } @@ -224,69 +241,110 @@ public class SocketConsoleApplicationShell implements Runnable { * @param writer the writer to the application * @param in the input from the client * @throws IOException if the communication failed */ - private void communicate(BufferedWriter writer, + private void communicate(final Socket socket, final PrintWriter writer, BufferedReader in) throws IOException { - synchronized (promptingLock) { - if (!consoleManager.isPrompting()) { + Thread th = new Thread(new Runnable() { + + @SuppressWarnings("synthetic-access") + @Override + public void run() { try { - // wait for application to finish its operation - promptingLock.wait(); - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, INTERRUPTION_WHILE_WORKING, e); + while (!socket.isOutputShutdown()) { + while (!socket.isOutputShutdown() && + !consoleManager.available()) { + try { + synchronized (this) { + wait(ONE_TENTH_OF_SECOND); + } + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, "Interrupted wait", //$NON-NLS-1$ + e); + return; + } + } + if (socket.isOutputShutdown()) { + return; + } + String m = consoleManager.readNextLine(); + writer.println(m); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Unexpected problem in manager", //$NON-NLS-1$ + e); } } - if (autoClose) { - communicateOnce(in, writer); - } else { - communicateLoop(in, writer); - } + }, "ClientComm"); //$NON-NLS-1$ + th.start(); + if (autoClose) { + communicateOnce(socket, in); + } else { + communicateLoop(socket, in); } } /** @param in the input from the client - * @param writer the output to the client * @throws IOException if the communication failed */ - private void communicateOnce(BufferedReader in, - BufferedWriter writer) throws IOException { + private void communicateOnce(Socket socket, + BufferedReader in) throws IOException { + ReadingRunnable reading = new ReadingRunnable(in); + Thread th = new Thread(reading, "gclcToApp"); + th.start(); String ln; - if ((ln = in.readLine()) != null) { + if (app.isRunning()) { + while (app.isRunning() && !reading.hasMessage()) { + synchronized (this) { + try { + wait(ONE_SEC); + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, "Wait interrupted", e); + } + } + } + if (!app.isRunning()) { + return; + } + ln = reading.getMessage(); if (ln.equals(close)) { return; } // Pass command to application - writer.write(ln + EOL); - writer.flush(); - try { - // Wait for application process to - // finish - promptingLock.wait(); - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, INTERRUPTION_WHILE_WORKING, e); - } + consoleManager.type(ln); } + reading.setRunning(false); + socket.shutdownOutput(); } /** @param in the input from the client - * @param writer the output to the client * @throws IOException if the communication failed */ - private void communicateLoop(BufferedReader in, - BufferedWriter writer) throws IOException { + private void communicateLoop(Socket socket, + BufferedReader in) throws IOException { + ReadingRunnable reading = new ReadingRunnable(in); + Thread th = new Thread(reading, "gclcToApp"); + th.start(); String ln; - while (app.isRunning() && (ln = in.readLine()) != null) { + while (app.isRunning()) { + while (app.isRunning() && !reading.hasMessage()) { + synchronized (this) { + try { + wait(ONE_SEC); + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, "Wait interrupted", e); + } + } + } + if (!app.isRunning()) { + break; + } + ln = reading.getMessage(); if (ln.equals(close)) { break; } // Pass command to application - writer.write(ln + EOL); - writer.flush(); - try { - // Wait for application process to - // finish - promptingLock.wait(); - } catch (final InterruptedException e) { - LOGGER.log(Level.SEVERE, INTERRUPTION_WHILE_WORKING, e); - } + consoleManager.type(ln); } + reading.setRunning(false); + socket.shutdownOutput(); + } /** @return the consoleManager */ @@ -310,9 +368,6 @@ public class SocketConsoleApplicationShell implements Runnable { } catch (IOException e) { LOGGER.log(Level.SEVERE, "Exception in closing socket server", e); //$NON-NLS-1$ } - synchronized (promptingLock) { - promptingLock.notifyAll(); - } app.exit(); } diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ThreadedServerConsoleManager.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ThreadedServerConsoleManager.java deleted file mode 100644 index 7b0484c..0000000 --- a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ThreadedServerConsoleManager.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright E. Bigeon (2014) - * - * emmanuel@bigeon.fr - * - * This software is a computer program whose purpose is to - * Socket implementation of GCLC. - * - * 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-socket:fr.bigeon.gclc.socket.ThreadedServerConsoleManager.java - * Created on: Jun 1, 2016 - */ -package fr.bigeon.gclc.socket; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.logging.Level; -import java.util.logging.Logger; - -import fr.bigeon.gclc.manager.ConsoleManager; -import fr.bigeon.smu.StringEncoder; - -/** The console manager for socket communication - * - * @author Emmanuel Bigeon */ -public class ThreadedServerConsoleManager implements ConsoleManager { - - /** The eol character */ - private static final String EOL = "\n"; //$NON-NLS-1$ - /** The class logger */ - private static final Logger LOGGER = Logger - .getLogger(ThreadedServerConsoleManager.class.getName()); - /** The empty string constant */ - private static final String EMPTY = ""; //$NON-NLS-1$ - /** The prompting sequence */ - private String prompt = EMPTY; - /** The buffer of data to send to the user */ - private StringBuilder buffer = new StringBuilder(); - /** The synchronized object */ - private final Object promptingLock; - /** The output to write data comming from the application */ - private PrintWriter output; - /** The encoder to encode data coming from the application */ - private final StringEncoder encoder; - /** The input to wait data from the user */ - private BufferedReader input; - /** the prompting status */ - private boolean doPrompt; - /** - * - */ - private boolean closed = false; - - /** Create the console manager. - * - * @param encoder the encoder for output - * @param promptingLock the synchronization object */ - public ThreadedServerConsoleManager(StringEncoder encoder, - Object promptingLock) { - super(); - this.encoder = encoder; - this.promptingLock = promptingLock; - } - - /** @param input the input to set */ - public void setInput(BufferedReader input) { - this.input = input; - } - - /** @param output the output to set */ - public void setOutput(PrintWriter output) { - this.output = output; - } - - @Override - public void setPrompt(String prompt) { - this.prompt = prompt; - } - - @Override - public String prompt(String message) { - buffer.append(message); - String userInput = EMPTY; - boolean prompting = true; - while (prompting) { - // Send buffer content - output.println(encoder.encode(buffer.toString())); - try { - synchronized (promptingLock) { - doPrompt = true; - promptingLock.notify(); - } - userInput = input.readLine(); - doPrompt = false; - prompting = false; - } catch (final IOException e) { - LOGGER.log(Level.SEVERE, "input reading error", e); //$NON-NLS-1$ - } - } - // Renew buffer - buffer = new StringBuilder(); - return userInput; - } - - /** @return the prompting status */ - public synchronized boolean isPrompting() { - return doPrompt; - } - - @Override - public String prompt() { - return prompt(prompt); - } - - @Override - public void println(String message) { - buffer.append(message + EOL); - - } - - @Override - public void println() { - buffer.append(EOL); - } - - @Override - public void print(String text) { - buffer.append(text); - } - - @Override - public String getPrompt() { - return prompt; - } - - /* (non-Javadoc) - * @see fr.bigeon.gclc.manager.ConsoleManager#close() */ - @Override - public void close() throws IOException { - // Do nothing - this.closed = true; - } - - /* (non-Javadoc) - * @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */ - @Override - public boolean isClosed() { - return closed; - } - -} diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java index e44b04c..54e7e65 100644 --- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java +++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java @@ -123,28 +123,33 @@ public class ConsoleRunnableTest { public boolean isClosed() { return i == cmds.length; } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */ + @Override + public void interruptPrompt() { + // + } } /** Test method for - * {@link fr.bigeon.gclc.socket.ConsoleRunnable#ConsoleRunnable(fr.bigeon.gclc.ConsoleApplication, java.lang.Object)} + * {@link fr.bigeon.gclc.socket.ConsoleRunnable#ConsoleRunnable(fr.bigeon.gclc.ConsoleApplication)} * . */ @Test public void testConsoleRunnable() { - Object lock = new Object(); ConsoleApplication app = new ConsoleTestApplication( new SystemConsoleManager()); - ConsoleRunnable runnable = new ConsoleRunnable(app, lock); + ConsoleRunnable runnable = new ConsoleRunnable(app); } /** Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#run()}. */ @Test public void testRunFlow() { - Object lock = new Object(); ConsoleApplication app = new ConsoleTestApplication( new ConsoleManagerTestImplementation( new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$ - ConsoleRunnable runnable = new ConsoleRunnable(app, lock); + ConsoleRunnable runnable = new ConsoleRunnable(app); Thread th = new Thread(runnable); th.start(); @@ -155,11 +160,10 @@ public class ConsoleRunnableTest { /** Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#stop()}. */ @Test public void testStop() { - Object lock = new Object(); ConsoleApplication app = new ConsoleTestApplication( new ConsoleManagerTestImplementation( new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$ - ConsoleRunnable runnable = new ConsoleRunnable(app, lock); + ConsoleRunnable runnable = new ConsoleRunnable(app); runnable.stop(); Thread th = new Thread(runnable); th.start(); @@ -170,11 +174,10 @@ public class ConsoleRunnableTest { /** Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#stop()}. */ @Test public void testRun() { - Object lock = new Object(); ConsoleApplication app = new ConsoleTestApplication( new ConsoleManagerTestImplementation( new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$ - ConsoleRunnable runnable = new ConsoleRunnable(app, lock); + ConsoleRunnable runnable = new ConsoleRunnable(app); runnable.run(); } diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/SocketConsoleApplicationTest.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/SocketConsoleApplicationTest.java index 4f66691..34fbc65 100644 --- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/SocketConsoleApplicationTest.java +++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/SocketConsoleApplicationTest.java @@ -39,7 +39,9 @@ package fr.bigeon.gclc.socket; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.IOException; @@ -64,9 +66,14 @@ public class SocketConsoleApplicationTest { @Test public void integrationTest() { Thread server; - server = TestServer.startServer("bye"); try { - Thread.sleep(100); + server = TestServer.startServer("bye"); + } catch (IOException e3) { + assertNull(e3); + fail("unable to start server"); + } + try { + Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } @@ -83,14 +90,22 @@ public class SocketConsoleApplicationTest { int i = 0; String[] cmds = {"help", "toto", "test", "bye"}; while ((fromServer = in.readLine()) != null) { -// System.out.println("Server: \n" + ENCODER.decode(fromServer)); + System.out.println("Server: \n" + ENCODER.decode(fromServer)); if (fromServer.equals("Bye.")) { break; } + while (fromServer != null && !fromServer.equals("> ")) { + fromServer = in.readLine(); + System.out + .println("Server: \n" + ENCODER.decode(fromServer)); + } + if (fromServer == null) { + fail("Null pointer"); + } final String fromUser = cmds[i]; if (fromUser != null) { -// System.out.println("Client: " + fromUser); + System.out.println("Client: " + fromUser); out.println(fromUser); } i++; @@ -111,14 +126,19 @@ public class SocketConsoleApplicationTest { String[] cmds = {"help", "toto", "test", ConsoleTestApplication.EXIT}; while ((fromServer = in.readLine()) != null) { -// System.out.println("Server: \n" + ENCODER.decode(fromServer)); - if (fromServer.equals("Bye.")) { + System.out.println("Server: \n" + ENCODER.decode(fromServer)); + while (fromServer != null && !fromServer.equals("> ")) { + fromServer = in.readLine(); + System.out + .println("Server: \n" + ENCODER.decode(fromServer)); + } + if (fromServer == null) { break; } final String fromUser = cmds[i]; if (fromUser != null) { -// System.out.println("Client: " + fromUser); + System.out.println("Client: " + fromUser); out.println(fromUser); } i++; @@ -144,14 +164,18 @@ public class SocketConsoleApplicationTest { String[] cmds = {"help", "toto", "test", ConsoleTestApplication.EXIT}; while ((fromServer = in.readLine()) != null) { -// System.out.println("Server: \n" + ENCODER.decode(fromServer)); - if (fromServer.equals("Bye.")) { + while (fromServer != null && !fromServer.equals("> ")) { + fromServer = in.readLine(); + System.out + .println("Server: \n" + ENCODER.decode(fromServer)); + } + if (fromServer == null) { break; } final String fromUser = cmds[i]; if (fromUser != null) { -// System.out.println("Client: " + fromUser); + System.out.println("Client: " + fromUser); out.println(fromUser); } i++; @@ -166,7 +190,11 @@ public class SocketConsoleApplicationTest { } catch (InterruptedException e2) { e2.printStackTrace(); } - server = TestServer.startServer(true); + try { + server = TestServer.startServer(true); + } catch (IOException e2) { + assertNull(e2); + } try { Thread.sleep(100); } catch (InterruptedException e1) { @@ -183,9 +211,15 @@ public class SocketConsoleApplicationTest { int i = 0; String[] cmds = {"help", "test", "close"}; while ((fromServer = in.readLine()) != null) { - assertTrue(i < 2); - System.out.println("Server: \n" + ENCODER.decode(fromServer)); - if (fromServer.equals("Bye.")) { + assertTrue(i < 1); + while (fromServer != null && !fromServer.equals("> ") && + !fromServer.equals("See you")) { + fromServer = in.readLine(); + System.out + .println("Server: \n" + ENCODER.decode(fromServer)); + } + if (fromServer == null || fromServer.equals("Bye.") || + fromServer.equals("See you")) { break; } @@ -196,11 +230,16 @@ public class SocketConsoleApplicationTest { } i++; } - assertEquals(2, i); + assertEquals(1, i); } catch (final IOException e) { e.printStackTrace(); } - Thread srv = TestServer.getServer(); + Thread srv = null; + try { + srv = TestServer.getServer(); + } catch (IOException e1) { + assertNull(e1); + } TestServer.closeServer(); try { srv.join(); diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestServer.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestServer.java index 4a4b144..dc78925 100644 --- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestServer.java +++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestServer.java @@ -34,6 +34,7 @@ */ package fr.bigeon.gclc.socket; +import java.io.IOException; import java.nio.charset.Charset; /** A test server @@ -45,8 +46,9 @@ public class TestServer { private static SocketConsoleApplicationShell SHELL; private static Thread server; - /** @param args no argument */ - public static void main(String... args) { + /** @param args no argument + * @throws IOException if the server starting failed */ + public static void main(String... args) throws IOException { try { startServer(false).join(); } catch (final InterruptedException e) { @@ -54,7 +56,7 @@ public class TestServer { } } - public static Thread getServer() { + public static Thread getServer() throws IOException { if (server == null) { server = new Thread(getShell(), "gclcServer"); server.start(); @@ -62,7 +64,7 @@ public class TestServer { return server; } - private static SocketConsoleApplicationShell getShell() { + private static SocketConsoleApplicationShell getShell() throws IOException { if (SHELL == null) { SHELL = new SocketConsoleApplicationShell(3300, "close", ConsoleTestApplication.EXIT, Charset.forName("UTF-8")); @@ -73,7 +75,7 @@ public class TestServer { return SHELL; } - public static Thread startServer(boolean autoClose) { + public static Thread startServer(boolean autoClose) throws IOException { if (SHELL == null) { SHELL = new SocketConsoleApplicationShell(3300, autoClose, ConsoleTestApplication.EXIT, Charset.forName("UTF-8")); @@ -85,7 +87,7 @@ public class TestServer { return getServer(); } - public static Thread startServer(String closeConnection) { + public static Thread startServer(String closeConnection) throws IOException { if (SHELL == null) { SHELL = new SocketConsoleApplicationShell(3300, closeConnection, ConsoleTestApplication.EXIT, Charset.forName("UTF-8")); diff --git a/gclc-swt/pom.xml b/gclc-swt/pom.xml index 24cb2bc..28ae9c8 100644 --- a/gclc-swt/pom.xml +++ b/gclc-swt/pom.xml @@ -37,7 +37,7 @@ > 4.0.0 gclc-swt - 1.0.5-SNAPSHOT + 1.1.0-SNAPSHOT jar http://www.bigeon.fr/emmanuel @@ -53,7 +53,7 @@ fr.bigeon gclc - 1.2.6 + 1.3.0 fr.bigeon 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 96f49d9..1fc95a0 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/PipedConsoleManager.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/PipedConsoleManager.java @@ -159,6 +159,12 @@ public final class PipedConsoleManager return reading.getMessage(); } + /** @return the content of the next line written by the application + * @throws IOException if the reading failed */ + public boolean available() throws IOException { + return reading.hasMessage(); + } + /* (non-Javadoc) * @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */ @Override diff --git a/gclc/src/main/java/fr/bigeon/gclc/manager/ReadingRunnable.java b/gclc/src/main/java/fr/bigeon/gclc/manager/ReadingRunnable.java index e0acf28..20aea73 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/ReadingRunnable.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/ReadingRunnable.java @@ -65,6 +65,7 @@ public class ReadingRunnable implements Runnable { /** Synchro object */ private final Object lock = new Object(); + private boolean waiting; /** @param reader the input to read from */ public ReadingRunnable(BufferedReader reader) { @@ -93,6 +94,7 @@ public class ReadingRunnable implements Runnable { } } catch (InterruptedIOException e) { LOGGER.log(Level.INFO, "Reading interrupted", e); //$NON-NLS-1$ + } catch (IOException e) { LOGGER.log(Level.SEVERE, "Unable to read from stream", e); //$NON-NLS-1$ running = false; @@ -122,6 +124,7 @@ public class ReadingRunnable implements Runnable { if (!running) { throw new IOException("Closed pipe"); //$NON-NLS-1$ } + waiting = true; while (messages.isEmpty()) { try { lock.wait(TIMEOUT); @@ -134,6 +137,7 @@ public class ReadingRunnable implements Runnable { } } LOGGER.finest("Polled: " + messages.peek()); //$NON-NLS-1$ + waiting = false; return messages.poll(); } } @@ -151,4 +155,27 @@ public class ReadingRunnable implements Runnable { return running; } } + + /** @return if a message is waiting + * @throws IOException if the pipe is closed */ + public boolean hasMessage() throws IOException { + synchronized (lock) { + if (!running) { + throw new IOException("Closed pipe"); //$NON-NLS-1$ + } + return !messages.isEmpty(); + } + } + + /** + * + */ + public void interrupt() { + synchronized (lock) { + if (waiting) { + messages.offer(""); + lock.notify(); + } + } + } } 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 8333072..ca7bbcf 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java @@ -170,7 +170,7 @@ public final class SystemConsoleManager implements ConsoleManager { // NOSONAR * @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */ @Override public void interruptPrompt() { - promptThread.interrupt(); + reading.interrupt(); } } diff --git a/gclc/src/main/java/fr/bigeon/gclc/manager/WritingRunnable.java b/gclc/src/main/java/fr/bigeon/gclc/manager/WritingRunnable.java index 82ce2fb..f7bb87e 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/WritingRunnable.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/WritingRunnable.java @@ -39,7 +39,7 @@ package fr.bigeon.gclc.manager; import java.io.IOException; -import java.io.PipedOutputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayDeque; @@ -62,7 +62,7 @@ public class WritingRunnable implements Runnable { /** Messages to write */ private final Deque messages = new ArrayDeque<>(); /** Stream to write to */ - private final PipedOutputStream outPrint; + private final OutputStream outPrint; /** The charset */ private final Charset charset; /** Runnable state */ @@ -73,7 +73,7 @@ public class WritingRunnable implements Runnable { /** @param outPrint the output to print to * @param charset the charset of the stream */ - public WritingRunnable(PipedOutputStream outPrint, Charset charset) { + public WritingRunnable(OutputStream outPrint, Charset charset) { super(); this.outPrint = outPrint; this.charset = charset;