Added prompting interruption, add exception in parameterized commands

Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
This commit is contained in:
Emmanuel Bigeon 2016-11-29 16:37:20 -05:00
parent fd8dde32f1
commit 65b6be8283
11 changed files with 264 additions and 72 deletions

View File

@ -74,4 +74,9 @@ public class ConsoleRunnable implements Runnable {
app.exit(); app.exit();
} }
/** @return if the application is running */
public boolean isRunning() {
return app.isRunning();
}
} }

View File

@ -110,7 +110,7 @@ public class SocketConsoleApplicationShell implements Runnable {
/** The application shutdown string */ /** The application shutdown string */
private final String applicationShutdown; private final String applicationShutdown;
/** The charset for the communication. */ /** The charset for the communication. */
private Charset charset; private final Charset charset;
/** Create a socket application shell which will listen on the given port /** Create a socket application shell which will listen on the given port
* and close session upon the provided string reception by client * and close session upon the provided string reception by client
@ -156,8 +156,7 @@ public class SocketConsoleApplicationShell implements Runnable {
// Create the streams // Create the streams
try (PipedOutputStream outStream = new PipedOutputStream(); try (PipedOutputStream outStream = new PipedOutputStream();
BufferedWriter writer = new BufferedWriter( BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(outStream, new OutputStreamWriter(outStream, charset));
charset));
InputStreamReader isr = new InputStreamReader(consoleInput, InputStreamReader isr = new InputStreamReader(consoleInput,
charset); charset);
BufferedReader inBuf = new BufferedReader(isr)) { BufferedReader inBuf = new BufferedReader(isr)) {
@ -187,24 +186,24 @@ public class SocketConsoleApplicationShell implements Runnable {
Thread appThOld = null; Thread appThOld = null;
Thread appThNext = new Thread(runnable); Thread appThNext = new Thread(runnable);
while (running) { while (running) {
LOGGER.info("Opening client"); //$NON-NLS-1$
try (Socket clientSocket = serverSocket.accept(); try (Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter( PrintWriter out = new PrintWriter(new OutputStreamWriter(
new OutputStreamWriter(clientSocket.getOutputStream(), clientSocket.getOutputStream(), charset), true);
charset),
true);
InputStreamReader isr = new InputStreamReader( InputStreamReader isr = new InputStreamReader(
clientSocket.getInputStream(), clientSocket.getInputStream(), charset);
charset);
BufferedReader in = new BufferedReader(isr);) { BufferedReader in = new BufferedReader(isr);) {
// this is not threaded to avoid several clients at the same // this is not threaded to avoid several clients at the same
// time // time
consoleManager.setOutput(out); consoleManager.setOutput(out);
// Initiate application // Initiate application
if (appThOld == null || !appThOld.isAlive()) {
if (appThOld == null || !appThOld.isAlive() ||
!runnable.isRunning()) {
appThNext.start(); appThNext.start();
// Prepare next start // Prepare next start
appThOld = appThNext; appThOld = appThNext;
appThNext = new Thread(runnable); appThNext = new Thread(runnable, "gclc-ctrl"); //$NON-NLS-1$
} else { } else {
out.println("Reconnected"); //$NON-NLS-1$ out.println("Reconnected"); //$NON-NLS-1$
} }
@ -215,7 +214,9 @@ public class SocketConsoleApplicationShell implements Runnable {
"Socket closed with exception (probably due to server interruption)", //$NON-NLS-1$ "Socket closed with exception (probably due to server interruption)", //$NON-NLS-1$
e); e);
} }
LOGGER.info("Closing client"); //$NON-NLS-1$
} }
LOGGER.info("Out client"); //$NON-NLS-1$
} }
/** active communication between server and client /** active communication between server and client

View File

@ -38,6 +38,9 @@
*/ */
package fr.bigeon.gclc.socket; package fr.bigeon.gclc.socket;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -60,7 +63,8 @@ public class SocketConsoleApplicationTest {
@Test @Test
public void integrationTest() { public void integrationTest() {
Thread server = TestServer.startServer(false); Thread server;
server = TestServer.startServer("bye");
try { try {
Thread.sleep(100); Thread.sleep(100);
} catch (InterruptedException e1) { } catch (InterruptedException e1) {
@ -70,27 +74,89 @@ public class SocketConsoleApplicationTest {
final int portNumber = 3300; final int portNumber = 3300;
try (Socket kkSocket = new Socket(hostName, portNumber); try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), PrintWriter out = new PrintWriter(kkSocket.getOutputStream(),
true); true);
BufferedReader in = new BufferedReader( BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));) { new InputStreamReader(kkSocket.getInputStream()));) {
String fromServer; String fromServer;
int i = 0; int i = 0;
String[] cmds = {"help", "toto", "test", "close"}; String[] cmds = {"help", "toto", "test", "bye"};
while ((fromServer = in.readLine()) != null) { 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.")) { if (fromServer.equals("Bye.")) {
break; break;
} }
final String fromUser = cmds[i]; final String fromUser = cmds[i];
if (fromUser != null) { if (fromUser != null) {
System.out.println("Client: " + fromUser); // System.out.println("Client: " + fromUser);
out.println(fromUser); out.println(fromUser);
} }
i++; i++;
} }
assertEquals(4, i);
} catch (final IOException e) {
e.printStackTrace();
}
try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(),
true);
BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));) {
String fromServer;
int i = 0;
String[] cmds = {"help", "toto", "test",
ConsoleTestApplication.EXIT};
while ((fromServer = in.readLine()) != null) {
// System.out.println("Server: \n" + ENCODER.decode(fromServer));
if (fromServer.equals("Bye.")) {
break;
}
final String fromUser = cmds[i];
if (fromUser != null) {
// System.out.println("Client: " + fromUser);
out.println(fromUser);
}
i++;
}
assertEquals(4, i);
} catch (final IOException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(),
true);
BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));) {
String fromServer;
int i = 0;
String[] cmds = {"help", "toto", "test",
ConsoleTestApplication.EXIT};
while ((fromServer = in.readLine()) != null) {
// System.out.println("Server: \n" + ENCODER.decode(fromServer));
if (fromServer.equals("Bye.")) {
break;
}
final String fromUser = cmds[i];
if (fromUser != null) {
// System.out.println("Client: " + fromUser);
out.println(fromUser);
}
i++;
}
assertEquals(4, i);
} catch (final IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -108,31 +174,39 @@ public class SocketConsoleApplicationTest {
} }
try (Socket kkSocket = new Socket(hostName, portNumber); try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), PrintWriter out = new PrintWriter(kkSocket.getOutputStream(),
true); true);
BufferedReader in = new BufferedReader( BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));) { new InputStreamReader(kkSocket.getInputStream()));) {
String fromServer; String fromServer;
int i = 0; int i = 0;
String[] cmds = {"help", "test", "close"}; String[] cmds = {"help", "test", "close"};
while ((fromServer = in.readLine()) != null) { while ((fromServer = in.readLine()) != null) {
// System.out.println("Server: \n" + ENCODER.decode(fromServer)); assertTrue(i < 2);
System.out.println("Server: \n" + ENCODER.decode(fromServer));
if (fromServer.equals("Bye.")) { if (fromServer.equals("Bye.")) {
break; break;
} }
final String fromUser = cmds[i]; final String fromUser = cmds[i];
if (fromUser != null) { if (fromUser != null) {
// System.out.println("Client: " + fromUser); System.out.println("Client: " + fromUser);
out.println(fromUser); out.println(fromUser);
} }
i++; i++;
} }
assertEquals(2, i);
} catch (final IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
Thread srv = TestServer.getServer();
TestServer.closeServer(); TestServer.closeServer();
try {
srv.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
} }

View File

@ -85,6 +85,18 @@ public class TestServer {
return getServer(); return getServer();
} }
public static Thread startServer(String closeConnection) {
if (SHELL == null) {
SHELL = new SocketConsoleApplicationShell(3300, closeConnection,
ConsoleTestApplication.EXIT, Charset.forName("UTF-8"));
final ConsoleTestApplication app = new ConsoleTestApplication(
SHELL.getConsoleManager());
SHELL.setApplication(app);
server = null;
}
return getServer();
}
public static void closeServer() { public static void closeServer() {
SHELL.stop(); SHELL.stop();
SHELL = null; SHELL = null;

View File

@ -40,13 +40,13 @@ package fr.bigeon.gclc.swt;
import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.manager.ConsoleManager;
/** /** This class represents an object used to send commands to a console
* application.
* <p> * <p>
* TODO * The sending of command is done in two phases. Define the input through get
* and set, and then validate the input.
* *
* @author Emmanuel Bigeon * @author Emmanuel Bigeon */
*
*/
public interface ConsoleDelayIO extends ConsoleManager { public interface ConsoleDelayIO extends ConsoleManager {
/** Actually send the input as the prompt next input. */ /** Actually send the input as the prompt next input. */
void validateInput(); void validateInput();

View File

@ -37,6 +37,7 @@
package fr.bigeon.gclc; package fr.bigeon.gclc;
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -106,18 +107,13 @@ public class ConsoleApplication implements ICommandProvider {
return root.add(cmd); return root.add(cmd);
} }
/** Add a command request listener. /** @param listener the command listener */
* <p>
* A listener can listen several times to the same application.
*
* @param listener the listener to add. */
public final void addListener(CommandRequestListener listener) { public final void addListener(CommandRequestListener listener) {
listeners.add(listener); listeners.add(listener);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see * @see fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String,
* fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String,
* java.lang.String[]) */ * java.lang.String[]) */
@Override @Override
public final void executeSub(String command, public final void executeSub(String command,
@ -125,10 +121,11 @@ public class ConsoleApplication implements ICommandProvider {
root.executeSub(command, args); root.executeSub(command, args);
} }
/** Exit this running application before next command prompt */ /** Signify to the application that no command should be inputed anymore */
public final void exit() { public final void exit() {
LOGGER.fine("Request exiting application..."); //$NON-NLS-1$ LOGGER.fine("Request exiting application..."); //$NON-NLS-1$
running = false; running = false;
manager.interruptPrompt();
} }
/* (non-Javadoc) /* (non-Javadoc)
@ -143,8 +140,8 @@ public class ConsoleApplication implements ICommandProvider {
return manager; return manager;
} }
/** @param cmd the command to interpret /** @param cmd the command
* @throws IOException if the manager was closed */ * @throws IOException if the command could not be parsed */
public final void interpretCommand(String cmd) throws IOException { public final void interpretCommand(String cmd) throws IOException {
List<String> args; List<String> args;
try { try {
@ -170,7 +167,7 @@ public class ConsoleApplication implements ICommandProvider {
} }
} }
/** @param listener the listener to remove (once) */ /** @param listener the command listener to remove */
public final void removeListener(CommandRequestListener listener) { public final void removeListener(CommandRequestListener listener) {
for (int i = 0; i < listeners.size(); i++) { for (int i = 0; i < listeners.size(); i++) {
if (listeners.get(i) == listener) { if (listeners.get(i) == listener) {
@ -180,14 +177,23 @@ public class ConsoleApplication implements ICommandProvider {
} }
} }
/** Launches the prompting application */ /** Start the application */
public final void start() { public final void start() {
try { try {
running = true; running = true;
if (header != null) { if (header != null) {
manager.println(header); manager.println(header);
} }
do { } catch (IOException e) {
// The manager was closed
running = false;
LOGGER.log(Level.WARNING,
"The console manager was closed. Closing the application as no one can reach it.", //$NON-NLS-1$
e);
return;
}
do {
try {
final String cmd = manager.prompt(); final String cmd = manager.prompt();
if (cmd == null || cmd.isEmpty()) { if (cmd == null || cmd.isEmpty()) {
continue; continue;
@ -196,22 +202,34 @@ public class ConsoleApplication implements ICommandProvider {
listener.commandRequest(cmd); listener.commandRequest(cmd);
} }
interpretCommand(cmd); interpretCommand(cmd);
} while (running); } catch (InterruptedIOException e) {
if (footer != null) { LOGGER.log(Level.INFO,
manager.println(footer); "Prompt interrupted. It is likely the application is closing.", //$NON-NLS-1$
e);
} catch (IOException e) {
// The manager was closed
running = false;
LOGGER.log(Level.WARNING,
"The console manager was closed. Closing the application as no one can reach it.", //$NON-NLS-1$
e);
}
} while (running);
if (footer != null) {
try {
manager.println(footer);
} catch (IOException e) {
// The manager was closed
running = false;
LOGGER.log(Level.WARNING,
"The console manager was closed.", //$NON-NLS-1$
e);
} }
running = false;
LOGGER.fine("Exiting application."); //$NON-NLS-1$
} catch (IOException e) {
// The manager was closed
running = false;
LOGGER.log(Level.WARNING,
"The console manager was closed. Closing the application as no one can reach it.", //$NON-NLS-1$
e);
} }
running = false;
LOGGER.fine("Exiting application."); //$NON-NLS-1$
} }
/** @return if the application is running */ /** @return the running status */
public boolean isRunning() { public boolean isRunning() {
return running; return running;
} }

View File

@ -154,8 +154,9 @@ public abstract class ParametrizedCommand extends Command {
} }
} }
/** @param parameters the command parameters */ /** @param parameters the command parameters
protected abstract void doExecute(CommandParameters parameters); * @throws CommandRunException if the command failed */
protected abstract void doExecute(CommandParameters parameters) throws CommandRunException;
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */ * @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */

View File

@ -39,6 +39,7 @@
package fr.bigeon.gclc.manager; package fr.bigeon.gclc.manager;
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException;
/** <p> /** <p>
* A console manager is in charge of the basic prompts and prints on a console. * A console manager is in charge of the basic prompts and prints on a console.
@ -69,15 +70,15 @@ public interface ConsoleManager {
void println(String message) throws IOException; void println(String message) throws IOException;
/** @return the user inputed string /** @return the user inputed string
* @throws IOException if the manager is closed or could not read the * @throws IOException if the manager is closed or could not read the prompt
* prompt */ * @throws InterruptedIOException if the prompt was interrupted */
String prompt() throws IOException; String prompt() throws IOException, InterruptedIOException;
/** @param message the message to prompt the user /** @param message the message to prompt the user
* @return the user inputed string * @return the user inputed string
* @throws IOException if the manager is closed or could not read the * @throws IOException if the manager is closed or could not read the prompt
* prompt */ * @throws InterruptedIOException if the prompt was interrupted */
String prompt(String message) throws IOException; String prompt(String message) throws IOException, InterruptedIOException;
/** <p> /** <p>
* Set a prompting prefix. * Set a prompting prefix.
@ -92,4 +93,7 @@ public interface ConsoleManager {
/** @return if the manager is closed. */ /** @return if the manager is closed. */
boolean isClosed(); boolean isClosed();
/** Indicate to the manager that is should interrompt the prompting */
void interruptPrompt();
} }

View File

@ -0,0 +1,54 @@
/**
* gclc:fr.bigeon.gclc.manager.ReadRunnable.java
* Created on: Nov 21, 2016
*/
package fr.bigeon.gclc.manager;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <p>
* TODO
*
* @author Emmanuel Bigeon
*
*/
public class ReadRunnable implements Runnable {
/** The logger */
private static final Logger LOGGER = Logger
.getLogger(ReadRunnable.class.getName());
/** The result */
private String result = "";
/** The input buffer */
private final BufferedReader in;
/** @param in the input buffer */
protected ReadRunnable(BufferedReader in) {
super();
this.in = in;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run() */
@Override
public void run() {
try {
result = in.readLine();
while (result != null && result.length() > 0 &&
result.charAt(0) == 0) {
result = result.substring(1);
}
} catch (final IOException e) {
LOGGER.log(Level.SEVERE, "Unable to read prompt", e); //$NON-NLS-1$
}
}
/** @return the result */
public String getResult() {
return result;
}
}

View File

@ -44,7 +44,6 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** A console using the input stream and print stream. /** A console using the input stream and print stream.
@ -75,6 +74,8 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR
/** If the manager is closed */ /** If the manager is closed */
private boolean closed = false; private boolean closed = false;
private Thread reading;
/** This default constructor relies on the system defined standart output /** This default constructor relies on the system defined standart output
* and input stream. */ * and input stream. */
public SystemConsoleManager() { public SystemConsoleManager() {
@ -140,18 +141,24 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR
@Override @Override
public String prompt(String message) throws IOException { public String prompt(String message) throws IOException {
checkOpen(); checkOpen();
String result = EMPTY;
out.print(message); out.print(message);
try { // ReadRunnable rr = new ReadRunnable(in);
// reading = new Thread(rr, "prompt"); //$NON-NLS-1$
// reading.start();
// try {
// reading.join();
// } catch (final InterruptedException e) {
// LOGGER.log(Level.SEVERE, "Prompt reading interrupted", e); //$NON-NLS-1$
// throw new InterruptedIOException("Prompt interruption"); //$NON-NLS-1$
// }
String result = EMPTY;
result = in.readLine(); result = in.readLine();
while (result != null && result.length() > 0 && while (result != null && result.length() > 0 &&
result.charAt(0) == 0) { result.charAt(0) == 0) {
result = result.substring(1); result = result.substring(1);
} }
} catch (final IOException e) {
LOGGER.log(Level.SEVERE, "Unable to read prompt", e); //$NON-NLS-1$
}
return result; return result;
// return rr.getResult();
} }
/** @param prompt the prompt to set */ /** @param prompt the prompt to set */
@ -174,4 +181,13 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR
return closed; return closed;
} }
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
@Override
public void interruptPrompt() {
if (reading != null) {
reading.interrupt();
}
}
} }

View File

@ -74,7 +74,7 @@ public class TestConsoleManager implements ConsoleManager, AutoCloseable {
PipedOutputStream out = new PipedOutputStream(commandOutput); PipedOutputStream out = new PipedOutputStream(commandOutput);
commandBuffOutput = new BufferedReader( commandBuffOutput = new BufferedReader(
new InputStreamReader(commandOutput, Charset.defaultCharset())); new InputStreamReader(commandOutput, Charset.defaultCharset()));
outPrint = new PrintStream(out); outPrint = new PrintStream(out, true, Charset.defaultCharset().name());
innerManager = new SystemConsoleManager(outPrint, in, innerManager = new SystemConsoleManager(outPrint, in,
Charset.forName("UTF-8")); Charset.forName("UTF-8"));
} }
@ -141,4 +141,11 @@ public class TestConsoleManager implements ConsoleManager, AutoCloseable {
public String readNextLine() throws IOException { public String readNextLine() throws IOException {
return commandBuffOutput.readLine(); return commandBuffOutput.readLine();
} }
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
@Override
public void interruptPrompt() {
innerManager.interruptPrompt();
}
} }