diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/CommandForeground.java b/gclc-process/src/main/java/net/bigeon/gclc/process/CommandForeground.java index d6ada19..eb22e00 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/CommandForeground.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/CommandForeground.java @@ -94,12 +94,14 @@ import net.bigeon.gclc.manager.ConsoleOutput; * @author Emmanuel Bigeon */ public class CommandForeground extends ParametrizedCommand { + /** Number of milliseconds in a second. */ private static final int MILLIS_IN_A_SEC = 1000; /** The class logger. */ - private static final Logger LOGGER = Logger + private static final Logger LOGGER = Logger .getLogger(CommandForeground.class.getName()); + /** The task pool to fetch task from. */ - private final TaskPool pool; + private final TaskPool pool; /** Add the fork command. * diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/CommandFork.java b/gclc-process/src/main/java/net/bigeon/gclc/process/CommandFork.java index c46d81a..26353fb 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/CommandFork.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/CommandFork.java @@ -91,15 +91,19 @@ import net.bigeon.gclc.manager.ConsoleOutput; * @author Emmanuel Bigeon */ public class CommandFork extends Command { + /** The task pool containing the tasks. */ private final TaskPool pool; + /** The command provider. */ private final ICommandProvider provider; + /** The number of lines stored in the commands. */ private final int lines; /** Add the fork command. * * @param name the command name * @param provider the allowed command collection */ - public CommandFork(final String name, ICommandProvider provider, TaskPool pool) { + public CommandFork(final String name, final ICommandProvider provider, + final TaskPool pool) { this(name, provider, pool, -1); } @@ -107,17 +111,20 @@ public class CommandFork extends Command { * * @param name the command name * @param provider the allowed command collection */ - public CommandFork(final String name, ICommandProvider provider, TaskPool pool, - int lines) { + public CommandFork(final String name, final ICommandProvider provider, + final TaskPool pool, final int lines) { super(name); this.provider = provider; this.pool = pool; this.lines = lines; } + /* (non-Javadoc) + * @see net.bigeon.gclc.command.ICommand#execute(net.bigeon.gclc.manager. + * ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */ @Override - public void execute(final ConsoleOutput out, final ConsoleInput in, String... args) - throws CommandRunException { + public void execute(final ConsoleOutput out, final ConsoleInput in, + final String... args) throws CommandRunException { if (args.length < 1) { throw new CommandRunException("No command to fork"); } diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/ForkTask.java b/gclc-process/src/main/java/net/bigeon/gclc/process/ForkTask.java index c47ab78..9d48a81 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/ForkTask.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/ForkTask.java @@ -77,23 +77,28 @@ import net.bigeon.gclc.manager.ConsoleOutput; import net.bigeon.gclc.process.io.ConnectingConsoleInput; import net.bigeon.gclc.process.io.ConnectingConsoleOutput; -/** - *

- * TODO +/** A task that is made to run into a thread. * * @author Emmanuel Bigeon */ public abstract class ForkTask implements Task { + /** THe listeners. */ private final Set listeners = new HashSet<>(); + /** The running state. */ private boolean running = false; - - protected final ConnectingConsoleInput in = new ConnectingConsoleInput(); + /** The connecting input for this task */ + protected final ConnectingConsoleInput in = new ConnectingConsoleInput(); + /** The connecting output for this task */ protected final ConnectingConsoleOutput out; + /** The exception of the run. */ private CommandRunException exception; - private final Object runLock = new Object(); + /** The synchronization lock. */ + private final Object runLock = new Object(); - /** @param lines the number of print to store in the output */ - public ForkTask(int lines) { + /** Create the task. + * + * @param lines the number of print to store in the output */ + public ForkTask(final int lines) { out = new ConnectingConsoleOutput(ConnectingConsoleOutput.PERSIST, lines); } @@ -128,10 +133,13 @@ public abstract class ForkTask implements Task { } } - /** @param out the console output + /** Join the task. + * + * @param out the console output * @param in the console input - * @param timeout the maximal wait (0 for ever) */ - public final void join(final ConsoleOutput out, final ConsoleInput in, long timeout) { + * @param timeout the maximal time to join for (0 for ever) */ + public final void join(final ConsoleOutput out, final ConsoleInput in, + final long timeout) { synchronized (runLock) { this.out.connect(out); this.in.connect(in); @@ -140,7 +148,6 @@ public abstract class ForkTask implements Task { runLock.wait(timeout); } } catch (final InterruptedException e) { - // TODO log. Thread.currentThread().interrupt(); } this.out.disconnect(); diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleInput.java b/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleInput.java index 15bb843..9cf6a66 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleInput.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleInput.java @@ -75,19 +75,30 @@ import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.tools.ConstantString; import net.bigeon.gclc.tools.StringProvider; -/** @author Emmanuel Bigeon */ +/** A console input that can be connected to and diconnected from. + * + * @author Emmanuel Bigeon */ public final class ConnectingConsoleInput implements ConsoleInput { + /** The empty string provider. */ private static final ConstantString EMPTY_STRING = new ConstantString(""); + /** The logger. */ private static final Logger LOGGER = Logger .getLogger(ConnectingConsoleInput.class.getName()); + /** If the input is closed. */ private boolean close = false; + /** The prompt string. */ private StringProvider prompt = EMPTY_STRING; + /** If the input is currently in prompting state. */ private boolean prompting = false; + /** The synchronization lock for the rompting status. */ private final Object promptLock = new Object(); + /** The synchronization lock for the connection status. */ private final Object connectionLock = new Object(); + /** The connected console input. */ private ConsoleInput connected = null; + /** The connection state. */ private boolean disconnection = false; /* (non-Javadoc) @@ -97,6 +108,9 @@ public final class ConnectingConsoleInput implements ConsoleInput { close = true; } + /** Connect an input. + * + * @param input the input to connect */ public void connect(final ConsoleInput input) { disconnect(); synchronized (promptLock) { @@ -105,6 +119,7 @@ public final class ConnectingConsoleInput implements ConsoleInput { } } + /** Disconnect the current input. */ public void disconnect() { synchronized (connectionLock) { if (connected != null) { diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleOutput.java b/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleOutput.java index 5268986..509b56f 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleOutput.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleOutput.java @@ -75,26 +75,37 @@ import java.util.logging.Logger; import net.bigeon.gclc.manager.ConsoleOutput; -/** @author Emmanuel Bigeon */ +/** A console output that can be connected and disconnected from. + * + * @author Emmanuel Bigeon */ public class ConnectingConsoleOutput implements ConsoleOutput { + /** The logger. */ private static final Logger LOGGER = Logger .getLogger(ConnectingConsoleOutput.class.getName()); - /** If the undelivered message should be stored. */ + /** The flag indicating that the un-delivered message should be stored. */ public static final int QUEUE = 1; - /** If the messages should be stored in all cases. */ + /** The flag indicating that the messages should be stored in all cases. */ public static final int PERSIST = 1 << 1; + /** The connected output console */ private ConsoleOutput output; + /** If the console is closed */ private boolean close = false; + /** If the messages should be stored. */ private final boolean persistent; + /** If the message should be stored until delivery. */ private final boolean queued; + /** The messages. */ private final Deque messages; + /** The number of stored messages. */ private final int lines; - /** @param style the type of redirected output + /** Create the console output. + * + * @param style the type of redirected output * @param lines the number of lines to store */ - public ConnectingConsoleOutput(int style, int lines) { + public ConnectingConsoleOutput(final int style, final int lines) { super(); this.lines = lines; queued = (style & QUEUE) != 0; @@ -106,7 +117,10 @@ public class ConnectingConsoleOutput implements ConsoleOutput { } } - private synchronized void addMessage(String text) { + /** Add a message. + * + * @param text the message */ + private synchronized void addMessage(final String text) { if (persistent || queued && output == null) { if (messages.size() == lines) { messages.poll(); @@ -130,7 +144,10 @@ public class ConnectingConsoleOutput implements ConsoleOutput { close = true; } - public synchronized void connect(ConsoleOutput output) { + /** Connect an output. + * + * @param output the output */ + public synchronized void connect(final ConsoleOutput output) { this.output = output; for (final String string : messages) { try { @@ -145,6 +162,7 @@ public class ConnectingConsoleOutput implements ConsoleOutput { } } + /** Disconnect the currently connected output. */ public synchronized void disconnect() { output = null; } @@ -159,7 +177,7 @@ public class ConnectingConsoleOutput implements ConsoleOutput { /* (non-Javadoc) * @see fr.bigeon.gclc.manager.ConsoleOutput#print(java.lang.String) */ @Override - public void print(String text) { + public void print(final String text) { addMessage(text); } @@ -173,7 +191,7 @@ public class ConnectingConsoleOutput implements ConsoleOutput { /* (non-Javadoc) * @see fr.bigeon.gclc.manager.ConsoleOutput#println(java.lang.String) */ @Override - public void println(String message) { + public void println(final String message) { addMessage(message + System.lineSeparator()); } } diff --git a/gclc-socket/src/main/java/net/bigeon/gclc/socket/PluggableConsoleInput.java b/gclc-socket/src/main/java/net/bigeon/gclc/socket/PluggableConsoleInput.java index e0daa7e..ebcb181 100644 --- a/gclc-socket/src/main/java/net/bigeon/gclc/socket/PluggableConsoleInput.java +++ b/gclc-socket/src/main/java/net/bigeon/gclc/socket/PluggableConsoleInput.java @@ -54,9 +54,12 @@ import net.bigeon.gclc.utils.ReadingRunnable; /** A console input where the stream can be plugged. *

* This pluggable console input accepts an input and output to be connected to - * it. The connexion cannot be concurrent, which mean that any connected stream + * it. The connection cannot be concurrent, which mean that any connected stream * must be disconnected before a new call to * {@link #connect(InputStream, PrintStream)} is done. + *

+ * On connection, if the prompt is in wait, this console input will provide the + * prompting message. * * @author Emmanuel Bigeon */ public final class PluggableConsoleInput implements ConsoleInput { @@ -109,15 +112,15 @@ public final class PluggableConsoleInput implements ConsoleInput { throws IOException { synchronized (connexionLock) { if (connected) { + // Cannot be connected to several sources throw new IOException("Input already connected to an input stream"); //$NON-NLS-1$ } - output = out; if (prompting) { + // print the hint, to indicate we are waiting for a user input. out.print(hint); out.flush(); } - final InputStreamReader streamReader = new InputStreamReader(stream, StandardCharsets.UTF_8); final BufferedReader reader = new BufferedReader(streamReader); @@ -189,19 +192,11 @@ public final class PluggableConsoleInput implements ConsoleInput { if (closed) { throw new IOException(); } - prompting = true; - hint = message; - synchronized (connexionLock) { - hint = message; - if (connected) { - output.print(message); - output.flush(); - } - } - + preparePrompt(message); String res = null; while (res == null && !interrupted) { try { + // Wait for a message or a connection with message res = waitMessageOrConnexion(TIMEOUT, TIMEOUT / TENTH); } catch (final InterruptedException e) { LOGGER.log(Level.FINE, "Interruption of thread", e); //$NON-NLS-1$ @@ -215,6 +210,22 @@ public final class PluggableConsoleInput implements ConsoleInput { return res; } + /** Prepare the new prompt. + * + * @param message the message to indicate request of prompt. */ + private void preparePrompt(final String message) { + prompting = true; + // hold the message + synchronized (connexionLock) { + hint = message; + if (connected) { + // print the message + output.print(message); + output.flush(); + } + } + } + /* (non-Javadoc) * @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String, long) */ @Override @@ -222,15 +233,7 @@ public final class PluggableConsoleInput implements ConsoleInput { if (closed) { throw new IOException(); } - prompting = true; - synchronized (connexionLock) { - hint = message; - if (connected) { - output.print(message); - output.flush(); - } - } - + preparePrompt(message); String res = null; final long tic = System.currentTimeMillis(); long time = System.currentTimeMillis() - tic; @@ -250,11 +253,16 @@ public final class PluggableConsoleInput implements ConsoleInput { return res; } + /* (non-Javadoc) + * @see net.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */ @Override - public void setPrompt(String prompt) { + public void setPrompt(final String prompt) { setPrompt(new ConstantString(prompt)); } + /* (non-Javadoc) + * @see net.bigeon.gclc.manager.ConsoleInput#setPrompt(net.bigeon.gclc.tools. + * StringProvider) */ @Override public void setPrompt(final StringProvider prompt) { this.prompt = prompt; diff --git a/gclc.system/src/main/java/net/bigeon/gclc/system/ExecSystemCommand.java b/gclc.system/src/main/java/net/bigeon/gclc/system/ExecSystemCommand.java index 335ac8d..d4a1b03 100644 --- a/gclc.system/src/main/java/net/bigeon/gclc/system/ExecSystemCommand.java +++ b/gclc.system/src/main/java/net/bigeon/gclc/system/ExecSystemCommand.java @@ -51,7 +51,11 @@ import net.bigeon.gclc.exception.CommandRunExceptionType; import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleOutput; -/** A command that will execute a system command. +/** A command that will execute system commands. + *

+ * Note that this class may induce security issues in the code as it can execute + * arbitrary system commands. It is recommended that it be used only in private + * applications or application used by aware parties. * * @author Emmanuel Bigeon */ public class ExecSystemCommand extends Command { @@ -91,10 +95,11 @@ public class ExecSystemCommand extends Command { } catch (final IOException e2) { throw new CommandRunException("Unable to run process", e2); } - + // Stream forwarding to the application's console final InputStream is = proc.getInputStream(); final Thread th = new Thread(new Runnable() { - + /* (non-Javadoc) + * @see java.lang.Runnable#run() */ @Override public void run() { try { @@ -109,10 +114,11 @@ public class ExecSystemCommand extends Command { }); th.start(); in.setPrompt(""); //$NON-NLS-1$ + // Forwar console input to the process. final OutputStream os = proc.getOutputStream(); try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os))) { while (th.isAlive()) { - String user = in.prompt(); + final String user = in.prompt(); if (!user.isEmpty()) { writer.write(user + EOL); } @@ -123,14 +129,16 @@ public class ExecSystemCommand extends Command { } } - /** @param is the input stream + /** Read the input until its end. + * + * @param is the input stream * @throws CommandRunException if the manager was closed while writing the * stream */ protected void readToEnd(final ConsoleOutput out, final InputStream is) throws CommandRunException { int c; try { - while ((c = is.read()) != -1) { + while ((c = is.read()) >= 0) { printCharacter(out, (char) c); } } catch (final IOException e) { @@ -143,7 +151,7 @@ public class ExecSystemCommand extends Command { * @param out the console * @param c the character * @throws CommandRunException if the console failed to print the character. */ - private void printCharacter(final ConsoleOutput out, char c) + private static void printCharacter(final ConsoleOutput out, final char c) throws CommandRunException { try { out.print(Character.toString(c));