getConnected();
+
+ /** Test if a connection is active.
+ *
+ * @param id the connexion id
+ * @return if the connection is active. */
+ boolean isConnected(String id);
+
+ /** Add a lock on the disconnection.
+ *
+ * This lock will not prevent calls to
+ * {@link #disconnect(String)}. It will however stop them from completing
+ * after the effective disconnection of the specified connection.
+ *
+ * Calls to {@link #releaseDisconnexionLock(String)} remove a lock (at a
+ * pace of one for one).
+ *
+ * @param id the connexion id */
+ void lockDisconnexion(String id);
+
+ /** Release one lock on a disconnection
+ *
+ * @param id the connexion being released. */
+ void releaseDisconnexionLock(String id);
+
+ /** Wait for calls to {@link #disconnect(String)}
+ *
+ * @param id the connexion id
+ * @throws InterruptedException if the wait was interrupted. */
+ void waitDisconnexion(String id) throws InterruptedException;
+}
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
deleted file mode 100644
index a79e5ae..0000000
--- a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java
+++ /dev/null
@@ -1,133 +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.ConsoleRunnable.java
- * Created on: Jun 1, 2016
- */
-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.
- *
- * @author Emmanuel Bigeon */
-public class ConsoleRunnable implements Runnable {
-
- /** The wait timeout */
- private static final long TIMEOUT = 100;
- /** The logger */
- private static final Logger LOGGER = Logger
- .getLogger(ConsoleRunnable.class.getName());
- /** The actual application */
- private final ConsoleApplication app;
- /** The synchro object */
- private final Object lock = new Object();
- /** the state of this runnable */
- private boolean running = true;
- /** If a start is required */
- private boolean startReq;
-
- /** @param app the application */
- public ConsoleRunnable(ConsoleApplication app) {
- super();
- this.app = app;
- }
-
- /* (non-Javadoc)
- * @see java.lang.Runnable#run() */
- @Override
- public void run() {
- while (running) {
- synchronized (lock) {
- while (running && !startReq) {
- try {
- lock.wait(TIMEOUT);
- } catch (InterruptedException e) {
- LOGGER.log(Level.SEVERE,
- "Console application runnable interrupted wildly!", //$NON-NLS-1$
- e);
- return;
- }
- }
- startReq = false;
- if (!running) {
- return;
- }
- lock.notify();
- }
- app.start();
- }
- }
-
- /** Stop the application (it will finish its current operation) */
- public void stop() {
- app.exit();
- }
-
- /** @return if the application is running */
- 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;
- }
- }
-
- /** Request a restart of application */
- public void restart() {
- synchronized (lock) {
- startReq = true;
- lock.notify();
- try {
- lock.wait(TIMEOUT);
- } catch (InterruptedException e) {
- LOGGER.log(Level.SEVERE, "Restart wait interrupted!", e); //$NON-NLS-1$
- }
- }
- }
-}
diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/DConnexionManager.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/DConnexionManager.java
new file mode 100644
index 0000000..28a1d2f
--- /dev/null
+++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/DConnexionManager.java
@@ -0,0 +1,173 @@
+/*
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
+ *
+ * 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.DConnexionManager.java
+ * Created on: Nov 18, 2017
+ */
+package fr.bigeon.gclc.socket;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/** Default implementation of the {@link ConnexionManager}.
+ *
+ * @author Emmanuel Bigeon
+ * @param the connected objects */
+public final class DConnexionManager implements ConnexionManager {
+
+ /** Class logger. */
+ private static final Logger LOGGER = Logger
+ .getLogger(DConnexionManager.class.getName());
+ /** The connected objects. */
+ private final Map connecteds = new HashMap<>();
+ /** The locks for the connexions. */
+ private final Map locks = new HashMap<>();
+ /** The counter for the disconnexion locks. */
+ private final Map counters = new HashMap<>();
+ /** The lock for modification of {@link #counters}. */
+ private final Object counterLock = new Object();
+ /** The count of connexions. */
+ private int count = 0;
+
+ /** Default.constructor. */
+ public DConnexionManager() {
+ //
+ }
+ /* (non-Javadoc)
+ * @see
+ * fr.bigeon.gclc.socket.ConnexionManager#addConnexion(java.lang.Object) */
+ @Override
+ public String addConnexion(final T handle) {
+ final String newID = newID();
+ connecteds.put(newID, handle);
+ locks.put(newID, new Object());
+ counters.put(newID, Integer.valueOf(0));
+ return newID;
+ }
+
+ /* (non-Javadoc)
+ * @see
+ * fr.bigeon.gclc.socket.ConnexionManager#disconnect(java.lang.String) */
+ @Override
+ public T disconnect(final String id) {
+ if (connecteds.containsKey(id)) {
+ final T disc = connecteds.remove(id);
+ final Object lock = locks.get(id);
+ synchronized (lock) {
+ lock.notifyAll();
+ }
+ synchronized (counterLock) {
+ while (counters.get(id).intValue() > 0) {
+ try {
+ counterLock.wait();
+ } catch (final InterruptedException e) {
+ LOGGER.log(Level.FINE, "Interruption of thread", e); //$NON-NLS-1$
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ return disc;
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.socket.ConnexionManager#get(java.lang.String) */
+ @Override
+ public T get(final String id) {
+ return connecteds.get(id);
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.socket.ConnexionManager#getConnected() */
+ @Override
+ public Collection getConnected() {
+ return connecteds.keySet();
+ }
+
+ /* (non-Javadoc)
+ * @see
+ * fr.bigeon.gclc.socket.ConnexionManager#isConnected(java.lang.String) */
+ @Override
+ public boolean isConnected(final String id) {
+ return connecteds.containsKey(id);
+ }
+
+ /* (non-Javadoc)
+ * @see
+ * fr.bigeon.gclc.socket.ConnexionManager#lockDisconnexion(java.lang.String) */
+ @Override
+ public void lockDisconnexion(final String id) {
+ if (!connecteds.containsKey(id)) {
+ return;
+ }
+ synchronized (counterLock) {
+ counters.put(id, Integer.valueOf(counters.get(id).intValue() + 1));
+ }
+ }
+
+ /** Get a new identifier for connexion.
+ *
+ * @return a new ID */
+ private String newID() {
+ return "Client " + count++; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see
+ * fr.bigeon.gclc.socket.ConnexionManager#releaseDisconnexionLock(java.lang.
+ * String) */
+ @Override
+ public void releaseDisconnexionLock(final String id) {
+ synchronized (counterLock) {
+ counters.put(id, Integer
+ .valueOf(Math.max(counters.get(id).intValue() - 1, 0)));
+ counterLock.notifyAll();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see
+ * fr.bigeon.gclc.socket.ConnexionManager#waitDisconnexion(java.lang.String) */
+ @Override
+ public void waitDisconnexion(final String id) throws InterruptedException {
+ final Object lock = locks.get(id);
+ while (connecteds.containsKey(id)) {
+ synchronized (lock) {
+ lock.wait();
+ }
+ }
+ }
+}
diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/PluggableConsoleInput.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/PluggableConsoleInput.java
new file mode 100644
index 0000000..506a0ae
--- /dev/null
+++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/PluggableConsoleInput.java
@@ -0,0 +1,279 @@
+/*
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
+ *
+ * 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.PlugableConsoleInput.java
+ * Created on: Nov 18, 2017
+ */
+package fr.bigeon.gclc.socket;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import fr.bigeon.gclc.manager.ConsoleInput;
+import fr.bigeon.gclc.manager.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
+ * must be disconnected before a new call to
+ * {@link #connect(InputStream, PrintStream)} is done.
+ *
+ * @author Emmanuel Bigeon */
+public final class PluggableConsoleInput implements ConsoleInput {
+ /** The ten constant. */
+ private static final int TENTH = 10;
+ /** Class logger. */
+ private static final Logger LOGGER = Logger
+ .getLogger(PluggableConsoleInput.class.getName());
+ /** The default time out. */
+ private static final long TIMEOUT = 100;
+ /** The prompting. */
+ private boolean prompting = false;
+ /** If the element is closed. */
+ private boolean closed = false;
+ /** The default prompt. */
+ private String prompt = "> "; //$NON-NLS-1$
+ /** If the input is plugged or buffering. */
+ private boolean connected = false;
+ /** The current connexion (if any). */
+ private ReadingRunnable connexion;
+ /** The interrupted status for prompts. */
+ private boolean interrupted = false;
+ /** The last hint hint. */
+ private String hint;
+ /** The output for hints. */
+ private PrintStream output;
+
+ // Locks
+ /** The lock for connexion and disconnexion of actual streams. */
+ private final Object connexionLock = new Object();
+
+ /** Create the pluggable console input. */
+ public PluggableConsoleInput() {
+ // do nothing
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#close() */
+ @Override
+ public void close() {
+ closed = true;
+ }
+
+ /** Connect the given input stream to the input and output to the hints
+ * writing.
+ *
+ * @param stream the input stream
+ * @param out the output for hints.
+ * @throws IOException if the input is already connected. */
+ public void connect(final InputStream stream,
+ final PrintStream out) throws IOException {
+ synchronized (connexionLock) {
+ if (connected) {
+ throw new IOException(
+ "Input already connected to an input stream"); //$NON-NLS-1$
+ }
+
+ output = out;
+ if (prompting) {
+ out.print(hint);
+ out.flush();
+ }
+
+ final InputStreamReader streamReader = new InputStreamReader(
+ stream, StandardCharsets.UTF_8);
+ final BufferedReader reader = new BufferedReader(streamReader);
+ connexion = new ReadingRunnable(reader);
+ final Thread th = new Thread(connexion, "GCLC Socket - Read input"); //$NON-NLS-1$
+ th.start();
+ connexionLock.notifyAll();
+ connected = true;
+ }
+ }
+
+ /** Disconnect the current input and hint output. */
+ public synchronized void disconnect() {
+ synchronized (connexionLock) {
+ if (!connected) {
+ return;
+ }
+ connected = false;
+ connexion.setRunning(false);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#getPrompt() */
+ @Override
+ public String getPrompt() {
+ return prompt;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#interruptPrompt() */
+ @Override
+ public void interruptPrompt() {
+ interrupted = true;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#isClosed() */
+ @Override
+ public boolean isClosed() {
+ return closed;
+ }
+
+ /** Test if a prompt is occuring.
+ *
+ * @return the prompting */
+ public boolean isPrompting() {
+ return prompting;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt() */
+ @Override
+ public String prompt() throws IOException {
+ return prompt(prompt);
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */
+ @Override
+ public String prompt(final long timeout) throws IOException {
+ return prompt(prompt, timeout);
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String) */
+ @Override
+ public String prompt(final String message) throws IOException {
+ if (closed) {
+ throw new IOException();
+ }
+ prompting = true;
+ hint = message;
+ synchronized (connexionLock) {
+ hint = message;
+ if (connected) {
+ output.print(message);
+ output.flush();
+ }
+ }
+
+ String res = null;
+ while (res == null && !interrupted) {
+ try {
+ res = waitMessageOrConnexion(TIMEOUT, TIMEOUT / TENTH);
+ } catch (final InterruptedException e) {
+ LOGGER.log(Level.FINE, "Interruption of thread", e); //$NON-NLS-1$
+ Thread.currentThread().interrupt();
+ }
+ if (closed) {
+ throw new IOException();
+ }
+ }
+ prompting = false;
+ return res;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String,
+ * long) */
+ @Override
+ public String prompt(final String message,
+ final long timeout) throws IOException {
+ if (closed) {
+ throw new IOException();
+ }
+ prompting = true;
+ synchronized (connexionLock) {
+ hint = message;
+ if (connected) {
+ output.print(message);
+ output.flush();
+ }
+ }
+
+ String res = null;
+ final long tic = System.currentTimeMillis();
+ long time = System.currentTimeMillis() - tic;
+ while (res == null && !interrupted && time < timeout) {
+ try {
+ res = waitMessageOrConnexion(timeout - time,
+ (timeout - time) / TENTH);
+ } catch (final InterruptedException e) {
+ LOGGER.log(Level.FINE, "Interruption of thread", e); //$NON-NLS-1$
+ Thread.currentThread().interrupt();
+ }
+ time = System.currentTimeMillis() - tic;
+ if (closed) {
+ throw new IOException();
+ }
+ }
+ prompting = false;
+ return res;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */
+ @Override
+ public void setPrompt(final String prompt) {
+ this.prompt = prompt;
+ }
+
+ /** Wait for a hint or connexion.
+ *
+ * @param messageTimeout the timeout on the current connexion hint waiting
+ * @param connexionTimeout the timeout on the new connexion wait
+ * @return the hint, or null if not connected or timed out.
+ * @throws IOException if the reading failed.
+ * @throws InterruptedException if the wait was interrupted */
+ private String waitMessageOrConnexion(final long messageTimeout,
+ final long connexionTimeout) throws IOException,
+ InterruptedException {
+ synchronized (connexionLock) {
+ if (connected) {
+ return connexion.getNextMessage(messageTimeout);
+ }
+ connexionLock.wait(connexionTimeout);
+ }
+ return null;
+ }
+}
diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/PluggableConsoleOutput.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/PluggableConsoleOutput.java
new file mode 100644
index 0000000..9b57af0
--- /dev/null
+++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/PluggableConsoleOutput.java
@@ -0,0 +1,126 @@
+/*
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
+ *
+ * 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.PluggableConsoleOutput.java
+ * Created on: Nov 18, 2017
+ */
+package fr.bigeon.gclc.socket;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import fr.bigeon.gclc.manager.ConsoleOutput;
+
+/** An output that can be hotplugged to an actual output.
+ *
+ * @author Emmanuel Bigeon */
+public final class PluggableConsoleOutput implements ConsoleOutput {
+
+ /** The actual output. */
+ private PrintStream out;
+ /** The buffered messages. */
+ private final Deque messages = new ArrayDeque<>();
+ /** If this output is closed. */
+ private boolean closed = false;
+
+ /** Default constructor. */
+ public PluggableConsoleOutput() {
+ //
+ }
+ /* (non-Javadoc)
+ * @see java.lang.AutoCloseable#close() */
+ @Override
+ public void close() {
+ closed = true;
+ }
+
+ /** Set the output to write to.
+ *
+ * @param output the output to set */
+ public synchronized void connect(final PrintStream output) {
+ out = output;
+ while (!messages.isEmpty()) {
+ output.print(messages.pop());
+ }
+ }
+
+ /** Disconnects the output. */
+ public synchronized void disconnect() {
+ out = null;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleOutput#isClosed() */
+ @Override
+ public boolean isClosed() {
+ return closed;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleOutput#print(java.lang.String) */
+ @Override
+ public synchronized void print(final String text) throws IOException {
+ if (closed) {
+ throw new IOException("Closed output"); //$NON-NLS-1$
+ }
+ if (out == null) {
+ messages.add(text);
+ } else {
+ out.print(text);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleOutput#println() */
+ @Override
+ public synchronized void println() throws IOException {
+ if (closed) {
+ throw new IOException("Closed output"); //$NON-NLS-1$
+ }
+ if (out == null) {
+ messages.add("\n"); //$NON-NLS-1$
+ } else {
+ out.println();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.manager.ConsoleOutput#println(java.lang.String) */
+ @Override
+ public synchronized void println(final String message) throws IOException {
+ print(message);
+ println();
+ }
+
+}
diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/RemoteDisconnectCommand.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/RemoteDisconnectCommand.java
new file mode 100644
index 0000000..312dd53
--- /dev/null
+++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/RemoteDisconnectCommand.java
@@ -0,0 +1,132 @@
+/*
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
+ *
+ * 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.RemoteDisconnectCommand.java
+ * Created on: Nov 18, 2017
+ */
+package fr.bigeon.gclc.socket;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Collection;
+
+import fr.bigeon.gclc.command.Command;
+import fr.bigeon.gclc.exception.CommandRunException;
+import fr.bigeon.gclc.exception.CommandRunExceptionType;
+import fr.bigeon.gclc.manager.ConsoleInput;
+import fr.bigeon.gclc.manager.ConsoleOutput;
+
+/** A {@link Command} to disconnect elements from a {@link ConnexionManager}.
+ *
+ * @author Emmanuel Bigeon
+ * @param the type of connected object */
+public final class RemoteDisconnectCommand extends Command {
+
+ /** The connexion manager. */
+ private final ConnexionManager manager;
+ /** If all connexion should be disconnected when no argument have been
+ * specified. */
+ private final boolean all;
+
+ /** Create the disconnection command.
+ *
+ * @param name the command name
+ * @param manager the manager
+ * @param all if all elements should be disconnected when no argument is
+ * provided */
+ public RemoteDisconnectCommand(final String name,
+ final ConnexionManager manager, final boolean all) {
+ super(name);
+ this.manager = manager;
+ this.all = all;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
+ * ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
+ * java.lang.String[]) */
+ @Override
+ public void execute(final ConsoleOutput out, final ConsoleInput in,
+ final String... args) throws CommandRunException {
+ if (args.length == 0 && all) {
+ final Collection coll = manager.getConnected();
+ for (final String string : coll) {
+ manager.disconnect(string);
+ }
+ }
+ for (final String string : args) {
+ if (manager.isConnected(string)) {
+ manager.disconnect(string);
+ } else {
+ print(out, MessageFormat
+ .format("[WARNING] {0} is not connected", string)); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /** Print a message if the output is defined.
+ *
+ * @param out the output
+ * @param string the message
+ * @throws CommandRunException if the output exists but cannot be printed
+ * to */
+ private void print(final ConsoleOutput out,
+ final String string) throws CommandRunException {
+ if (out != null) {
+ try {
+ out.println(string);
+ } catch (final IOException e) {
+ throw new CommandRunException(
+ CommandRunExceptionType.INTERACTION,
+ "Unable to print to existing output", e, this); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.ICommand#tip() */
+ @Override
+ public String tip() {
+ return "Close a connexion."; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.Command#usageDetail() */
+ @SuppressWarnings("nls")
+ @Override
+ protected String usageDetail() {
+ return MessageFormat.format(
+ " If arguments are provided the corresponding connexions are closed, " +
+ "otherwise{0}{1} are.",
+ System.lineSeparator(), all ? "all connexions" : "none");
+ }
+}
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 817561b..20ce92c 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
@@ -1,10 +1,7 @@
/*
- * Copyright E. Bigeon (2014)
- *
- * emmanuel@bigeon.fr
- *
- * This software is a computer program whose purpose is to
- * Socket implementation of GCLC.
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
@@ -34,24 +31,17 @@
*/
package fr.bigeon.gclc.socket;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
+import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
-import java.nio.charset.Charset;
import java.util.logging.Level;
import java.util.logging.Logger;
import fr.bigeon.gclc.ConsoleApplication;
-import fr.bigeon.gclc.manager.PipedConsoleInput;
-import fr.bigeon.gclc.manager.PipedConsoleOutput;
-import fr.bigeon.gclc.manager.ReadingRunnable;
-/** This is a socket communicating console consoleManager
+/** This is a socket communicating console consoleManager.
*
* To use this application, the following flow should be used:
*
@@ -69,238 +59,60 @@ import fr.bigeon.gclc.manager.ReadingRunnable;
* end of the execution.
*
* @author Emmanuel Bigeon */
-public class SocketConsoleApplicationShell implements Runnable, AutoCloseable {
+public final class SocketConsoleApplicationShell implements Runnable {
- /** The runnable to forward output of application to socket.
- *
- * @author Emmanuel Bigeon */
- private final class OutputForwardRunnable implements Runnable {
- /**
- *
- */
- private final PrintWriter writer;
- /**
- *
- */
- private final Socket socket;
-
- /** @param writer the writer
- * @param socket the socket */
- protected OutputForwardRunnable(final PrintWriter writer, final Socket socket) {
- this.writer = writer;
- this.socket = socket;
- }
-
- @SuppressWarnings("synthetic-access")
- @Override
- public void run() {
- try {
- while (!socket.isClosed()) {
- while (!socket.isClosed() && !output.available()) {
- waitASec();
- }
- if (socket.isClosed()) {
- return;
- }
- final String m = output.readNextLine();
- writer.println(m);
- }
- } catch (final IOException e) {
- LOGGER.log(Level.SEVERE, "Unexpected problem in manager", //$NON-NLS-1$
- e);
- }
- }
-
- }
-
- /** The end of line character */
- protected static final String EOL = "\n"; //$NON-NLS-1$
- /** The class logger */
+ /** The class logger. */
private static final Logger LOGGER = Logger
.getLogger(SocketConsoleApplicationShell.class.getName());
- /** Time of wait */
- protected static final long ONE_TENTH_OF_SECOND = 100;
- /** The listening port */
+ /** The listening port. */
private final int port;
- /** The application */
- private ConsoleApplication app;
- /** The session closing command */
- private final String close;
- /** The running status */
+ /** The running status. */
private boolean running;
+ /** The socket console interface. */
+ private SocketConsoleInterface sci;
+ /** The remote disconnection command. */
+ private ConnexionManager rdc;
+ /** The application. */
+ private ConsoleApplication app;
- /** The auto close flag. if this is true, every request closes the session
- * after its call */
- private final boolean autoClose;
- /** The server socket */
+ /** The server socket. */
private ServerSocket serverSocket;
- /** The application shutdown string */
- private final String applicationShutdown;
- /** The charset for the communication. */
- private final Charset charset;
- private final PipedConsoleOutput output;
- private final PipedConsoleInput input;
+ /** THe server address. */
+ private final InetAddress addr;
/** Create a socket application shell which will listen on the given port
- * and auto close session after one instruction
+ * and network interface.
*
- * @param port the port to listen to
- * @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
- * @throws IOException if the manager could not be created */
- public SocketConsoleApplicationShell(final int port, final boolean autoClose,
- final String applicationShutdown, final Charset charset) throws IOException {
+ * @param port the part
+ * @param addr the inet address */
+ public SocketConsoleApplicationShell(final int port,
+ final InetAddress addr) {
+ super();
this.port = port;
- this.autoClose = autoClose;
- this.applicationShutdown = applicationShutdown;
- close = autoClose ? null : "close"; //$NON-NLS-1$
- this.charset = charset;
- //
- output = new PipedConsoleOutput();
- input = new PipedConsoleInput();
+ this.addr = addr;
}
- /** Create a socket application shell which will listen on the given port
- * and close session upon the provided string reception by client
+ /** Wait for the identified connection to disconnect.
*
- * @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
- * @throws IOException if the manager could not be created */
- public SocketConsoleApplicationShell(final int port, final String close,
- final String applicationShutdown, final Charset charset) throws IOException {
- this.port = port;
- this.close = close;
- this.applicationShutdown = applicationShutdown;
- autoClose = false;
- this.charset = charset;
- //
- output = new PipedConsoleOutput();
- input = new PipedConsoleInput();
- }
-
- /* (non-Javadoc)
- * @see java.lang.AutoCloseable#close() */
- @Override
- public void close() throws IOException {
- input.close();
- output.close();
- }
-
- /** Close the console manager after writing the application shutdown
- * command.
- *
- * @param appThNext the thread containing the application
- * @throws IOException if the typyng or closing failed */
- private void closeManager(final Thread appThNext) throws IOException {
- input.type(applicationShutdown);
- try {
- appThNext.join(ONE_TENTH_OF_SECOND);
- } catch (final InterruptedException e) {
- LOGGER.warning("Application thread was interrupted!"); //$NON-NLS-1$
- LOGGER.log(Level.FINE,
- "Application thread was interrupted while closing", //$NON-NLS-1$
- e);
- }
- close();
- }
-
- /** active communication between server and client
- *
- * @param socket the socket
- * @param writer the writer to the application
- * @param in the input from the client
- * @throws IOException if the communication failed */
- private void communicate(final Socket socket, final PrintWriter writer,
- final BufferedReader in) throws IOException {
- final OutputForwardRunnable cc = new OutputForwardRunnable(writer, socket);
- final Thread th = new Thread(cc, "ClientComm"); //$NON-NLS-1$
- th.start();
- if (autoClose) {
- communicateOnce(in);
- } else {
- communicateLoop(in);
- }
- }
-
- /** @param in the input from the client
- * @throws IOException if the communication failed */
- private void communicateLoop(final BufferedReader in) throws IOException {
- final ReadingRunnable reading = new ReadingRunnable(in);
- final Thread th = new Thread(reading, "gclcToApp"); //$NON-NLS-1$
- th.start();
- while (app.isRunning() && communicationContent(reading)) {
- // keep on going
- }
- doEndCommunication(reading);
- }
-
- /** @param in the input from the client
- * @throws IOException if the communication failed */
- private void communicateOnce(final BufferedReader in) throws IOException {
- final ReadingRunnable reading = new ReadingRunnable(in);
- final Thread th = new Thread(reading, "gclcToApp"); //$NON-NLS-1$
- th.start();
- communicationContent(reading);
- doEndCommunication(reading);
- }
-
- /** @param reading the reading
- * @return if the communication should be stopped.
- * @throws IOException if the reading failed */
- private boolean communicationContent(final ReadingRunnable reading) throws IOException {
- try {
- while (app.isRunning() && !reading.hasMessage()) {
- synchronized (this) {
- waitASec();
- }
+ * @param id the connexion id. */
+ private void awaitDisconnexion(final String id) {
+ while (rdc.isConnected(id)) {
+ try {
+ rdc.waitDisconnexion(id);
+ } catch (final InterruptedException e) {
+ LOGGER.log(Level.SEVERE, "Unexpected interruption", e); //$NON-NLS-1$
+ Thread.currentThread().interrupt();
}
- } catch (final IOException e) {
- LOGGER.warning("Client seems dead. Closing communication"); //$NON-NLS-1$
- LOGGER.log(Level.FINE, "Wait on message from client failed", e); //$NON-NLS-1$
- return false;
}
- if (!app.isRunning()) {
- return false;
- }
- final String ln = reading.getMessage();
- if (ln.equals(close)) {
- return false;
- }
- // Pass command to application
- input.type(ln);
- return true;
+ sci.disconnect();
}
- /** @param reading the reading runnable
- * @throws IOException if the end of communication failed */
- private void doEndCommunication(final ReadingRunnable reading) throws IOException {
- reading.setRunning(false);
- final Thread wait = output.getWaitForDelivery("Bye."); //$NON-NLS-1$
- output.println("Bye."); //$NON-NLS-1$
- try {
- wait.join();
- } catch (final InterruptedException e) {
- LOGGER.warning("The Bye wait was interrupted."); //$NON-NLS-1$
- LOGGER.log(Level.FINE, "An interruption occured", e); //$NON-NLS-1$
- }
- }
-
- /**
- * @return the input
+ /** If the port provided was 0, this allows to get the actual port.
+ * @return the local port
+ * @see java.net.ServerSocket#getLocalPort()
*/
- public PipedConsoleInput getInput() {
- return input;
- }
-
- /**
- * @return the output
- */
- public PipedConsoleOutput getOutput() {
- return output;
+ public int getLocalPort() {
+ return serverSocket.getLocalPort();
}
/* (non-Javadoc)
@@ -308,7 +120,8 @@ public class SocketConsoleApplicationShell implements Runnable, AutoCloseable {
@Override
public void run() {
// Create the server
- try (ServerSocket actualServerSocket = new ServerSocket(port)) {
+ try (ServerSocket actualServerSocket = new ServerSocket(port, 1,
+ addr)) {
serverSocket = actualServerSocket;
running = true;
// Create the streams
@@ -320,33 +133,20 @@ public class SocketConsoleApplicationShell implements Runnable, AutoCloseable {
}
}
- /** @throws IOException if the communication with the client failed */
+ /** Acctually run the server loop on connexion.
+ *
+ * @throws IOException if the communication with the client failed */
private void runSokectServer() throws IOException {
- final ConsoleRunnable runnable = new ConsoleRunnable(app);
- final Thread appThNext = new Thread(runnable, "gclc-ctrl"); //$NON-NLS-1$
- appThNext.start();
- while (running) {
+ while (running && app.isRunning()) {
LOGGER.info("Waiting client"); //$NON-NLS-1$
- try (Socket clientSocket = serverSocket.accept();
- PrintWriter out = new PrintWriter(new OutputStreamWriter(
- clientSocket.getOutputStream(), charset), true);
- InputStreamReader isr = new InputStreamReader(
- clientSocket.getInputStream(), charset);
- BufferedReader in = new BufferedReader(isr);) {
- // this is not threaded to avoid several clients at the same
- // time
- LOGGER.info("Opening client"); //$NON-NLS-1$
+ try (Socket clientSocket = serverSocket.accept();) {
- // Initiate application
- if (!runnable.isApplicationRunning()) {
- LOGGER.info("Start application"); //$NON-NLS-1$
- startApplication(runnable);
- } else {
- LOGGER.info("Reconnect to application"); //$NON-NLS-1$
- out.println("Reconnected"); //$NON-NLS-1$
- out.println(input.getPrompt());
- }
- communicate(clientSocket, out, in);
+ sci.connect(clientSocket);
+
+ final String id = rdc.addConnexion(clientSocket);
+ rdc.lockDisconnexion(id);
+ awaitDisconnexion(id);
+ rdc.releaseDisconnexionLock(id);
} catch (final SocketException e) {
LOGGER.log(Level.INFO, "Socket closed"); //$NON-NLS-1$
LOGGER.log(Level.FINE,
@@ -357,33 +157,30 @@ public class SocketConsoleApplicationShell implements Runnable, AutoCloseable {
}
LOGGER.info("Closing client"); //$NON-NLS-1$
}
- runnable.setRunning(false);
- try {
- closeManager(appThNext);
- } catch (final IOException e) {
- LOGGER.warning("Unable to close application correctly"); //$NON-NLS-1$
- LOGGER.log(Level.FINE, "Application closing caused an exception", //$NON-NLS-1$
- e);
- }
LOGGER.info("Closing Server"); //$NON-NLS-1$
}
- /** @param app the application to set */
- public synchronized void setApplication(final ConsoleApplication app) {
+ /** Set the application.
+ *
+ * If the application is closed, the server will also close.
+ *
+ * @param app the app to set */
+ public void setApplication(final ConsoleApplication app) {
this.app = app;
}
- /** @param runnable the runnable */
- private void startApplication(final ConsoleRunnable runnable) {
- runnable.restart();
- synchronized (this) {
- try {
- wait(ONE_TENTH_OF_SECOND);
- } catch (final InterruptedException e) {
- LOGGER.log(Level.SEVERE, "Interruption in application start", //$NON-NLS-1$
- e);
- }
- }
+ /** Set the connexion manager.
+ *
+ * @param rdc the rdc to set */
+ public void setConnexionManager(final ConnexionManager rdc) {
+ this.rdc = rdc;
+ }
+
+ /** Set the socket console interface.
+ *
+ * @param sci the console interface to set */
+ public void setInterface(final SocketConsoleInterface sci) {
+ this.sci = sci;
}
/** This method will request the server to stop.
@@ -397,19 +194,5 @@ public class SocketConsoleApplicationShell implements Runnable, AutoCloseable {
} catch (final IOException e) {
LOGGER.log(Level.SEVERE, "Exception in closing socket server", e); //$NON-NLS-1$
}
- app.exit();
- }
-
- /** a method to wait some time */
- protected void waitASec() {
- try {
- synchronized (this) {
- wait(ONE_TENTH_OF_SECOND);
- }
- } catch (final InterruptedException e) {
- LOGGER.log(Level.SEVERE, "Interrupted wait", //$NON-NLS-1$
- e);
- return;
- }
}
}
diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleInterface.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleInterface.java
new file mode 100644
index 0000000..2aff21d
--- /dev/null
+++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleInterface.java
@@ -0,0 +1,82 @@
+/*
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
+ *
+ * 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.SocketConsoleInterface.java
+ * Created on: Nov 18, 2017
+ */
+package fr.bigeon.gclc.socket;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+
+/** The interface for socket based access to console application.
+ *
+ * @author Emmanuel Bigeon */
+public final class SocketConsoleInterface {
+
+ /** The application's input. */
+ private final PluggableConsoleInput input;
+ /** The application's output. */
+ private final PluggableConsoleOutput output;
+
+ /** Create the interfacing object.
+ *
+ * @param input the input
+ * @param output the output */
+ public SocketConsoleInterface(final PluggableConsoleInput input,
+ final PluggableConsoleOutput output) {
+ super();
+ this.input = input;
+ this.output = output;
+ }
+
+ /** Connect the application's input and outputs to the socket's.
+ *
+ * @param socket the socket
+ * @throws IOException if the connection failed */
+ public void connect(final Socket socket) throws IOException {
+ final PrintStream printStream = new PrintStream(
+ socket.getOutputStream(), true,
+ StandardCharsets.UTF_8.name());
+ output.connect(printStream);
+ input.connect(socket.getInputStream(),
+ printStream);
+ }
+
+ /** Disconnect the input and output of the application from the socket's. */
+ public void disconnect() {
+ input.disconnect();
+ output.disconnect();
+ }
+}
diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/package-info.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/package-info.java
new file mode 100644
index 0000000..a0f3219
--- /dev/null
+++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/package-info.java
@@ -0,0 +1,40 @@
+/*
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
+ *
+ * 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.package-info.java
+ * Created on: Nov 18, 2017
+ */
+/** This package define a framework to access
+ * {@link fr.bigeon.gclc.ConsoleApplication} through a socket.
+ *
+ * @author Emmanuel Bigeon */
+package fr.bigeon.gclc.socket;
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
deleted file mode 100644
index 323f9d0..0000000
--- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java
+++ /dev/null
@@ -1,182 +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.ConsoleRunnableTest.java
- * Created on: Jun 1, 2016
- */
-package fr.bigeon.gclc.socket;
-
-import java.io.IOException;
-
-import org.junit.Test;
-
-import fr.bigeon.gclc.manager.ConsoleInput;
-
-/** Test class for {@link ConsoleRunnable}
- *
- * @author Emmanuel Bigeon */
-@SuppressWarnings({"unused", "javadoc"})
-public class ConsoleRunnableTest {
-
- /**
- * TODO
- *
- * @author Emmanuel Bigeon */
- private static final class ConsoleManagerTestImplementation
- implements ConsoleInput {
- int i = 0;
- String[] cmds;
-
- /** @param cmds the commands to run */
- public ConsoleManagerTestImplementation(final String[] cmds) {
- super();
- this.cmds = cmds;
- }
-
- @Override
- public void close() throws IOException {
- // do nothing
- }
-
- @Override
- public String getPrompt() {
- // Not used in test
- return ""; //$NON-NLS-1$
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
- @Override
- public void interruptPrompt() {
- //
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
- @Override
- public boolean isClosed() {
- return i == cmds.length;
- }
-
- @Override
- public String prompt() {
- try {
- Thread.sleep(1000);
- } catch (final InterruptedException e) { // NOSONAR
- // do nothing
- }
- i++;
- if (i == cmds.length) {
- i = 0;
- }
- return cmds[i];
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.manager.ConsoleManager#prompt(long) */
- @Override
- public String prompt(final long timeout) throws IOException {
- return prompt();
- }
-
- @Override
- public String prompt(final String message) {
- return prompt();
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.manager.ConsoleManager#prompt(java.lang.String,
- * long) */
- @Override
- public String prompt(final String message, final long timeout) throws IOException {
- return prompt(message);
- }
-
- @Override
- public void setPrompt(final String prompt) {
- // do nothing
- }
- }
-
- /** Test method for
- * {@link fr.bigeon.gclc.socket.ConsoleRunnable#ConsoleRunnable(fr.bigeon.gclc.ConsoleApplication)}
- * . */
- @Test
- public void testConsoleRunnable() {
-// ConsoleApplication app = new ConsoleTestApplication(
-// new SystemConsoleManager());
-// ConsoleRunnable runnable = new ConsoleRunnable(app);
-
- }
-
- /** Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#stop()}. */
- @Test
- public void testRun() {
-// ConsoleApplication app = new ConsoleTestApplication(
-// new ConsoleManagerTestImplementation(
-// new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$
-// ConsoleRunnable runnable = new ConsoleRunnable(app);
-// runnable.run();
- }
-
- /** Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#run()}. */
- @Test
- public void testRunFlow() {
-// ConsoleApplication app = new ConsoleTestApplication(
-// new ConsoleManagerTestImplementation(
-// new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$
-// ConsoleRunnable runnable = new ConsoleRunnable(app);
-//
-// Thread th = new Thread(runnable);
-// th.start();
-//
-// runnable.stop();
- }
-
- /** Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#stop()}. */
- @Test
- public void testStop() {
-// ConsoleApplication app = new ConsoleTestApplication(
-// new ConsoleManagerTestImplementation(
-// new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$
-// ConsoleRunnable runnable = new ConsoleRunnable(app);
-// runnable.stop();
-// Thread th = new Thread(runnable);
-// th.start();
-// runnable.stop();
-// runnable.stop();
- }
-
-}
diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleTestApplication.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleTestApplication.java
index 1276208..8a16289 100644
--- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleTestApplication.java
+++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleTestApplication.java
@@ -1,10 +1,7 @@
/*
- * Copyright E. Bigeon (2014)
- *
- * emmanuel@bigeon.fr
- *
- * This software is a computer program whose purpose is to
- * Socket implementation of GCLC.
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
@@ -35,6 +32,8 @@
package fr.bigeon.gclc.socket;
import java.io.IOException;
+import java.net.Socket;
+import java.util.Collection;
import fr.bigeon.gclc.ConsoleApplication;
import fr.bigeon.gclc.command.Command;
@@ -53,17 +52,32 @@ public class ConsoleTestApplication {
/** Exit command */
public static final String EXIT = "exit"; //$NON-NLS-1$
- /** @param manager the manager
+ /** Create the test application.
+ *
+ * @param output the output
+ * @param input the input
+ * @param manager the manager
* @return create the application */
@SuppressWarnings("nls")
- public static ConsoleApplication create(final ConsoleOutput manager,
- final ConsoleInput input) {
+ public static ConsoleApplication create(final ConsoleOutput output,
+ final ConsoleInput input,
+ final ConnexionManager manager) {
try {
final ConsoleApplication application = new ConsoleApplication(
- manager, input,
+ output, input,
"Welcome to the test application. Type help or test.",
"See you");
- application.add(new ExitCommand(EXIT, application));
+ application.add(new ExitCommand(EXIT, application) {
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.ExitCommand#beforeExit() */
+ @Override
+ protected void beforeExit() {
+ final Collection coll = manager.getConnected();
+ for (final String string : coll) {
+ manager.disconnect(string);
+ }
+ }
+ });
application
.add(new HelpExecutor("help", application.root));
application.add(new Command("test") {
@@ -77,7 +91,7 @@ public class ConsoleTestApplication {
final ConsoleInput in,
final String... args) throws CommandRunException {
try {
- manager.println("Test command ran fine");
+ output.println("Test command ran fine");
} catch (final IOException e) {
throw new CommandRunException("manager closed", e,
this);
@@ -111,7 +125,7 @@ public class ConsoleTestApplication {
final String... args) throws CommandRunException {
try {
Thread.sleep(2000);
- manager.println("Test command ran fine");
+ output.println("Test command ran fine");
} catch (IOException | InterruptedException e) {
throw new CommandRunException("manager closed", e,
this);
@@ -133,6 +147,8 @@ public class ConsoleTestApplication {
throw new RuntimeException("Not implemented yet");
}
});
+ application.add(
+ new RemoteDisconnectCommand<>("out", manager, true));
return application;
} catch (final InvalidCommandName e) {
e.printStackTrace();
diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/PluggableConsoleInputTest.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/PluggableConsoleInputTest.java
new file mode 100644
index 0000000..cc214e1
--- /dev/null
+++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/PluggableConsoleInputTest.java
@@ -0,0 +1,200 @@
+/*
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
+ *
+ * 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.PluggableConsoleInputTest.java
+ * Created on: Nov 18, 2017
+ */
+package fr.bigeon.gclc.socket;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+
+import org.junit.Test;
+
+/**
+ *
+ * TODO
+ *
+ * @author Emmanuel Bigeon */
+public class PluggableConsoleInputTest {
+
+ /** Test method for
+ * {@link fr.bigeon.gclc.socket.PluggableConsoleInput#close()}. */
+ @Test
+ public final void testClose() {
+ final PluggableConsoleInput input = new PluggableConsoleInput();
+ assertFalse("Input should not be initially closed", input.isClosed());
+ input.close();
+ assertTrue("Close should close the input", input.isClosed());
+
+ try {
+ input.prompt();
+ fail("Closed prompt should cause an IO");
+ } catch (final IOException e) {
+ // ok
+ }
+ try {
+ input.prompt(10);
+ fail("Closed prompt should cause an IO");
+ } catch (final IOException e) {
+ // ok
+ }
+
+ final PluggableConsoleInput input2 = new PluggableConsoleInput();
+
+ final Thread th = new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ while (!input2.isPrompting()) {
+
+ }
+ try {
+ Thread.sleep(100);
+ } catch (final InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ input2.close();
+ }
+ });
+ th.start();
+ try {
+ input.prompt();
+ fail("Closed prompt should cause an IO");
+ } catch (final IOException e) {
+ // ok
+ }
+ final PluggableConsoleInput input3 = new PluggableConsoleInput();
+
+ final Thread th2 = new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(200);
+ } catch (final InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ input3.close();
+ }
+ });
+ th2.start();
+ try {
+ input.prompt(2000);
+ fail("Closed prompt should cause an IO");
+ } catch (final IOException e) {
+ // ok
+ }
+ }
+
+ @Test
+ public final void testConnect() throws IOException, InterruptedException {
+ final PluggableConsoleInput input = new PluggableConsoleInput();
+
+ input.disconnect();
+ try (final PipedInputStream pis = new PipedInputStream();
+ final PipedOutputStream pos = new PipedOutputStream();
+ final PipedInputStream inner = new PipedInputStream(pos);
+ final PipedOutputStream innerPos = new PipedOutputStream(pis);
+ final PrintStream testIn = new PrintStream(innerPos, true, "UTF8");
+ final PrintStream out = new PrintStream(pos)) {
+ input.connect(pis, out);
+ try {
+ input.connect(pis, out);
+ fail("Should not be able to connect already connected");
+ } catch (final IOException e) {
+ // ok
+ }
+ input.disconnect();
+
+ final Thread th = new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ input.prompt("Test", 5000);
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ th.start();
+
+ while (!input.isPrompting()) {
+ Thread.sleep(10);
+ }
+
+ input.connect(pis, out);
+ testIn.println("tac");
+
+ final Thread th2 = new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ input.prompt("Test", 5000);
+ fail("Prompt should io");
+ } catch (final IOException e) {
+ // ok
+ }
+ }
+ });
+ th2.start();
+ while (!input.isPrompting()) {
+ Thread.sleep(10);
+ }
+
+ input.close();
+
+
+ }
+ }
+
+ /** Test method for
+ * {@link fr.bigeon.gclc.socket.PluggableConsoleInput#getPrompt()}. */
+ @Test
+ public final void testGetPrompt() {
+ final PluggableConsoleInput input = new PluggableConsoleInput();
+ assertEquals("Default prompt invalid", "> ", input.getPrompt());
+ input.setPrompt("test");
+ assertEquals("Prompt setting failed", "test", input.getPrompt());
+ }
+}
diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/RemoteDisconnectCommandTest.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/RemoteDisconnectCommandTest.java
new file mode 100644
index 0000000..dc7528d
--- /dev/null
+++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/RemoteDisconnectCommandTest.java
@@ -0,0 +1,105 @@
+/*
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
+ *
+ * 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.RemoteDisconnectCommandTest.java
+ * Created on: Nov 18, 2017
+ */
+package fr.bigeon.gclc.socket;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import fr.bigeon.gclc.exception.CommandRunException;
+import fr.bigeon.gclc.manager.PipedConsoleOutput;
+
+/**
+ *
+ * TODO
+ *
+ * @author Emmanuel Bigeon */
+public class RemoteDisconnectCommandTest {
+
+ /** Test method for
+ * {@link fr.bigeon.gclc.socket.RemoteDisconnectCommand#execute(fr.bigeon.gclc.manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}.
+ *
+ * @throws CommandRunException if the command unexpectedly failed.
+ * @throws IOException if the output could not be written to */
+ @Test
+ public final void testExecute() throws CommandRunException, IOException {
+ final DConnexionManager manager = new DConnexionManager<>();
+ final RemoteDisconnectCommand cmd = new RemoteDisconnectCommand<>(
+ "quit", manager, true);
+ final RemoteDisconnectCommand cmd2 = new RemoteDisconnectCommand<>(
+ "quit", manager, false);
+ manager.addConnexion("test");
+
+ cmd2.execute(null, null);
+ assertFalse("No arguemnt should remova no connections",
+ manager.getConnected().isEmpty());
+ cmd.execute(null, null);
+ assertTrue("No arguemnt should remova all connections",
+ manager.getConnected().isEmpty());
+
+ final String name1 = manager.addConnexion("test");
+ final String name2 = manager.addConnexion("test");
+
+ cmd.execute(null, null, name1);
+ assertFalse("With argument shuld remove specified name",
+ manager.getConnected().contains(name1));
+ assertTrue("With argument shuld remove only specified name",
+ manager.getConnected().contains(name2));
+
+ cmd.execute(null, null, name1);
+ assertTrue("With argument shuld remove only specified name",
+ manager.getConnected().contains(name2));
+
+ try (PipedConsoleOutput out = new PipedConsoleOutput()) {
+ cmd.execute(out, null, name1);
+ assertTrue("With argument shuld remove only specified name",
+ manager.getConnected().contains(name2));
+
+ out.close();
+ try {
+ cmd.execute(out, null, name1);
+ fail("Closed stream should cause error in printing");
+ } catch (final CommandRunException e) {
+ // ok
+ }
+ }
+ }
+
+}
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 68fb45f..9f24fd1 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
@@ -1,10 +1,7 @@
/*
- * Copyright E. Bigeon (2014)
- *
- * emmanuel@bigeon.fr
- *
- * This software is a computer program whose purpose is to
- * Socket implementation of GCLC.
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
@@ -39,9 +36,6 @@
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;
@@ -53,28 +47,34 @@ import java.util.logging.Logger;
import org.junit.Test;
/** Test class for {@link SocketConsoleApplicationShell}
- *
+ *
* @author Emmanuel Bigeon */
-@SuppressWarnings({"static-method", "unused", "javadoc", "nls"})
+@SuppressWarnings({"static-method", "javadoc", "nls"})
public class SocketConsoleApplicationTest {
private static final Logger LOGGER = Logger
.getLogger(SocketConsoleApplicationTest.class.getName());
+ /** @param in the input
+ * @return the string
+ * @throws IOException if the input reading failed */
+ private String consumeToPrompt(final String server,
+ final BufferedReader in) throws IOException {
+ String fromServer = server;
+ LOGGER.fine("Server: \n" + fromServer);
+ while (fromServer != null && !fromServer.equals("Bye.") &&
+ !fromServer.equals("> ")) {
+ fromServer = in.readLine();
+ LOGGER.fine("Server: \n" + fromServer);
+ }
+ return fromServer;
+ }
+
@Test
- public void integrationTest() {
+ public void testIntegration() throws IOException, InterruptedException {
Thread server;
- try {
- server = TestServer.startServer("bye");
- } catch (IOException e3) {
- assertNull(e3);
- fail("unable to start server");
- }
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e1) {
- e1.printStackTrace();
- }
+ server = TestServer.getServer();
+ Thread.sleep(1000);
final String hostName = "127.0.0.1";
final int portNumber = 3300;
@@ -86,7 +86,7 @@ public class SocketConsoleApplicationTest {
String fromServer;
int i = -1;
- String[] cmds = {"help", "toto", "test", "bye"};
+ final String[] cmds = {"help", "toto", "test", "out"};
while ((fromServer = in.readLine()) != null) {
i++;
fromServer = consumeToPrompt(fromServer, in);
@@ -100,9 +100,7 @@ public class SocketConsoleApplicationTest {
out.println(fromUser);
}
}
- assertEquals(4, i);
- } catch (final IOException e) {
- e.printStackTrace();
+ assertEquals("Disconnection command should close connection", 3, i);
}
try (Socket kkSocket = new Socket(hostName, portNumber);
@@ -113,7 +111,7 @@ public class SocketConsoleApplicationTest {
String fromServer;
int i = 0;
- String[] cmds = {"help", "toto", "test",
+ final String[] cmds = {"help", "toto", "test",
ConsoleTestApplication.EXIT};
while ((fromServer = in.readLine()) != null) {
fromServer = consumeToPrompt(fromServer, in);
@@ -129,15 +127,15 @@ public class SocketConsoleApplicationTest {
}
i++;
}
- assertEquals(4, i);
- } catch (final IOException e) {
- e.printStackTrace();
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e1) {
- e1.printStackTrace();
+ assertEquals("Application exit command should close connection", 4,
+ i);
}
+ Thread.sleep(100);
+ TestServer.closeServer();
+ server.join();
+ server = TestServer.getServer();
+ Thread.sleep(1000);
+
try (Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(),
@@ -147,7 +145,7 @@ public class SocketConsoleApplicationTest {
String fromServer;
int i = 0;
- String[] cmds = {"help", "toto", "test",
+ final String[] cmds = {"help", "toto", "test",
ConsoleTestApplication.EXIT};
while ((fromServer = in.readLine()) != null) {
fromServer = consumeToPrompt(fromServer, in);
@@ -162,80 +160,10 @@ public class SocketConsoleApplicationTest {
}
i++;
}
- assertEquals(4, i);
- } catch (final IOException e) {
- e.printStackTrace();
+ assertEquals("Application exit command should close connection", 4,
+ i);
}
TestServer.closeServer();
- try {
- Thread.sleep(100);
- } catch (InterruptedException e2) {
- e2.printStackTrace();
- }
- try {
- server = TestServer.startServer(true);
- } catch (IOException e2) {
- assertNull(e2);
- }
- 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", "test", "close"};
- while ((fromServer = in.readLine()) != null) {
- fromServer = consumeToPrompt(fromServer, in);
- if (fromServer == null || fromServer.equals("Bye.")) {
- break;
- }
- assertTrue(i < 1);
- final String fromUser = cmds[i];
- if (fromUser != null) {
- LOGGER.fine("Client: " + fromUser);
- out.println(fromUser);
- }
- i++;
- }
- assertEquals(1, i);
- } catch (final IOException e) {
- e.printStackTrace();
- }
- Thread srv = null;
- try {
- srv = TestServer.getServer();
- } catch (IOException e1) {
- assertNull(e1);
- }
- TestServer.closeServer();
- try {
- srv.join();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /** @param in the input
- * @return the string
- * @throws IOException if the input reading failed */
- private String consumeToPrompt(String server,
- BufferedReader in) throws IOException {
- String fromServer = server;
- LOGGER.fine("Server: \n" + fromServer);
- while (fromServer != null && !fromServer.equals("Bye.") &&
- !fromServer.equals("> ")) {
- fromServer = in.readLine();
- LOGGER.fine("Server: \n" + fromServer);
- }
- return fromServer;
+ server.join();
}
}
diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestConsoleClient.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestConsoleClient.java
index 4119e7b..97dce4e 100644
--- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestConsoleClient.java
+++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestConsoleClient.java
@@ -1,10 +1,7 @@
/*
- * Copyright E. Bigeon (2014)
- *
- * emmanuel@bigeon.fr
- *
- * This software is a computer program whose purpose is to
- * Socket implementation of GCLC.
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
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 782bb2e..58fbce2 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
@@ -1,10 +1,7 @@
/*
- * Copyright E. Bigeon (2014)
- *
- * emmanuel@bigeon.fr
- *
- * This software is a computer program whose purpose is to
- * Socket implementation of GCLC.
+ * GCLC Socket, Socket implementation of GCLC
+ * Copyright (C) 2014-2017 E. Bigeon
+ * mailto:emmanuel@bigeon.fr
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
@@ -35,7 +32,9 @@
package fr.bigeon.gclc.socket;
import java.io.IOException;
-import java.nio.charset.Charset;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
import fr.bigeon.gclc.ConsoleApplication;
@@ -46,14 +45,21 @@ import fr.bigeon.gclc.ConsoleApplication;
public class TestServer {
private static SocketConsoleApplicationShell SHELL;
- private static Thread server;
+ private static ConnexionManager manager;
- public static void closeServer() {
+ private static Thread server;
+ private static PluggableConsoleInput input;
+ private static PluggableConsoleOutput output;
+
+ public static synchronized void closeServer() {
SHELL.stop();
+ input.close();
+ output.close();
SHELL = null;
+ server = null;
}
- public static Thread getServer() throws IOException {
+ public static synchronized Thread getServer() throws IOException {
if (server == null) {
server = new Thread(getShell(), "gclcServer");
server.start();
@@ -61,13 +67,27 @@ public class TestServer {
return server;
}
- private static SocketConsoleApplicationShell getShell() throws IOException {
+ private static SocketConsoleApplicationShell getShell() throws UnknownHostException {
if (SHELL == null) {
- SHELL = new SocketConsoleApplicationShell(3300, "close",
- ConsoleTestApplication.EXIT, Charset.forName("UTF-8"));
+ input = new PluggableConsoleInput();
+ input.setPrompt("> \n");
+ output = new PluggableConsoleOutput();
+ manager = new DConnexionManager<>();
+ SHELL = new SocketConsoleApplicationShell(3300,
+ InetAddress.getByName("127.0.0.1"));
final ConsoleApplication app = ConsoleTestApplication
- .create(SHELL.getOutput(), SHELL.getInput());
+ .create(output, input, manager);
+ SHELL.setInterface(new SocketConsoleInterface(input, output));
+ SHELL.setConnexionManager(manager);
SHELL.setApplication(app);
+ final Thread th = new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ app.start();
+ }
+ });
+ th.start();
}
return SHELL;
}
@@ -76,33 +96,10 @@ public class TestServer {
* @throws IOException if the server starting failed */
public static void main(final String... args) throws IOException {
try {
- startServer(false).join();
+ getServer().join();
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
- public static Thread startServer(final boolean autoClose) throws IOException {
- if (SHELL == null) {
- SHELL = new SocketConsoleApplicationShell(3300, autoClose,
- ConsoleTestApplication.EXIT, Charset.forName("UTF-8"));
- final ConsoleApplication app = ConsoleTestApplication
- .create(SHELL.getOutput(), SHELL.getInput());
- SHELL.setApplication(app);
- server = null;
- }
- return getServer();
- }
-
- public static Thread startServer(final String closeConnection) throws IOException {
- if (SHELL == null) {
- SHELL = new SocketConsoleApplicationShell(3300, closeConnection,
- ConsoleTestApplication.EXIT, Charset.forName("UTF-8"));
- final ConsoleApplication app = ConsoleTestApplication
- .create(SHELL.getOutput(), SHELL.getInput());
- SHELL.setApplication(app);
- server = null;
- }
- return getServer();
- }
}