From d5f2d39808139d3d71a0eb88ab4dfaa80e351e45 Mon Sep 17 00:00:00 2001 From: Emmanuel Bigeon Date: Sat, 18 Nov 2017 16:33:33 -0500 Subject: [PATCH] Update gclc-socket. Signed-off-by: Emmanuel Bigeon --- gclc-socket/pom.xml | 33 +- .../bigeon/gclc/socket/ConnexionManager.java | 98 +++++ .../bigeon/gclc/socket/ConsoleRunnable.java | 133 ------- .../bigeon/gclc/socket/DConnexionManager.java | 173 +++++++++ .../gclc/socket/PluggableConsoleInput.java | 279 ++++++++++++++ .../gclc/socket/PluggableConsoleOutput.java | 126 ++++++ .../gclc/socket/RemoteDisconnectCommand.java | 132 +++++++ .../socket/SocketConsoleApplicationShell.java | 359 ++++-------------- .../gclc/socket/SocketConsoleInterface.java | 82 ++++ .../fr/bigeon/gclc/socket/package-info.java | 40 ++ .../gclc/socket/ConsoleRunnableTest.java | 182 --------- .../gclc/socket/ConsoleTestApplication.java | 42 +- .../socket/PluggableConsoleInputTest.java | 200 ++++++++++ .../socket/RemoteDisconnectCommandTest.java | 105 +++++ .../socket/SocketConsoleApplicationTest.java | 148 ++------ .../bigeon/gclc/socket/TestConsoleClient.java | 9 +- .../fr/bigeon/gclc/socket/TestServer.java | 73 ++-- 17 files changed, 1443 insertions(+), 771 deletions(-) create mode 100644 gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConnexionManager.java delete mode 100644 gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java create mode 100644 gclc-socket/src/main/java/fr/bigeon/gclc/socket/DConnexionManager.java create mode 100644 gclc-socket/src/main/java/fr/bigeon/gclc/socket/PluggableConsoleInput.java create mode 100644 gclc-socket/src/main/java/fr/bigeon/gclc/socket/PluggableConsoleOutput.java create mode 100644 gclc-socket/src/main/java/fr/bigeon/gclc/socket/RemoteDisconnectCommand.java create mode 100644 gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleInterface.java create mode 100644 gclc-socket/src/main/java/fr/bigeon/gclc/socket/package-info.java delete mode 100644 gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java create mode 100644 gclc-socket/src/test/java/fr/bigeon/gclc/socket/PluggableConsoleInputTest.java create mode 100644 gclc-socket/src/test/java/fr/bigeon/gclc/socket/RemoteDisconnectCommandTest.java diff --git a/gclc-socket/pom.xml b/gclc-socket/pom.xml index 133f0a2..cb333d4 100644 --- a/gclc-socket/pom.xml +++ b/gclc-socket/pom.xml @@ -1,4 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -81,7 +112,7 @@ of Emmanuel Bigeon. --> fr.bigeon gclc - 2.0.0 + 2.0.1 fr.bigeon diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConnexionManager.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConnexionManager.java new file mode 100644 index 0000000..fecf085 --- /dev/null +++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConnexionManager.java @@ -0,0 +1,98 @@ +/* + * 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.ConnexionManager.java + * Created on: Nov 18, 2017 + */ +package fr.bigeon.gclc.socket; + +import java.util.Collection; + +/** A manager for connected elements. + *

+ * Connected elements are given an identifier (unique) at connexion. + * + * @author Emmanuel Bigeon + * @param the type of object connected */ +public interface ConnexionManager { + /** Add a connection in the name. + * + * @param handle the connected object + * @return the name */ + String addConnexion(T handle); + + /** Disconnect an element. + * + * @param id the element connection id + * @return the object being disconnected */ + T disconnect(String id); + + /** Get the connected object. + * + * @param id the connexion id + * @return the object */ + T get(String id); + + /** Get the connected elements' ids. + * + * @return the connected elements' ids */ + Collection 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(); - } }