Compare commits
97 Commits
gclc-socke
...
process-0.
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ca2905b2a | |||
| 37cbe2eb95 | |||
| 9bc46c93a6 | |||
| 00c0e86d72 | |||
| eae7e0d69f | |||
| 926a8d72fa | |||
| 95ec674528 | |||
| 4d30402a42 | |||
| cdcf9f0fae | |||
| cce8a9504d | |||
| 119afe1660 | |||
| 7517251d1c | |||
| f8a37e7eee | |||
| 3598bb359e | |||
| e9fa6e209f | |||
| d4b695f60b | |||
| 8a8d298485 | |||
| 50887f44bb | |||
| 3a5250d987 | |||
| ddc60652f1 | |||
| 206df88506 | |||
| d2cd059c01 | |||
| 90dab5a809 | |||
| d5f2d39808 | |||
| 36f39df87e | |||
| 82e8d1e1b7 | |||
| 0ebcd7b210 | |||
| d32ea6b4b0 | |||
| e5d5edcf63 | |||
| b80a3fc5b8 | |||
| 6bb937342e | |||
| 283090d361 | |||
| 83c02f82ec | |||
| fce2b01914 | |||
| 9b071a378f | |||
| 89e849c27f | |||
| 9747cf21b2 | |||
| 6f8c536f55 | |||
| b3224ad689 | |||
| 0feb069878 | |||
| b5f1463864 | |||
| 0bbe9d321d | |||
| 2e28e269e1 | |||
| e11d90378f | |||
| 6a0e68d312 | |||
| 3534e817e2 | |||
| 20193dd1e4 | |||
| 5df8321dee | |||
| 629d07bc32 | |||
| e32e2428e5 | |||
| db9f81c6a8 | |||
| 0a995394f9 | |||
| 0ca055db8e | |||
| b93c2b5220 | |||
| 8322454f72 | |||
| ab47d6573b | |||
| 6d496d701d | |||
| b4df39491e | |||
| 775a71b2ec | |||
| c9eb221d31 | |||
| 340a0317af | |||
| 35959a8d8a | |||
| 07645d7c0c | |||
| 99c0c9008b | |||
| 26265ad7e6 | |||
| 092883f4c3 | |||
| 25904c907d | |||
| ae55ebea29 | |||
| e989aff2f4 | |||
| 2f0c03a73b | |||
| ff9ace1033 | |||
| ffa54af3be | |||
| 269704f5a2 | |||
| aecc18cc83 | |||
| 063cad61cd | |||
| 201b6ad366 | |||
| bf8d76750f | |||
| 17ac47f15e | |||
| 7de2dadbe0 | |||
| 50395ba852 | |||
| 248c82cf50 | |||
| 6289ca1db9 | |||
| 5b6634eaf0 | |||
| 4737a16874 | |||
| bf0a3b30e5 | |||
| 5d25971b56 | |||
| c27872de94 | |||
| a12484a72a | |||
| ee0d91e455 | |||
| 719d7ada3f | |||
| c877450b2b | |||
| f3a8aafce1 | |||
| d2bc86fffc | |||
| 3b6ba337f1 | |||
| 6a117afcb3 | |||
| 7d41a9f4c6 | |||
| 90579d79a9 |
4
gclc-process/.gitignore
vendored
Normal file
4
gclc-process/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/target/
|
||||
/.classpath
|
||||
/.project
|
||||
/.settings/
|
||||
95
gclc-process/pom.xml
Normal file
95
gclc-process/pom.xml
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
<!-- process, Distribution repositories and basic setup for Emmanuel Bigeon projects -->
|
||||
<!-- Copyright (C) 2014-2018 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. -->
|
||||
|
||||
|
||||
<!-- process, Distribution repositories and basic setup for Emmanuel Bigeon projects -->
|
||||
<!-- 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. -->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>fr.bigeon.gclc</groupId>
|
||||
<artifactId>process</artifactId>
|
||||
<version>0.0.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>process</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>fr.bigeon.config</groupId>
|
||||
<artifactId>ebigeon-config</artifactId>
|
||||
<version>1.8.2</version>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<artifactId>gclc</artifactId>
|
||||
<version>2.0.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<scm>
|
||||
<tag>process-0.0.2</tag>
|
||||
<developerConnection>scm:git:gogs@git.code.bigeon.net:emmanuel/gclc.git</developerConnection>
|
||||
</scm>
|
||||
</project>
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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-process:fr.bigeon.gclc.process.CommandFork.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import fr.bigeon.gclc.command.CommandParameters;
|
||||
import fr.bigeon.gclc.command.ParametrizedCommand;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.CommandRunExceptionType;
|
||||
import fr.bigeon.gclc.exception.InvalidParameterException;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** A command that is launched inside an internal terminal.
|
||||
* <p>
|
||||
* Several things are to be considered before adding this command to an
|
||||
* application:
|
||||
* <ul>
|
||||
* <li>The commands will be able to run in parallel.
|
||||
* <li>The managing of the commands will be handled through piped systems and
|
||||
* buffered in so switching to a forked command will actually print again all
|
||||
* the command history to the output console.
|
||||
* </ul>
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class CommandForeground extends ParametrizedCommand {
|
||||
|
||||
private final TaskPool pool;
|
||||
|
||||
/** Add the fork command.
|
||||
*
|
||||
* @param name the command name
|
||||
* @param pool The pool to get joinable tasks from */
|
||||
public CommandForeground(final String name, TaskPool pool) {
|
||||
super(name, false);
|
||||
this.pool = pool;
|
||||
addParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void addParameters() {
|
||||
try {
|
||||
addStringParameter("pid", false);
|
||||
addStringParameter("delai", false);
|
||||
} catch (final InvalidParameterException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ParametrizedCommand#doExecute(fr.bigeon.gclc.
|
||||
* manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
|
||||
* fr.bigeon.gclc.command.CommandParameters) */
|
||||
@Override
|
||||
protected void doExecute(final ConsoleOutput out, final ConsoleInput in,
|
||||
final CommandParameters parameters) throws CommandRunException {
|
||||
String string = parameters.get("pid");
|
||||
final List<String> additionals = parameters.getAdditionals();
|
||||
if (string == null && !additionals.isEmpty()) {
|
||||
string = additionals.get(0);
|
||||
}
|
||||
if (string == null) {
|
||||
throw new CommandRunException(CommandRunExceptionType.USAGE,
|
||||
"Missing process id", this);
|
||||
}
|
||||
// Join the command.
|
||||
final Task cmd = pool.get(string);
|
||||
if (!(cmd instanceof ForkTask)) {
|
||||
throw new CommandRunException("No such forked process", this);
|
||||
}
|
||||
long delai = 0;
|
||||
final String delaiOpt = parameters.get("delai");
|
||||
if (delaiOpt != null) {
|
||||
delai = Long.parseLong(delaiOpt) * 1000;
|
||||
}
|
||||
if (delai < 0) {
|
||||
throw new CommandRunException("Join delai cannot be negative", this);
|
||||
}
|
||||
((ForkTask) cmd).join(out, in, delai);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@Override
|
||||
public String tip() {
|
||||
return "Join the execution of a command, for a given amount of time";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usagePattern() */
|
||||
@Override
|
||||
protected String usagePattern() {
|
||||
return super.usagePattern() + " [(-pid) <id>] ([-delai <delai>])";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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-process:fr.bigeon.gclc.process.CommandFork.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import fr.bigeon.gclc.command.Command;
|
||||
import fr.bigeon.gclc.command.ICommand;
|
||||
import fr.bigeon.gclc.command.ICommandProvider;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** A command that is launched inside an internal terminal.
|
||||
* <p>
|
||||
* Several things are to be considered before adding this command to an
|
||||
* application:
|
||||
* <ul>
|
||||
* <li>The commands will be able to run in parallel.
|
||||
* <li>The managing of the commands will be handled through piped systems and
|
||||
* buffered in so switching to a forked command will actually print again all
|
||||
* the command history to the output console.
|
||||
* </ul>
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class CommandFork extends Command {
|
||||
|
||||
private final TaskPool pool;
|
||||
private final ICommandProvider provider;
|
||||
private final int lines;
|
||||
|
||||
/** Add the fork command.
|
||||
*
|
||||
* @param name the command name
|
||||
* @param provider the allowed command collection */
|
||||
public CommandFork(final String name, ICommandProvider provider, TaskPool pool) {
|
||||
this(name, provider, pool, -1);
|
||||
}
|
||||
|
||||
/** Add the fork command.
|
||||
*
|
||||
* @param name the command name
|
||||
* @param provider the allowed command collection */
|
||||
public CommandFork(final String name, ICommandProvider provider, TaskPool pool,
|
||||
int lines) {
|
||||
super(name);
|
||||
this.provider = provider;
|
||||
this.pool = pool;
|
||||
this.lines = lines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final ConsoleOutput out, final ConsoleInput in, String... args)
|
||||
throws CommandRunException {
|
||||
if (args.length < 1) {
|
||||
throw new CommandRunException("No command to fork", this);
|
||||
}
|
||||
final ICommand cmd = provider.get(args[0]);
|
||||
final String[] inner = Arrays.copyOfRange(args, 1, args.length);
|
||||
final ForkTask task = new ForkCommandTask(cmd, inner, lines);
|
||||
final Thread th = new Thread(task);
|
||||
pool.add(task);
|
||||
th.start();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@Override
|
||||
public String tip() {
|
||||
return "Command background launch";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usagePattern() */
|
||||
@Override
|
||||
protected String usagePattern() {
|
||||
return super.usagePattern() + " <command>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import fr.bigeon.gclc.command.ICommand;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bigeon
|
||||
*
|
||||
*/
|
||||
public class ForkCommandTask extends ForkTask {
|
||||
private final ICommand command;
|
||||
private final String[] args;
|
||||
|
||||
/** @param cmd the command
|
||||
* @param args the arguements
|
||||
* @param lines the number of print to store in the output */
|
||||
public ForkCommandTask(final ICommand cmd, final String[] args, int lines) {
|
||||
super(lines);
|
||||
command = cmd;
|
||||
this.args = Arrays.copyOf(args, args.length);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.process.Task#getName() */
|
||||
@Override
|
||||
public String getName() {
|
||||
return command.getCommandName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.process.ForkTask#doRun() */
|
||||
@Override
|
||||
protected void doRun() throws CommandRunException {
|
||||
command.execute(out, in, args);
|
||||
}
|
||||
|
||||
}
|
||||
155
gclc-process/src/main/java/fr/bigeon/gclc/process/ForkTask.java
Normal file
155
gclc-process/src/main/java/fr/bigeon/gclc/process/ForkTask.java
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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-process:fr.bigeon.gclc.process.ForkTask.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
import fr.bigeon.gclc.process.io.ConnectingConsoleInput;
|
||||
import fr.bigeon.gclc.process.io.ConnectingConsoleOutput;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* TODO
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public abstract class ForkTask implements Task {
|
||||
|
||||
private final Set<InterruptionListener> listeners = new HashSet<>();
|
||||
private boolean running = false;
|
||||
|
||||
protected final ConnectingConsoleInput in = new ConnectingConsoleInput();
|
||||
protected final ConnectingConsoleOutput out;
|
||||
private CommandRunException exception;
|
||||
private final Object runLock = new Object();
|
||||
|
||||
/** @param lines the number of print to store in the output */
|
||||
public ForkTask(int lines) {
|
||||
out = new ConnectingConsoleOutput(ConnectingConsoleOutput.PERSIST, lines);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.process.Task#addInterruptionListener(fr.bigeon.gclc.
|
||||
* process.InterruptionListener) */
|
||||
@Override
|
||||
public final void addInterruptionListener(final InterruptionListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.process.Task#isRunning() */
|
||||
@Override
|
||||
public final boolean isRunning() {
|
||||
synchronized (runLock) {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param out the console output
|
||||
* @param in the console input
|
||||
* @param timeout the maximal wait (0 for ever) */
|
||||
public final void join(final ConsoleOutput out, final ConsoleInput in, long timeout) {
|
||||
synchronized (runLock) {
|
||||
this.out.connect(out);
|
||||
this.in.connect(in);
|
||||
try {
|
||||
if (running) {
|
||||
runLock.wait(timeout);
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO log.
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
this.out.disconnect();
|
||||
this.in.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see
|
||||
* fr.bigeon.gclc.process.Task#rmInterruptionListener(fr.bigeon.gclc.process
|
||||
* .InterruptionListener) */
|
||||
@Override
|
||||
public final void rmInterruptionListener(final InterruptionListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Runnable#run() */
|
||||
@Override
|
||||
public final void run() {
|
||||
synchronized (runLock) {
|
||||
running = true;
|
||||
}
|
||||
try {
|
||||
doRun();
|
||||
} catch (final CommandRunException e) {
|
||||
exception = e;
|
||||
} finally {
|
||||
setRunning(false);
|
||||
}
|
||||
for (final InterruptionListener interruptionListener : listeners) {
|
||||
interruptionListener.interrupted();
|
||||
}
|
||||
}
|
||||
|
||||
/** Actually run the fork. */
|
||||
protected abstract void doRun() throws CommandRunException;
|
||||
|
||||
/** Get the excepion that caused a failure.
|
||||
*
|
||||
* @return the exception */
|
||||
public final CommandRunException getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.process.Task#setRunning(boolean) */
|
||||
@Override
|
||||
public final void setRunning(final boolean running) {
|
||||
synchronized (runLock) {
|
||||
this.running = running;
|
||||
runLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected final Object getRunningLock() {
|
||||
return runLock;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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:fr.bigeon.gclc.proc.InterruptionListener.java
|
||||
* Created on: May 10, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
/** A listener for interruption
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public interface InterruptionListener {
|
||||
/** Notification of an interuption of a listened object */
|
||||
void interrupted();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import fr.bigeon.gclc.ApplicationAttachement;
|
||||
import fr.bigeon.gclc.command.ICommandProvider;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bigeon
|
||||
*
|
||||
*/
|
||||
public class ProcessAttachement implements ApplicationAttachement {
|
||||
|
||||
private final TaskPool pool;
|
||||
|
||||
/** @param pool the task pool to manage */
|
||||
public ProcessAttachement(TaskPool pool) {
|
||||
super();
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ApplicationAttachement#attach(fr.bigeon.gclc.ConsoleApplication)
|
||||
*/
|
||||
@Override
|
||||
public void attach(ICommandProvider application) throws InvalidCommandName {
|
||||
application.add(new ProcessKill("kill", pool));
|
||||
application.add(new ProcessList("list", pool));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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:fr.bigeon.gclc.proc.ProcessList.java
|
||||
* Created on: May 10, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import fr.bigeon.gclc.command.Command;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** A command that will flag a task to stop
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class ProcessClear extends Command {
|
||||
/** The taskpool */
|
||||
private final TaskPool pool;
|
||||
|
||||
/** @param name the command name
|
||||
* @param pool the pool */
|
||||
public ProcessClear(final String name, final TaskPool pool) {
|
||||
super(name);
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
/* (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 {
|
||||
for (final String id : pool.getPIDs()) {
|
||||
if (!pool.get(id).isRunning()) {
|
||||
pool.remove(id);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@SuppressWarnings("nls")
|
||||
@Override
|
||||
public String tip() {
|
||||
return "Request a process to stop (softly)";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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:fr.bigeon.gclc.proc.ProcessList.java
|
||||
* Created on: May 10, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import fr.bigeon.gclc.command.Command;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** A command that will flag a task to stop
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class ProcessKill extends Command {
|
||||
/** The taskpool */
|
||||
private final TaskPool pool;
|
||||
|
||||
/** @param name the command name
|
||||
* @param pool the pool */
|
||||
public ProcessKill(final String name, final TaskPool pool) {
|
||||
super(name);
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
/* (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 {
|
||||
pool.get(args[0]).setRunning(false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@SuppressWarnings("nls")
|
||||
@Override
|
||||
public String tip() {
|
||||
return "Request a process to stop (softly)";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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:fr.bigeon.gclc.proc.ProcessList.java
|
||||
* Created on: May 10, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
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 command to list current processes
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class ProcessList extends Command {
|
||||
/** The process pool */
|
||||
private final TaskPool pool;
|
||||
|
||||
/** @param name the command name
|
||||
* @param pool the pool */
|
||||
public ProcessList(final String name, final TaskPool pool) {
|
||||
super(name);
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
/* (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 {
|
||||
final ArrayList<String> pids = new ArrayList<>(pool.getPIDs());
|
||||
Collections.sort(pids);
|
||||
for (final String string : pids) {
|
||||
try {
|
||||
out.println(MessageFormat.format("{0}\t{1}", string, //$NON-NLS-1$
|
||||
pool.get(string).getName()));
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException(
|
||||
CommandRunExceptionType.INTERACTION,
|
||||
"Unable to communicate with user", e, this); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@SuppressWarnings("nls")
|
||||
@Override
|
||||
public String tip() {
|
||||
return "List all processes";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import fr.bigeon.gclc.ApplicationAttachement;
|
||||
import fr.bigeon.gclc.command.ICommandProvider;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
|
||||
/** @author Emmanuel Bigeon */
|
||||
public class ScreenAttachement implements ApplicationAttachement {
|
||||
|
||||
private final TaskPool pool;
|
||||
private final int lines;
|
||||
|
||||
/** @param pool the task pool to manage */
|
||||
public ScreenAttachement(TaskPool pool, int lines) {
|
||||
super();
|
||||
this.pool = pool;
|
||||
this.lines = lines;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ApplicationAttachement#attach(fr.bigeon.gclc.
|
||||
* ConsoleApplication) */
|
||||
@Override
|
||||
public void attach(ICommandProvider application) throws InvalidCommandName {
|
||||
application.add(new ProcessKill("terminate", pool));
|
||||
application.add(new ProcessList("list", pool));
|
||||
application.add(new ProcessClear("clear", pool));
|
||||
application.add(new CommandForeground("fg", pool));
|
||||
application.add(new CommandFork("fork", application, pool, lines));
|
||||
}
|
||||
}
|
||||
78
gclc-process/src/main/java/fr/bigeon/gclc/process/Task.java
Normal file
78
gclc-process/src/main/java/fr/bigeon/gclc/process/Task.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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:fr.bigeon.gclc.proc.ThreadCommand.java
|
||||
* Created on: May 10, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
/** Tasks are named runnable that can be interrupted.
|
||||
* <p>
|
||||
* Good practice for those objects include an absence of interaction with the
|
||||
* user (otherwise the user may not know from which running command comes
|
||||
* information and requests) and unicity of the object to have a coherent
|
||||
* control through the {@link #setRunning(boolean)} method.
|
||||
* <p>
|
||||
* Typical cases where such command can be useful is for an application that
|
||||
* does long computations.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public interface Task extends Runnable {
|
||||
/** Add a listener for this command end of execution
|
||||
*
|
||||
* @param listener the listener */
|
||||
void addInterruptionListener(InterruptionListener listener);
|
||||
|
||||
/** Get the task name.
|
||||
*
|
||||
* @return the task name */
|
||||
String getName();
|
||||
|
||||
/** Test the running status of the task.
|
||||
*
|
||||
* @return if the command is supposed to be running */
|
||||
boolean isRunning();
|
||||
|
||||
/** Remove a listener of this command end of execution
|
||||
*
|
||||
* @param listener the listener */
|
||||
void rmInterruptionListener(InterruptionListener listener);
|
||||
|
||||
/** Set the running state.
|
||||
* <p>
|
||||
* This method should be only called by external objects with the false
|
||||
* argument. Calling this method with true has unspecified behavior and
|
||||
* could do nothing as well as restart the command for example.
|
||||
*
|
||||
* @param running the running state */
|
||||
void setRunning(boolean running);
|
||||
}
|
||||
149
gclc-process/src/main/java/fr/bigeon/gclc/process/TaskPool.java
Normal file
149
gclc-process/src/main/java/fr/bigeon/gclc/process/TaskPool.java
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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:fr.bigeon.gclc.proc.TaskPool.java
|
||||
* Created on: May 10, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/** A process pool.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class TaskPool {
|
||||
/** The running processes. */
|
||||
private final Map<String, Task> running = new HashMap<>();
|
||||
/** The count for process id. */
|
||||
private int count = 0;
|
||||
/** The lock for pid attribution synchronization. */
|
||||
private final Object lock = new Object();
|
||||
private final boolean autoClear;
|
||||
|
||||
/** Default constructor. */
|
||||
public TaskPool() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
/** Default constructor. */
|
||||
public TaskPool(boolean autoClear) {
|
||||
this.autoClear = autoClear;
|
||||
}
|
||||
|
||||
/** Remove a task from the pool
|
||||
*
|
||||
* @param pid the task id */
|
||||
public void remove(String pid) {
|
||||
synchronized (lock) {
|
||||
running.remove(pid);
|
||||
count = Math.min(count, Integer.parseInt(pid));
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a process in the pool.
|
||||
*
|
||||
* @param cmd the process
|
||||
* @return the pid */
|
||||
public String add(final Task cmd) {
|
||||
if (cmd == null) {
|
||||
throw new IllegalArgumentException("Task cannot be null"); //$NON-NLS-1$
|
||||
}
|
||||
final String pid;
|
||||
synchronized (lock) {
|
||||
pid = getPID();
|
||||
running.put(pid, cmd);
|
||||
}
|
||||
if (autoClear) {
|
||||
cmd.addInterruptionListener(new InterruptionListener() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void interrupted() {
|
||||
synchronized (lock) {
|
||||
remove(pid);
|
||||
}
|
||||
cmd.rmInterruptionListener(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
/** Get a process by it associated identifier.
|
||||
*
|
||||
* @param pid the task id
|
||||
* @return the task, if any, associated to this id */
|
||||
public Task get(final String pid) {
|
||||
synchronized (lock) {
|
||||
return running.get(pid);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the next process id.
|
||||
*
|
||||
* @return the process id */
|
||||
private String getPID() {
|
||||
synchronized (lock) {
|
||||
String pid;
|
||||
do {
|
||||
pid = Integer.toString(count++);
|
||||
} while (running.containsKey(pid));
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the running processes' identifiers.
|
||||
*
|
||||
* @return the pids */
|
||||
public Collection<String> getPIDs() {
|
||||
return new HashSet<>(running.keySet());
|
||||
}
|
||||
|
||||
/** @return if the clearing of ended task is automatic */
|
||||
public boolean isAutoClearing() {
|
||||
return autoClear;
|
||||
}
|
||||
|
||||
/** Request all task to stop running.
|
||||
* <p>
|
||||
* This call does not guaranty end of execution, it is up to the task
|
||||
* implementation to actually take into account the termination request. */
|
||||
public void shutdown() {
|
||||
synchronized (lock) {
|
||||
for (final Task task : running.values()) {
|
||||
task.setRunning(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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:fr.bigeon.gclc.proc.ProcessList.java
|
||||
* Created on: May 10, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.process;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import fr.bigeon.gclc.command.Command;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** An abstract command to generate a task and return the control to the user
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public abstract class TaskSpawner extends Command {
|
||||
/** The process pool */
|
||||
private final TaskPool pool;
|
||||
private final ExecutorService threadPool;
|
||||
|
||||
/** @param name the command name
|
||||
* @param pool the pool */
|
||||
public TaskSpawner(final String name, final TaskPool pool,
|
||||
ExecutorService threadPool) {
|
||||
super(name);
|
||||
this.pool = pool;
|
||||
this.threadPool = threadPool;
|
||||
}
|
||||
|
||||
/** @param in the input
|
||||
* @param out the output
|
||||
* @param args the arguments
|
||||
* @return the process to start and add to the pool
|
||||
* @throws CommandRunException if the task creation failed */
|
||||
protected abstract Task createTask(ConsoleOutput out, ConsoleInput in,
|
||||
String... args) throws CommandRunException;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
|
||||
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
|
||||
* java.lang.String[]) */
|
||||
@Override
|
||||
public final void execute(final ConsoleOutput out, final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
final Task task = createTask(out, in, args);
|
||||
final Thread th = new Thread(task);
|
||||
pool.add(task);
|
||||
threadPool.execute(th);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package fr.bigeon.gclc.process.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.tools.StringProvider;
|
||||
|
||||
/** @author Emmanuel Bigeon */
|
||||
public final class ConnectingConsoleInput implements ConsoleInput {
|
||||
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(ConnectingConsoleInput.class.getName());
|
||||
private boolean close = false;
|
||||
private StringProvider prompt;
|
||||
private boolean prompting;
|
||||
private final Object promptLock = new Object();
|
||||
private final Object connectionLock = new Object();
|
||||
private ConsoleInput connected;
|
||||
private boolean disconnection;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#close() */
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
close = true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#getPrompt() */
|
||||
@Override
|
||||
public StringProvider getPrompt() {
|
||||
return prompt;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#interruptPrompt() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
synchronized (promptLock) {
|
||||
prompting = false;
|
||||
if (connected != null) {
|
||||
connected.interruptPrompt();
|
||||
}
|
||||
promptLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return close;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt() */
|
||||
@Override
|
||||
public String prompt() throws IOException {
|
||||
return prompt(prompt.apply());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(fr.bigeon.gclc.tools.
|
||||
* StringProvider) */
|
||||
@Override
|
||||
public void setPrompt(StringProvider string) {
|
||||
prompt = string;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */
|
||||
@Override
|
||||
public String prompt(long timeout) throws IOException {
|
||||
return prompt(prompt.apply(), timeout);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String) */
|
||||
@Override
|
||||
public String prompt(String message) throws IOException {
|
||||
synchronized (promptLock) {
|
||||
prompting = true;
|
||||
}
|
||||
while (prompting) {
|
||||
synchronized (promptLock) {
|
||||
if (connected == null) {
|
||||
try {
|
||||
promptLock.wait();
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.WARNING, "Inerruption of console thread", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
} else {
|
||||
final String res = connected.prompt(message);
|
||||
synchronized (connectionLock) {
|
||||
if (disconnection) {
|
||||
disconnection = false;
|
||||
} else if (prompting) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String, long) */
|
||||
@Override
|
||||
public String prompt(String message, long timeout) throws IOException {
|
||||
if (timeout <= 0) {
|
||||
return prompt(message);
|
||||
}
|
||||
final long end = System.currentTimeMillis() + timeout;
|
||||
synchronized (promptLock) {
|
||||
prompting = true;
|
||||
}
|
||||
while (prompting) {
|
||||
synchronized (promptLock) {
|
||||
if (connected == null) {
|
||||
try {
|
||||
promptLock.wait();
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.WARNING, "Inerruption of console thread", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
} else {
|
||||
final String res = connected.prompt(message,
|
||||
end - System.currentTimeMillis());
|
||||
synchronized (connectionLock) {
|
||||
if (disconnection) {
|
||||
disconnection = false;
|
||||
} else if (prompting) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */
|
||||
@Override
|
||||
public void setPrompt(final String prompt) {
|
||||
this.prompt = new StringProvider() {
|
||||
@Override
|
||||
public String apply() {
|
||||
return prompt;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void connect(ConsoleInput input) {
|
||||
disconnect();
|
||||
synchronized (promptLock) {
|
||||
connected = input;
|
||||
promptLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
synchronized (connectionLock) {
|
||||
if (connected != null) {
|
||||
disconnection = true;
|
||||
synchronized (promptLock) {
|
||||
connected.interruptPrompt();
|
||||
}
|
||||
connected = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* process, Distribution repositories and basic setup for Emmanuel Bigeon projects
|
||||
* Copyright (C) 2014-2018 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.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package fr.bigeon.gclc.process.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** @author Emmanuel Bigeon */
|
||||
public class ConnectingConsoleOutput implements ConsoleOutput {
|
||||
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(ConnectingConsoleOutput.class.getName());
|
||||
/** If the undelivered message should be stored. */
|
||||
public static final int QUEUE = 1;
|
||||
/** If the messages should be stored in all cases. */
|
||||
public static final int PERSIST = 1 << 1;
|
||||
private ConsoleOutput output;
|
||||
private boolean close = false;
|
||||
|
||||
private final boolean persistent;
|
||||
private final boolean queued;
|
||||
private final Deque<String> messages;
|
||||
private final int lines;
|
||||
|
||||
/** @param style the type of redirected output
|
||||
* @param lines the number of lines to store */
|
||||
public ConnectingConsoleOutput(int style, int lines) {
|
||||
super();
|
||||
this.lines = lines;
|
||||
queued = (style & QUEUE) != 0;
|
||||
persistent = (style & PERSIST) != 0;
|
||||
if (lines > 0) {
|
||||
messages = new ArrayDeque<>(lines);
|
||||
} else {
|
||||
messages = new ArrayDeque<>();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void addMessage(String text) {
|
||||
if (persistent || queued && output == null) {
|
||||
if (messages.size() == lines) {
|
||||
messages.poll();
|
||||
}
|
||||
messages.offer(text);
|
||||
}
|
||||
if (output != null) {
|
||||
try {
|
||||
output.print(text);
|
||||
} catch (final IOException e) {
|
||||
LOGGER.severe("nable to print to connecting console");
|
||||
LOGGER.log(Level.FINE, "Console error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.AutoCloseable#close() */
|
||||
@Override
|
||||
public void close() {
|
||||
close = true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return close;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#print(java.lang.String) */
|
||||
@Override
|
||||
public void print(String text) {
|
||||
addMessage(text);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#println() */
|
||||
@Override
|
||||
public void println() {
|
||||
addMessage(System.lineSeparator());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#println(java.lang.String) */
|
||||
@Override
|
||||
public void println(String message) {
|
||||
addMessage(message + System.lineSeparator());
|
||||
}
|
||||
|
||||
public synchronized void connect(ConsoleOutput output) {
|
||||
this.output = output;
|
||||
for (final String string : messages) {
|
||||
try {
|
||||
output.print(string);
|
||||
} catch (final IOException e) {
|
||||
LOGGER.severe("nable to print to connecting console");
|
||||
LOGGER.log(Level.FINE, "Console error", e);
|
||||
}
|
||||
}
|
||||
if (!persistent) {
|
||||
messages.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void disconnect() {
|
||||
output = null;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,35 @@
|
||||
|
||||
<!-- 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. -->
|
||||
|
||||
|
||||
<!-- Copyright E. Bigeon (2014) -->
|
||||
<!-- -->
|
||||
<!-- emmanuel@bigeon.fr -->
|
||||
@@ -70,7 +101,7 @@ of Emmanuel Bigeon. -->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>gclc-socket</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>1.1.10-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<url>http://www.bigeon.fr/emmanuel</url>
|
||||
<properties>
|
||||
@@ -78,32 +109,27 @@ of Emmanuel Bigeon. -->
|
||||
<project.scm.id>git.bigeon.net</project.scm.id>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<artifactId>gclc</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<version>2.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<artifactId>smu</artifactId>
|
||||
<version>0.0.5</version>
|
||||
<version>0.0.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<parent>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<groupId>fr.bigeon.config</groupId>
|
||||
<artifactId>ebigeon-config</artifactId>
|
||||
<version>1.7.1</version>
|
||||
<version>1.8.2</version>
|
||||
</parent>
|
||||
<name>GCLC Socket</name>
|
||||
<description>Socket implementation of GCLC</description>
|
||||
<scm>
|
||||
<developerConnection>scm:git:gogs@git.code.bigeon.net:emmanuel/gclc.git</developerConnection>
|
||||
<tag>gclc-socket-1.1.2</tag>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<groupId>fr.bigeon.gclc</groupId>
|
||||
</project>
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* Connected elements are given an identifier (unique) at connexion.
|
||||
*
|
||||
* @author Emmanuel Bigeon
|
||||
* @param <T> the type of object connected */
|
||||
public interface ConnexionManager<T> {
|
||||
/** 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<String> 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.
|
||||
* <p>
|
||||
* This lock will <strong>not</strong> prevent calls to
|
||||
* {@link #disconnect(String)}. It will however stop them from completing
|
||||
* after the effective disconnection of the specified connection.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
@@ -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$
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <T> the connected objects */
|
||||
public final class DConnexionManager<T> implements ConnexionManager<T> {
|
||||
|
||||
/** Class logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(DConnexionManager.class.getName());
|
||||
/** The connected objects. */
|
||||
private final Map<String, T> connecteds = new HashMap<>();
|
||||
/** The locks for the connexions. */
|
||||
private final Map<String, Object> locks = new HashMap<>();
|
||||
/** The counter for the disconnexion locks. */
|
||||
private final Map<String, Integer> 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<String> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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;
|
||||
import fr.bigeon.gclc.tools.ConstantString;
|
||||
import fr.bigeon.gclc.tools.StringProvider;
|
||||
|
||||
/** A console input where the stream can be plugged.
|
||||
* <p>
|
||||
* 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 StringProvider prompt = new ConstantString("> "); //$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 StringProvider 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.apply());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */
|
||||
@Override
|
||||
public String prompt(final long timeout) throws IOException {
|
||||
return prompt(prompt.apply(), 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrompt(final StringProvider prompt) {
|
||||
this.prompt = prompt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrompt(String prompt) {
|
||||
setPrompt(new ConstantString(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;
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <T> the type of connected object */
|
||||
public final class RemoteDisconnectCommand<T> extends Command {
|
||||
|
||||
/** The connexion manager. */
|
||||
private final ConnexionManager<T> 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<T> 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<String> 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");
|
||||
}
|
||||
}
|
||||
@@ -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.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ReadingRunnable;
|
||||
|
||||
/** This is a socket communicating console consoleManager
|
||||
/** This is a socket communicating console consoleManager.
|
||||
* <p>
|
||||
* To use this application, the following flow should be used:
|
||||
*
|
||||
@@ -69,116 +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(PrintWriter writer, Socket socket) {
|
||||
this.writer = writer;
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (!socket.isOutputShutdown()) {
|
||||
while (!socket.isOutputShutdown() &&
|
||||
!consoleManager.available()) {
|
||||
waitASec();
|
||||
}
|
||||
if (socket.isOutputShutdown()) {
|
||||
return;
|
||||
}
|
||||
String m = consoleManager.readNextLine();
|
||||
writer.println(m);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Unexpected problem in manager", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** 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<Socket> rdc;
|
||||
/** The application. */
|
||||
private ConsoleApplication app;
|
||||
|
||||
/** The console manager implementation */
|
||||
private final PipedConsoleManager consoleManager;
|
||||
/** 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;
|
||||
/** THe server address. */
|
||||
private final InetAddress addr;
|
||||
|
||||
/** Create a socket application shell which will listen on the given port
|
||||
* and close session upon the provided string reception by client
|
||||
* and network interface.
|
||||
*
|
||||
* @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(int port, String close,
|
||||
String applicationShutdown, 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.close = close;
|
||||
this.applicationShutdown = applicationShutdown;
|
||||
this.autoClose = false;
|
||||
this.charset = charset;
|
||||
//
|
||||
consoleManager = new PipedConsoleManager();
|
||||
this.addr = addr;
|
||||
}
|
||||
|
||||
/** Create a socket application shell which will listen on the given port
|
||||
* and auto close session after one instruction
|
||||
/** Wait for the identified connection to disconnect.
|
||||
*
|
||||
* @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(int port, boolean autoClose,
|
||||
String applicationShutdown, Charset charset) throws IOException {
|
||||
this.port = port;
|
||||
this.autoClose = autoClose;
|
||||
this.applicationShutdown = applicationShutdown;
|
||||
this.close = autoClose ? null : "close"; //$NON-NLS-1$
|
||||
this.charset = charset;
|
||||
//
|
||||
consoleManager = new PipedConsoleManager();
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
sci.disconnect();
|
||||
}
|
||||
|
||||
/** If the port provided was 0, this allows to get the actual port.
|
||||
* @return the local port
|
||||
* @see java.net.ServerSocket#getLocalPort()
|
||||
*/
|
||||
public int getLocalPort() {
|
||||
return serverSocket.getLocalPort();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -186,157 +120,69 @@ public class SocketConsoleApplicationShell implements Runnable, AutoCloseable {
|
||||
@Override
|
||||
public void run() {
|
||||
// Create the server
|
||||
try (ServerSocket actualServerSocket = new ServerSocket(port)) {
|
||||
this.serverSocket = actualServerSocket;
|
||||
try (ServerSocket actualServerSocket = new ServerSocket(port, 1,
|
||||
addr)) {
|
||||
serverSocket = actualServerSocket;
|
||||
running = true;
|
||||
// Create the streams
|
||||
runSokectServer();
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.SEVERE,
|
||||
LOGGER.severe("Communication error between client and server"); //$NON-NLS-1$
|
||||
LOGGER.log(Level.FINE,
|
||||
"Communication error between client and server", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
/** @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);
|
||||
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(consoleManager.getPrompt());
|
||||
}
|
||||
communicate(clientSocket, out, in);
|
||||
} catch (SocketException e) {
|
||||
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,
|
||||
"Socket closed with exception (probably due to server interruption)", //$NON-NLS-1$
|
||||
e);
|
||||
} catch (final IOException e) {
|
||||
throw e;
|
||||
}
|
||||
LOGGER.info("Closing client"); //$NON-NLS-1$
|
||||
}
|
||||
runnable.setRunning(false);
|
||||
consoleManager.type(applicationShutdown);
|
||||
consoleManager.close();
|
||||
LOGGER.info("Closing Server"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/** @param runnable the runnable */
|
||||
private void startApplication(ConsoleRunnable runnable) {
|
||||
runnable.restart();
|
||||
synchronized (this) {
|
||||
try {
|
||||
wait(ONE_TENTH_OF_SECOND);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Interruption in application start", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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,
|
||||
BufferedReader in) throws IOException {
|
||||
Thread th = new Thread(new OutputForwardRunnable(writer, socket),
|
||||
"ClientComm"); //$NON-NLS-1$
|
||||
th.start();
|
||||
if (autoClose) {
|
||||
communicateOnce(socket, in);
|
||||
} else {
|
||||
communicateLoop(socket, in);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** @param socket the socket
|
||||
* @param in the input from the client
|
||||
* @throws IOException if the communication failed */
|
||||
private void communicateOnce(Socket socket,
|
||||
BufferedReader in) throws IOException {
|
||||
ReadingRunnable reading = new ReadingRunnable(in);
|
||||
Thread th = new Thread(reading, "gclcToApp"); //$NON-NLS-1$
|
||||
th.start();
|
||||
communicationContent(reading);
|
||||
reading.setRunning(false);
|
||||
socket.shutdownOutput();
|
||||
}
|
||||
|
||||
/** @param socket the socket
|
||||
* @param in the input from the client
|
||||
* @throws IOException if the communication failed */
|
||||
private void communicateLoop(Socket socket,
|
||||
BufferedReader in) throws IOException {
|
||||
ReadingRunnable reading = new ReadingRunnable(in);
|
||||
Thread th = new Thread(reading, "gclcToApp"); //$NON-NLS-1$
|
||||
th.start();
|
||||
while (app.isRunning() && communicationContent(reading)) {
|
||||
// keep on going
|
||||
}
|
||||
reading.setRunning(false);
|
||||
socket.shutdownOutput();
|
||||
|
||||
}
|
||||
|
||||
/** @param reading the reading
|
||||
* @return if the communication should be stopped.
|
||||
* @throws IOException if the reading failed */
|
||||
private boolean communicationContent(ReadingRunnable reading) throws IOException {
|
||||
try {
|
||||
while (app.isRunning() && !reading.hasMessage()) {
|
||||
synchronized (this) {
|
||||
waitASec();
|
||||
}
|
||||
}
|
||||
} catch (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;
|
||||
}
|
||||
String ln = reading.getMessage();
|
||||
if (ln.equals(close)) {
|
||||
consoleManager.println("Bye."); //$NON-NLS-1$
|
||||
return false;
|
||||
}
|
||||
// Pass command to application
|
||||
consoleManager.type(ln);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return the consoleManager */
|
||||
public synchronized ConsoleManager getConsoleManager() {
|
||||
return consoleManager;
|
||||
}
|
||||
|
||||
/** @param app the application to set */
|
||||
public synchronized void setApplication(ConsoleApplication app) {
|
||||
/** Set the application.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
||||
/** Set the connexion manager.
|
||||
*
|
||||
* @param rdc the rdc to set */
|
||||
public void setConnexionManager(final ConnexionManager<Socket> 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.
|
||||
* <p>
|
||||
* In most cases, this will terminate communication on every client. On some
|
||||
@@ -345,29 +191,8 @@ public class SocketConsoleApplicationShell implements Runnable, AutoCloseable {
|
||||
running = false;
|
||||
try {
|
||||
serverSocket.close();
|
||||
} catch (IOException e) {
|
||||
} 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 (InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Interrupted wait", //$NON-NLS-1$
|
||||
e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.AutoCloseable#close() */
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
consoleManager.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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.ConsoleManager;
|
||||
|
||||
/** Test class for {@link ConsoleRunnable}
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
@SuppressWarnings({"unused", "javadoc"})
|
||||
public class ConsoleRunnableTest {
|
||||
|
||||
/** <p>
|
||||
* TODO
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
private static final class ConsoleManagerTestImplementation
|
||||
implements ConsoleManager {
|
||||
int i = 0;
|
||||
String[] cmds;
|
||||
|
||||
/** @param cmds the commands to run */
|
||||
public ConsoleManagerTestImplementation(String[] cmds) {
|
||||
super();
|
||||
this.cmds = cmds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrompt(String prompt) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrompt() {
|
||||
// Not used in test
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prompt() {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) { // NOSONAR
|
||||
// do nothing
|
||||
}
|
||||
i++;
|
||||
if (i == cmds.length) {
|
||||
i = 0;
|
||||
}
|
||||
return cmds[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prompt(String message) {
|
||||
return prompt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(String message) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(String text) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return i == cmds.length;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.socket.ConsoleRunnable#ConsoleRunnable(fr.bigeon.gclc.ConsoleApplication)}
|
||||
* . */
|
||||
@Test
|
||||
public void testConsoleRunnable() {
|
||||
// ConsoleApplication app = new ConsoleTestApplication(
|
||||
// new SystemConsoleManager());
|
||||
// ConsoleRunnable runnable = new ConsoleRunnable(app);
|
||||
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -42,61 +41,118 @@ import fr.bigeon.gclc.command.ExitCommand;
|
||||
import fr.bigeon.gclc.command.HelpExecutor;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** A test-purpose application
|
||||
*
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class ConsoleTestApplication extends ConsoleApplication {
|
||||
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 ConsoleTestApplication(final ConsoleManager manager) {
|
||||
super(manager, "Welcome to the test application. Type help or test.",
|
||||
"See you");
|
||||
public static ConsoleApplication create(final ConsoleOutput output,
|
||||
final ConsoleInput input,
|
||||
final ConnexionManager<Socket> manager) {
|
||||
try {
|
||||
add(new ExitCommand(EXIT, this));
|
||||
add(new HelpExecutor("help", manager, this.getRoot()));
|
||||
add(new Command("test") {
|
||||
final ConsoleApplication application = new ConsoleApplication(
|
||||
output, input,
|
||||
"Welcome to the test application. Type help or test.",
|
||||
"See you");
|
||||
application.add(new ExitCommand(EXIT, application) {
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ExitCommand#beforeExit() */
|
||||
@Override
|
||||
protected void beforeExit() {
|
||||
final Collection<String> coll = manager.getConnected();
|
||||
for (final String string : coll) {
|
||||
manager.disconnect(string);
|
||||
}
|
||||
}
|
||||
});
|
||||
application
|
||||
.add(new HelpExecutor("help", application.root));
|
||||
application.add(new Command("test") {
|
||||
|
||||
/* (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 {
|
||||
try {
|
||||
output.println("Test command ran fine");
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException("manager closed", e,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "A test command";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail()
|
||||
*/
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
protected String usageDetail() {
|
||||
// TODO Auto-generated method stub
|
||||
// return null;
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
});
|
||||
application.add(new Command("long") {
|
||||
|
||||
/* (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 {
|
||||
try {
|
||||
manager.println("Test command ran fine");
|
||||
} catch (IOException e) {
|
||||
Thread.sleep(2000);
|
||||
output.println("Test command ran fine");
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new CommandRunException("manager closed", e,
|
||||
this);
|
||||
}
|
||||
}
|
||||
});
|
||||
add(new Command("long") {
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "A long run test command";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail()
|
||||
*/
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
manager.println("Test command ran fine");
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new CommandRunException("manager closed", e,
|
||||
this);
|
||||
}
|
||||
protected String usageDetail() {
|
||||
// TODO Auto-generated method stub
|
||||
// return null;
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
});
|
||||
application.add(
|
||||
new RemoteDisconnectCommand<>("out", manager, true));
|
||||
return application;
|
||||
} catch (final InvalidCommandName e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 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<String> manager = new DConnexionManager<>();
|
||||
final RemoteDisconnectCommand<String> cmd = new RemoteDisconnectCommand<>(
|
||||
"quit", manager, true);
|
||||
final RemoteDisconnectCommand<String> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -85,31 +85,22 @@ public class SocketConsoleApplicationTest {
|
||||
new InputStreamReader(kkSocket.getInputStream()));) {
|
||||
|
||||
String fromServer;
|
||||
int i = 0;
|
||||
String[] cmds = {"help", "toto", "test", "bye"};
|
||||
int i = -1;
|
||||
final String[] cmds = {"help", "toto", "test", "out"};
|
||||
while ((fromServer = in.readLine()) != null) {
|
||||
LOGGER.fine("Server: \n" + fromServer);
|
||||
if (fromServer.equals("Bye.")) {
|
||||
i++;
|
||||
fromServer = consumeToPrompt(fromServer, in);
|
||||
if (fromServer == null || fromServer.equals("Bye.")) {
|
||||
break;
|
||||
}
|
||||
while (fromServer != null && !fromServer.equals("> ")) {
|
||||
fromServer = in.readLine();
|
||||
LOGGER.fine("Server: \n" + fromServer);
|
||||
}
|
||||
if (fromServer == null) {
|
||||
fail("Null pointer");
|
||||
}
|
||||
|
||||
final String fromUser = cmds[i];
|
||||
if (fromUser != null) {
|
||||
LOGGER.fine("Client: " + fromUser);
|
||||
out.println(fromUser);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
assertEquals(4, i);
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
assertEquals("Disconnection command should close connection", 3, i);
|
||||
}
|
||||
|
||||
try (Socket kkSocket = new Socket(hostName, portNumber);
|
||||
@@ -120,35 +111,31 @@ 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) {
|
||||
LOGGER.fine("Server: \n" + fromServer);
|
||||
while (fromServer != null && !fromServer.equals("> ")) {
|
||||
LOGGER.fine("Server: \n" + fromServer);
|
||||
fromServer = in.readLine();
|
||||
}
|
||||
if (fromServer == null) {
|
||||
fromServer = consumeToPrompt(fromServer, in);
|
||||
if (fromServer == null || fromServer.equals("Bye.")) {
|
||||
break;
|
||||
}
|
||||
LOGGER.fine("Server: \n" + fromServer);
|
||||
LOGGER.info("Server: \n" + fromServer);
|
||||
|
||||
final String fromUser = cmds[i];
|
||||
if (fromUser != null) {
|
||||
LOGGER.fine("Client: " + fromUser);
|
||||
LOGGER.info("Client: " + fromUser);
|
||||
out.println(fromUser);
|
||||
}
|
||||
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(),
|
||||
@@ -158,14 +145,11 @@ 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) {
|
||||
while (fromServer != null && !fromServer.equals("> ")) {
|
||||
fromServer = in.readLine();
|
||||
LOGGER.fine("Server: \n" + fromServer);
|
||||
}
|
||||
if (fromServer == null) {
|
||||
fromServer = consumeToPrompt(fromServer, in);
|
||||
if (fromServer == null || fromServer.equals("Bye.")) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -176,71 +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) {
|
||||
assertTrue(i < 1);
|
||||
while (fromServer != null && !fromServer.equals("> ") &&
|
||||
!fromServer.equals("See you")) {
|
||||
fromServer = in.readLine();
|
||||
LOGGER.fine("Server: \n" + fromServer);
|
||||
}
|
||||
if (fromServer == null || fromServer.equals("Bye.") ||
|
||||
fromServer.equals("See you")) {
|
||||
break;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,28 +32,34 @@
|
||||
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;
|
||||
|
||||
/** A test server
|
||||
*
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
@SuppressWarnings({"javadoc", "nls"})
|
||||
public class TestServer {
|
||||
|
||||
private static SocketConsoleApplicationShell SHELL;
|
||||
private static Thread server;
|
||||
private static ConnexionManager<Socket> manager;
|
||||
|
||||
/** @param args no argument
|
||||
* @throws IOException if the server starting failed */
|
||||
public static void main(String... args) throws IOException {
|
||||
try {
|
||||
startServer(false).join();
|
||||
} catch (final InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
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();
|
||||
@@ -64,43 +67,39 @@ 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"));
|
||||
final ConsoleTestApplication app = new ConsoleTestApplication(
|
||||
SHELL.getConsoleManager());
|
||||
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(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;
|
||||
}
|
||||
|
||||
public static Thread startServer(boolean autoClose) throws IOException {
|
||||
if (SHELL == null) {
|
||||
SHELL = new SocketConsoleApplicationShell(3300, autoClose,
|
||||
ConsoleTestApplication.EXIT, Charset.forName("UTF-8"));
|
||||
final ConsoleTestApplication app = new ConsoleTestApplication(
|
||||
SHELL.getConsoleManager());
|
||||
SHELL.setApplication(app);
|
||||
server = null;
|
||||
/** @param args no argument
|
||||
* @throws IOException if the server starting failed */
|
||||
public static void main(final String... args) throws IOException {
|
||||
try {
|
||||
getServer().join();
|
||||
} catch (final InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return getServer();
|
||||
}
|
||||
|
||||
public static Thread startServer(String closeConnection) throws IOException {
|
||||
if (SHELL == null) {
|
||||
SHELL = new SocketConsoleApplicationShell(3300, closeConnection,
|
||||
ConsoleTestApplication.EXIT, Charset.forName("UTF-8"));
|
||||
final ConsoleTestApplication app = new ConsoleTestApplication(
|
||||
SHELL.getConsoleManager());
|
||||
SHELL.setApplication(app);
|
||||
server = null;
|
||||
}
|
||||
return getServer();
|
||||
}
|
||||
|
||||
public static void closeServer() {
|
||||
SHELL.stop();
|
||||
SHELL = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,35 @@
|
||||
|
||||
<!-- GCLC swt, provide a swt window for console applications -->
|
||||
<!-- Copyright (C) 2015-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. -->
|
||||
|
||||
|
||||
<!-- Copyright E. Bigeon (2015) -->
|
||||
<!-- -->
|
||||
<!-- emmanuel@bigeon.fr -->
|
||||
@@ -35,7 +66,7 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>gclc-swt</artifactId>
|
||||
<version>1.1.2-SNAPSHOT</version>
|
||||
<version>1.1.5-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<url>http://www.bigeon.fr/emmanuel</url>
|
||||
<properties>
|
||||
@@ -43,20 +74,20 @@
|
||||
<project.scm.id>git.bigeon.net</project.scm.id>
|
||||
</properties>
|
||||
<parent>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<groupId>fr.bigeon.config</groupId>
|
||||
<artifactId>ebigeon-config</artifactId>
|
||||
<version>1.7.1</version>
|
||||
<version>1.8.2</version>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<artifactId>gclc</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<version>2.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<artifactId>collections</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<inceptionYear>2015</inceptionYear>
|
||||
@@ -98,4 +129,5 @@
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
<groupId>fr.bigeon.gclc</groupId>
|
||||
</project>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Copyright E. Bigeon (2015)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a swt window for console applications.
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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,
|
||||
@@ -46,12 +43,15 @@ package fr.bigeon.gclc.swt;
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public interface ConsoleDelayIO {
|
||||
/** Actually send the input as the prompt next input. */
|
||||
void validateInput();
|
||||
/** Get the input text.
|
||||
* @return the non validated input */
|
||||
String getInput();
|
||||
|
||||
/** @param input the input to set */
|
||||
/** Set the input text.
|
||||
*
|
||||
* @param input the input to set */
|
||||
void setInput(String input);
|
||||
|
||||
/** @return the non validated input */
|
||||
String getInput();
|
||||
/** Actually send the input as the prompt next input. */
|
||||
void validateInput();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Copyright E. Bigeon (2015)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a swt window for console applications.
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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,
|
||||
@@ -45,40 +42,46 @@ import org.eclipse.swt.events.KeyEvent;
|
||||
import fr.bigeon.collections.ArrayRibbon;
|
||||
import fr.bigeon.collections.Ribbon;
|
||||
|
||||
/** A key listener to validate commands and manage the history of commands
|
||||
/** A key listener to validate commands and manage the history of commands.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class HistoryTextKeyListener extends KeyAdapter {
|
||||
|
||||
/** The size of commands history */
|
||||
/** The size of commands history. */
|
||||
private static final int DEFAULT_HISTORY_SIZE = 10;
|
||||
/** The empty string constant */
|
||||
/** The empty string constant. */
|
||||
private static final String EMPTY = ""; //$NON-NLS-1$
|
||||
/** The history ribbon */
|
||||
/** The history ribbon. */
|
||||
private final Ribbon<String> commands;
|
||||
/** The current index in history search */
|
||||
/** The current index in history search. */
|
||||
private int currentIndex = 0;
|
||||
/** The console to notify of command validation */
|
||||
/** The console to notify of command validation. */
|
||||
private final ConsoleDelayIO console;
|
||||
|
||||
/** @param console the console delayed */
|
||||
public HistoryTextKeyListener(ConsoleDelayIO console) {
|
||||
/** Create the key listener that cycle the history.
|
||||
*
|
||||
* @param console the console delayed */
|
||||
public HistoryTextKeyListener(final ConsoleDelayIO console) {
|
||||
super();
|
||||
this.console = console;
|
||||
this.commands = new ArrayRibbon<>(DEFAULT_HISTORY_SIZE);
|
||||
commands = new ArrayRibbon<>(DEFAULT_HISTORY_SIZE);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.swt.events.KeyAdapter#keyPressed(org.eclipse.swt.events.
|
||||
* KeyEvent) */
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
|
||||
public void keyPressed(final KeyEvent e) {
|
||||
pressedKeyCode(e.keyCode);
|
||||
}
|
||||
|
||||
/** @param keyCode the pressed key code */
|
||||
public void pressedKeyCode(int keyCode) {
|
||||
/** Indicate a pressed key combination.
|
||||
*
|
||||
* @param keyCode the pressed key code */
|
||||
public void pressedKeyCode(final int keyCode) {
|
||||
// Enter validates the command if prompting
|
||||
if (keyCode == '\r') {
|
||||
String input = console.getInput();
|
||||
final String input = console.getInput();
|
||||
if (!input.isEmpty()) {
|
||||
commands.add(input);
|
||||
}
|
||||
@@ -90,7 +93,7 @@ public final class HistoryTextKeyListener extends KeyAdapter {
|
||||
if (keyCode == SWT.ARROW_UP &&
|
||||
currentIndex < commands.size() - 1) {
|
||||
currentIndex++;
|
||||
String cmd = commands.get(commands.size() - currentIndex - 1);
|
||||
final String cmd = commands.get(commands.size() - currentIndex - 1);
|
||||
console.setInput(cmd);
|
||||
}
|
||||
|
||||
@@ -100,7 +103,7 @@ public final class HistoryTextKeyListener extends KeyAdapter {
|
||||
currentIndex--;
|
||||
console.setInput(EMPTY);
|
||||
} else if (currentIndex > 0) {
|
||||
String cmd = commands
|
||||
final String cmd = commands
|
||||
.get(commands.size() - (--currentIndex) - 1);
|
||||
console.setInput(cmd);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Copyright E. Bigeon (2015)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a swt window for console applications.
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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,
|
||||
@@ -43,8 +40,6 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.FocusAdapter;
|
||||
import org.eclipse.swt.events.FocusEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
@@ -53,120 +48,114 @@ import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
|
||||
import fr.bigeon.gclc.ConsoleApplication;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
import fr.bigeon.gclc.tools.ConstantString;
|
||||
import fr.bigeon.gclc.tools.StringProvider;
|
||||
|
||||
/** A SWT component to connect to gclc {@link ConsoleApplication}
|
||||
/** A SWT component to connect to gclc {@link ConsoleApplication}.
|
||||
* <p>
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class SWTConsole extends Composite
|
||||
implements ConsoleDelayIO, ConsoleManager {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final int LAYOUT_NB_COLUMNS = 2;
|
||||
/** The cmd prefix in the output console */
|
||||
private static final String CMD_PREFIX = "[CMD] "; //$NON-NLS-1$
|
||||
/** The class logger */
|
||||
private static final Logger LOGGER = Logger
|
||||
public final class SWTConsole extends Composite
|
||||
implements ConsoleDelayIO, ConsoleInput, ConsoleOutput {
|
||||
/** The number of columns of the layout. */
|
||||
private static final int LAYOUT_NB_COLUMNS = 2;
|
||||
/** The cmd prefix in the output console. */
|
||||
private static final String CMD_PREFIX = "[CMD] "; //$NON-NLS-1$
|
||||
/** The class logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(SWTConsole.class.getName());
|
||||
/** The empty string constant */
|
||||
private static final String EMPTY = ""; //$NON-NLS-1$
|
||||
/** The console output text field */
|
||||
private final Text consoleOutput;
|
||||
/** The console input text field */
|
||||
private final Text consoleInput;
|
||||
/** The prompt label */
|
||||
private final Label lblPromptlabel;
|
||||
/** The prompt text */
|
||||
private String prompt = ">"; //$NON-NLS-1$
|
||||
/** The command entered by the user */
|
||||
private String command = null;
|
||||
/** If the prompt should be active */
|
||||
private boolean prompting = false;
|
||||
/** The object for thread synchronization with the prompt */
|
||||
private final Object promptLock = new Object();
|
||||
/** The empty string constant. */
|
||||
private static final String EMPTY = ""; //$NON-NLS-1$
|
||||
/** The console output text field. */
|
||||
private final Text consoleOutput;
|
||||
/** The console input text field. */
|
||||
private final Text consoleInput;
|
||||
/** The prompt label. */
|
||||
private final Label lblPromptlabel;
|
||||
/** The prompt text. */
|
||||
private StringProvider prompt = new ConstantString("> "); //$NON-NLS-1$
|
||||
/** The command entered by the user. */
|
||||
private String command = null;
|
||||
/** If the prompt should be active. */
|
||||
private boolean prompting = false;
|
||||
/** The object for thread synchronization with the prompt. */
|
||||
private final Object promptLock = new Object();
|
||||
|
||||
/** Create the composite.
|
||||
*
|
||||
* @param parent the prent composite
|
||||
* @param style the composite style */
|
||||
public SWTConsole(Composite parent, int style) {
|
||||
public SWTConsole(final Composite parent, final int style) {
|
||||
super(parent, style);
|
||||
|
||||
setLayout(new GridLayout(LAYOUT_NB_COLUMNS, false));
|
||||
|
||||
consoleOutput = new Text(this, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP |
|
||||
SWT.V_SCROLL | SWT.MULTI);
|
||||
consoleOutput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true,
|
||||
LAYOUT_NB_COLUMNS, 1));
|
||||
consoleOutput = new Text(this,
|
||||
SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI);
|
||||
consoleOutput.setLayoutData(
|
||||
new GridData(SWT.FILL, SWT.FILL, true, true, LAYOUT_NB_COLUMNS, 1));
|
||||
consoleOutput.setRedraw(true);
|
||||
consoleOutput.addFocusListener(new FocusAdapter() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
consoleInput.setFocus();
|
||||
}
|
||||
});
|
||||
|
||||
lblPromptlabel = new Label(this, SWT.NONE);
|
||||
lblPromptlabel.setText(prompt);
|
||||
lblPromptlabel.setText(prompt.apply());
|
||||
|
||||
consoleInput = new Text(this, SWT.BORDER);
|
||||
consoleInput.setLayoutData(
|
||||
new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
|
||||
consoleInput.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
|
||||
consoleInput.addKeyListener(new HistoryTextKeyListener(this));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void validateInput() {
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
consoleInput.setEnabled(false);
|
||||
}
|
||||
});
|
||||
synchronized (promptLock) {
|
||||
while (!prompting) {
|
||||
try {
|
||||
promptLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE,
|
||||
"Interruption while waiting prompt", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
command = consoleInput.getText();
|
||||
prompting = false;
|
||||
consoleInput.setText(EMPTY);
|
||||
consoleOutput.append(
|
||||
CMD_PREFIX + command + System.lineSeparator());
|
||||
}
|
||||
});
|
||||
promptLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkSubclass() {
|
||||
// Disable the check that prevents subclassing of SWT components
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#close() */
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (promptLock) {
|
||||
promptLock.notifyAll();
|
||||
}
|
||||
if (consoleInput.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
consoleInput.setEnabled(false);
|
||||
consoleOutput.setEnabled(false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.swt.ConsoleDelayIO#getInput() */
|
||||
@Override
|
||||
public String getInput() {
|
||||
return consoleInput.getText();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#getPrompt() */
|
||||
@Override
|
||||
public String getPrompt() {
|
||||
public StringProvider getPrompt() {
|
||||
return prompt;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
synchronized (promptLock) {
|
||||
promptLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return isDisposed();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#print(java.lang.String) */
|
||||
@Override
|
||||
@@ -223,22 +212,68 @@ public class SWTConsole extends Composite
|
||||
}
|
||||
try {
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
if (!consoleInput.isDisposed()) {
|
||||
consoleInput.setEnabled(true);
|
||||
lblPromptlabel.setText(prompt.apply());
|
||||
// relayout
|
||||
SWTConsole.this.layout();
|
||||
consoleInput.setFocus();
|
||||
}
|
||||
}
|
||||
});
|
||||
prompting = true;
|
||||
promptLock.notifyAll();
|
||||
promptLock.wait();
|
||||
while (prompting) {
|
||||
promptLock.wait();
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Error in synchronization of prompting", e); //$NON-NLS-1$
|
||||
LOGGER.log(Level.WARNING, "Error in synchronization of prompting", e); //$NON-NLS-1$
|
||||
command = null;
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (isDisposed()) {
|
||||
throw new IOException("Input closed"); //$NON-NLS-1$
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */
|
||||
@Override
|
||||
public String prompt(final long timeout) throws IOException {
|
||||
synchronized (promptLock) {
|
||||
if (isDisposed()) {
|
||||
throw new IOException();
|
||||
}
|
||||
try {
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!consoleInput.isDisposed()) {
|
||||
consoleInput.setEnabled(true);
|
||||
lblPromptlabel.setText(prompt.apply());
|
||||
// relayout
|
||||
SWTConsole.this.layout();
|
||||
consoleInput.setFocus();
|
||||
}
|
||||
}
|
||||
});
|
||||
prompting = true;
|
||||
command = null;
|
||||
promptLock.notifyAll();
|
||||
long start = System.currentTimeMillis();
|
||||
long cur = start;
|
||||
while (prompting && start + timeout>cur) {
|
||||
promptLock.wait((cur-start-timeout)/2);
|
||||
cur = System.currentTimeMillis();
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.WARNING, "Error in synchronization of prompting", e); //$NON-NLS-1$
|
||||
command = null;
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (isDisposed()) {
|
||||
@@ -275,16 +310,16 @@ public class SWTConsole extends Composite
|
||||
throw new IOException();
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Error in synchronization of prompting", e); //$NON-NLS-1$
|
||||
LOGGER.log(Level.WARNING, "Error in synchronization of prompting", e); //$NON-NLS-1$
|
||||
command = null;
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
if (!consoleOutput.isDisposed()) {
|
||||
lblPromptlabel.setText(prompt);
|
||||
lblPromptlabel.setText(prompt.apply());
|
||||
// relayout
|
||||
SWTConsole.this.layout();
|
||||
}
|
||||
@@ -295,6 +330,15 @@ public class SWTConsole extends Composite
|
||||
return command;
|
||||
}
|
||||
|
||||
/* (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 {
|
||||
// TODO Auto-generated method stub
|
||||
// return null;
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.swt.widgets.Composite#setFocus() */
|
||||
@Override
|
||||
@@ -302,17 +346,25 @@ public class SWTConsole extends Composite
|
||||
return consoleInput.setFocus();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.swt.ConsoleDelayIO#setInput(java.lang.String) */
|
||||
@Override
|
||||
public void setInput(final String input) {
|
||||
consoleInput.setText(input);
|
||||
consoleInput.setSelection(input.length());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#setPrompt(java.lang.String) */
|
||||
@Override
|
||||
public void setPrompt(final String prompt) {
|
||||
public void setPrompt(final StringProvider prompt) {
|
||||
this.prompt = prompt;
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
if (!consoleOutput.isDisposed()) {
|
||||
lblPromptlabel.setText(prompt);
|
||||
lblPromptlabel.setText(prompt.apply());
|
||||
// relayout
|
||||
SWTConsole.this.layout();
|
||||
}
|
||||
@@ -320,60 +372,54 @@ public class SWTConsole extends Composite
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#close() */
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (promptLock) {
|
||||
promptLock.notify();
|
||||
}
|
||||
if (consoleInput.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
consoleInput.setEnabled(false);
|
||||
consoleOutput.setEnabled(false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return isDisposed();
|
||||
public void setPrompt(String prompt) {
|
||||
setPrompt(new ConstantString(prompt));
|
||||
}
|
||||
|
||||
/** @param string the text */
|
||||
public void setText(String string) {
|
||||
public void setText(final String string) {
|
||||
consoleInput.setText(string);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public void validateCommand() {
|
||||
validateInput();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.swt.ConsoleDelayIO#setInput(java.lang.String) */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void setInput(String input) {
|
||||
consoleInput.setText(input);
|
||||
consoleInput.setSelection(input.length());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.swt.ConsoleDelayIO#getInput() */
|
||||
@Override
|
||||
public String getInput() {
|
||||
return consoleInput.getText();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
public void validateInput() {
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
consoleInput.setEnabled(false);
|
||||
}
|
||||
});
|
||||
synchronized (promptLock) {
|
||||
promptLock.notify();
|
||||
while (!prompting) {
|
||||
try {
|
||||
promptLock.wait();
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Interruption while waiting prompt", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
command = consoleInput.getText();
|
||||
prompting = false;
|
||||
consoleInput.setText(EMPTY);
|
||||
consoleOutput.append(CMD_PREFIX + command + System.lineSeparator());
|
||||
}
|
||||
});
|
||||
promptLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Copyright E. Bigeon (2015)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a swt window for console applications.
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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,
|
||||
@@ -43,13 +40,11 @@ import org.eclipse.swt.layout.FillLayout;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
|
||||
/** A shell containing a {@link SWTConsole}
|
||||
* <p>
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class SWTConsoleShell extends Shell {
|
||||
public final class SWTConsoleShell extends Shell {
|
||||
|
||||
/** The console component */
|
||||
private SWTConsole console;
|
||||
@@ -57,7 +52,7 @@ public class SWTConsoleShell extends Shell {
|
||||
/** Create the shell.
|
||||
*
|
||||
* @param display the display */
|
||||
public SWTConsoleShell(Display display) {
|
||||
public SWTConsoleShell(final Display display) {
|
||||
super(display, SWT.SHELL_TRIM);
|
||||
setLayout(new FillLayout(SWT.HORIZONTAL));
|
||||
|
||||
@@ -75,11 +70,6 @@ public class SWTConsoleShell extends Shell {
|
||||
setText("Console Application"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/** @return the console manager */
|
||||
public ConsoleManager getManager() {
|
||||
return console;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.swt.widgets.Shell#dispose() */
|
||||
@Override
|
||||
@@ -87,4 +77,9 @@ public class SWTConsoleShell extends Shell {
|
||||
super.dispose();
|
||||
console.close();
|
||||
}
|
||||
|
||||
/** @return the input and output. */
|
||||
public SWTConsole getManager() {
|
||||
return console;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Copyright E. Bigeon (2015)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a swt window for console applications.
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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,
|
||||
@@ -43,8 +40,6 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.FocusAdapter;
|
||||
import org.eclipse.swt.events.FocusEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
@@ -52,16 +47,17 @@ import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
|
||||
import fr.bigeon.gclc.ConsoleApplication;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleInput;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleOutput;
|
||||
import fr.bigeon.gclc.tools.AOutputForwardRunnable;
|
||||
|
||||
/** A SWT component to connect to gclc {@link ConsoleApplication}
|
||||
* <p>
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
public final class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
/** The local implementation of the forwarding runnable
|
||||
*
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
private final class ToSWTConsoleForwardRunnable
|
||||
extends AOutputForwardRunnable {
|
||||
@@ -69,12 +65,12 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
private boolean running = true;
|
||||
|
||||
/** @param manager the manager */
|
||||
public ToSWTConsoleForwardRunnable(PipedConsoleManager manager) {
|
||||
public ToSWTConsoleForwardRunnable(final PipedConsoleOutput manager) {
|
||||
super(manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void forwardLine(String m) {
|
||||
protected void forwardLine(final String m) {
|
||||
appendConsoleOutput(m);
|
||||
}
|
||||
|
||||
@@ -84,7 +80,7 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
}
|
||||
|
||||
/** @param running the running to set */
|
||||
public void setRunning(boolean running) {
|
||||
public void setRunning(final boolean running) {
|
||||
this.running = running;
|
||||
}
|
||||
}
|
||||
@@ -96,8 +92,8 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
private final Text consoleOutput;
|
||||
/** The console input text field */
|
||||
private final Text consoleInput;
|
||||
/** The actual manager */
|
||||
private PipedConsoleManager manager;
|
||||
/** The input. */
|
||||
private PipedConsoleInput input;
|
||||
/** The forwarding runnable */
|
||||
private ToSWTConsoleForwardRunnable forward;
|
||||
|
||||
@@ -105,7 +101,7 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
*
|
||||
* @param parent the prent composite
|
||||
* @param style the composite style */
|
||||
public SWTConsoleView(Composite parent, int style) {
|
||||
public SWTConsoleView(final Composite parent, final int style) {
|
||||
super(parent, style);
|
||||
|
||||
setLayout(new GridLayout(1, false));
|
||||
@@ -115,13 +111,6 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
consoleOutput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true,
|
||||
1, 1));
|
||||
consoleOutput.setRedraw(true);
|
||||
consoleOutput.addFocusListener(new FocusAdapter() {
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
consoleInput.setFocus();
|
||||
}
|
||||
});
|
||||
|
||||
consoleInput = new Text(this, SWT.BORDER);
|
||||
consoleInput.setLayoutData(
|
||||
@@ -129,17 +118,6 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
consoleInput.addKeyListener(new HistoryTextKeyListener(this));
|
||||
}
|
||||
|
||||
/** @param manager the manager to set */
|
||||
public void setManager(PipedConsoleManager manager) {
|
||||
this.manager = manager;
|
||||
if (forward != null) {
|
||||
forward.setRunning(false);
|
||||
}
|
||||
forward = new ToSWTConsoleForwardRunnable(manager);
|
||||
Thread th = new Thread(forward, "gclcToSWT"); //$NON-NLS-1$
|
||||
th.start();
|
||||
}
|
||||
|
||||
/** @param next the next message */
|
||||
protected void appendConsoleOutput(final String next) {
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@@ -151,23 +129,18 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void validateInput() {
|
||||
try {
|
||||
manager.type(getInput());
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to input value to console", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkSubclass() {
|
||||
// Disable the check that prevents subclassing of SWT components
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.swt.ConsoleDelayIO#getInput() */
|
||||
@Override
|
||||
public String getInput() {
|
||||
return consoleInput.getText();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.swt.widgets.Composite#setFocus() */
|
||||
@Override
|
||||
@@ -175,30 +148,43 @@ public class SWTConsoleView extends Composite implements ConsoleDelayIO {
|
||||
return consoleInput.setFocus();
|
||||
}
|
||||
|
||||
/** @param string the text */
|
||||
public void setText(String string) {
|
||||
consoleInput.setText(string);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void validateCommand() {
|
||||
validateInput();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.swt.ConsoleDelayIO#setInput(java.lang.String) */
|
||||
@Override
|
||||
public void setInput(String input) {
|
||||
public void setInput(final String input) {
|
||||
consoleInput.setText(input);
|
||||
consoleInput.setSelection(input.length());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.swt.ConsoleDelayIO#getInput() */
|
||||
/** Set the input and output.
|
||||
*
|
||||
* @param manager the output to set
|
||||
* @param input the input */
|
||||
public void setManager(final PipedConsoleOutput manager,
|
||||
final PipedConsoleInput input) {
|
||||
this.input = input;
|
||||
if (forward != null) {
|
||||
forward.setRunning(false);
|
||||
}
|
||||
forward = new ToSWTConsoleForwardRunnable(manager);
|
||||
final Thread th = new Thread(forward, "gclcToSWT"); //$NON-NLS-1$
|
||||
th.start();
|
||||
}
|
||||
|
||||
/** @param string the text */
|
||||
public void setText(final String string) {
|
||||
consoleInput.setText(string);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public String getInput() {
|
||||
return consoleInput.getText();
|
||||
public void validateInput() {
|
||||
try {
|
||||
input.type(getInput());
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to input value to console", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
gclc-swt/src/main/java/fr/bigeon/gclc/swt/package-info.java
Normal file
39
gclc-swt/src/main/java/fr/bigeon/gclc/swt/package-info.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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-swt:fr.bigeon.gclc.swt.package-info.java
|
||||
* Created on: Nov 18, 2017
|
||||
*/
|
||||
/** SWT components for a frontend of GCLC applications.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
package fr.bigeon.gclc.swt;
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Copyright E. Bigeon (2015)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a swt window for console applications.
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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,
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Copyright E. Bigeon (2015)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a swt window for console applications.
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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,
|
||||
@@ -53,8 +50,11 @@ import fr.bigeon.gclc.command.Command;
|
||||
import fr.bigeon.gclc.command.ExitCommand;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
/**
|
||||
* <p>
|
||||
* TODO
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
@@ -65,64 +65,192 @@ public class SWTConsoleShellTest {
|
||||
private static final Display DISPLAY = Display.getDefault();
|
||||
|
||||
@Test
|
||||
public void testConsoleClose() {
|
||||
public void test() {
|
||||
final SWTConsoleShell shell = new SWTConsoleShell(DISPLAY);
|
||||
final SWTConsole swtConsole = (SWTConsole) shell.getManager();
|
||||
swtConsole.close();
|
||||
swtConsole.setPrompt(":");
|
||||
final SWTConsole swtConsole = shell.getManager();
|
||||
try {
|
||||
final ConsoleApplication appl = new ConsoleApplication(swtConsole,
|
||||
"Hello", "See you");
|
||||
swtConsole, "Hello", "See you");
|
||||
appl.add(new ExitCommand("exit", appl));
|
||||
appl.add(new Command("long") {
|
||||
|
||||
/* (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 {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "a long running command";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
public void execute(String... args) {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
appl.add(new Command("test") {
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "a prompting running command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
try {
|
||||
appl.getManager().prompt("Test");
|
||||
} catch (IOException e) {
|
||||
throw new CommandRunException("No input", e, this);
|
||||
}
|
||||
protected String usageDetail() {
|
||||
// TODO Auto-generated method stub
|
||||
// return null;
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
});
|
||||
// shell.pack();
|
||||
shell.open();
|
||||
Thread applThread = new Thread(new Runnable() {
|
||||
final Thread applThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
appl.start();
|
||||
}
|
||||
});
|
||||
Thread testThread = new Thread(new Runnable() {
|
||||
final Thread testThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
swtConsole.setText("test"); //$NON-NLS-1$
|
||||
swtConsole.validateCommand();
|
||||
}
|
||||
});
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shell.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
applThread.start();
|
||||
testThread.start();
|
||||
while (!shell.isDisposed()) {
|
||||
if (!DISPLAY.readAndDispatch()) {
|
||||
DISPLAY.sleep();
|
||||
}
|
||||
}
|
||||
// DISPLAY.dispose();
|
||||
assertTrue(swtConsole.isClosed());
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
assertFalse(appl.isRunning());
|
||||
} catch (final InvalidCommandName e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsoleClose() {
|
||||
final SWTConsoleShell shell = new SWTConsoleShell(DISPLAY);
|
||||
final SWTConsole swtConsole = shell.getManager();
|
||||
swtConsole.close();
|
||||
swtConsole.setPrompt(":");
|
||||
try {
|
||||
final ConsoleApplication appl = new ConsoleApplication(swtConsole,
|
||||
swtConsole, "Hello", "See you");
|
||||
appl.add(new ExitCommand("exit", appl));
|
||||
appl.add(new Command("long") {
|
||||
/* (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 {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "a long running command";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
// TODO Auto-generated method stub
|
||||
// return null;
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
});
|
||||
appl.add(new Command("test") {
|
||||
/* (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 {
|
||||
try {
|
||||
swtConsole.prompt("Test");
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException("No input", e, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "a prompting running command";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
// TODO Auto-generated method stub
|
||||
// return null;
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
});
|
||||
// shell.pack();
|
||||
shell.open();
|
||||
final Thread applThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
appl.start();
|
||||
}
|
||||
});
|
||||
final Thread testThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -163,7 +291,7 @@ public class SWTConsoleShellTest {
|
||||
swtConsole.validateCommand();
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -187,102 +315,17 @@ public class SWTConsoleShellTest {
|
||||
try {
|
||||
swtConsole.prompt();
|
||||
fail("Prompting when closed should fail!");
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException e) {
|
||||
assertNotNull(e);
|
||||
}
|
||||
// DISPLAY.dispose();
|
||||
assertTrue(appl.getManager().isClosed());
|
||||
assertTrue(swtConsole.isClosed());
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
assertFalse(appl.isRunning());
|
||||
} catch (InvalidCommandName e) {
|
||||
} catch (final InvalidCommandName e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
final SWTConsoleShell shell = new SWTConsoleShell(DISPLAY);
|
||||
final SWTConsole swtConsole = (SWTConsole) shell.getManager();
|
||||
try {
|
||||
final ConsoleApplication appl = new ConsoleApplication(swtConsole,
|
||||
"Hello", "See you");
|
||||
appl.add(new ExitCommand("exit", appl));
|
||||
appl.add(new Command("long") {
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "a long running command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
// shell.pack();
|
||||
shell.open();
|
||||
Thread applThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
appl.start();
|
||||
}
|
||||
});
|
||||
Thread testThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
swtConsole.setText("test"); //$NON-NLS-1$
|
||||
swtConsole.validateCommand();
|
||||
}
|
||||
});
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
Display.getDefault().syncExec(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shell.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
applThread.start();
|
||||
testThread.start();
|
||||
while (!shell.isDisposed()) {
|
||||
if (!DISPLAY.readAndDispatch()) {
|
||||
DISPLAY.sleep();
|
||||
}
|
||||
}
|
||||
// DISPLAY.dispose();
|
||||
assertTrue(appl.getManager().isClosed());
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
assertFalse(appl.isRunning());
|
||||
} catch (InvalidCommandName e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Copyright E. Bigeon (2015)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a swt window for console applications.
|
||||
* GCLC swt, provide a swt window for console applications
|
||||
* Copyright (C) 2015-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,
|
||||
@@ -38,6 +35,8 @@
|
||||
*/
|
||||
package fr.bigeon.gclc.swt;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
@@ -48,8 +47,12 @@ import org.junit.Test;
|
||||
import fr.bigeon.gclc.ConsoleApplication;
|
||||
import fr.bigeon.gclc.command.Command;
|
||||
import fr.bigeon.gclc.command.ExitCommand;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleInput;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
* TODO
|
||||
@@ -65,44 +68,65 @@ public class SWTConsoleViewTest {
|
||||
public void test() {
|
||||
final Shell shell = new Shell(DISPLAY);
|
||||
final SWTConsoleView swtConsole = new SWTConsoleView(shell, SWT.NONE);
|
||||
try (PipedConsoleManager manager = new PipedConsoleManager()) {
|
||||
swtConsole.setManager(manager);
|
||||
try (PipedConsoleOutput manager = new PipedConsoleOutput();
|
||||
PipedConsoleInput input = new PipedConsoleInput(System.out)) {
|
||||
swtConsole.setManager(manager, input);
|
||||
} catch (final IOException e2) {
|
||||
assertNull(e2);
|
||||
}
|
||||
try (PipedConsoleOutput manager = new PipedConsoleOutput();
|
||||
PipedConsoleInput input = new PipedConsoleInput(System.out)) {
|
||||
swtConsole.setManager(manager, input);
|
||||
final ConsoleApplication appl = new ConsoleApplication(manager,
|
||||
input,
|
||||
"Hello", "See you");
|
||||
appl.add(new ExitCommand("exit", appl));
|
||||
appl.add(new Command("long") {
|
||||
|
||||
/* (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 {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "a long running command";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
public void execute(String... args) {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
protected String usageDetail() {
|
||||
// TODO Auto-generated method stub
|
||||
// return null;
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
});
|
||||
// shell.pack();
|
||||
shell.open();
|
||||
Thread applThread = new Thread(new Runnable() {
|
||||
final Thread applThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
appl.start();
|
||||
}
|
||||
});
|
||||
Thread testThread = new Thread(new Runnable() {
|
||||
final Thread testThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -110,12 +134,12 @@ public class SWTConsoleViewTest {
|
||||
@Override
|
||||
public void run() {
|
||||
swtConsole.setText("test"); //$NON-NLS-1$
|
||||
swtConsole.validateCommand();
|
||||
swtConsole.validateInput();
|
||||
}
|
||||
});
|
||||
try {
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -134,10 +158,10 @@ public class SWTConsoleViewTest {
|
||||
DISPLAY.sleep();
|
||||
}
|
||||
}
|
||||
} catch (InvalidCommandName e) {
|
||||
} catch (final InvalidCommandName e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e1) {
|
||||
} catch (final IOException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<dependency>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<artifactId>gclc</artifactId>
|
||||
<version>1.3.1</version>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<name>GCLC system command</name>
|
||||
|
||||
@@ -15,7 +15,8 @@ import java.util.logging.Logger;
|
||||
import fr.bigeon.gclc.command.Command;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.CommandRunExceptionType;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** A command that will execute a system command.
|
||||
*
|
||||
@@ -29,71 +30,68 @@ public class ExecSystemCommand extends Command {
|
||||
/** The class logger */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(ExecSystemCommand.class.getName());
|
||||
/** The manager for the application's user interface */
|
||||
private final ConsoleManager manager;
|
||||
|
||||
/** @param name the name of the command (the input from the manager that
|
||||
* should trigger this command)
|
||||
* @param manager the console manager for input and outputs */
|
||||
public ExecSystemCommand(String name, ConsoleManager manager) {
|
||||
super(name);
|
||||
this.manager = manager;
|
||||
/***/
|
||||
public ExecSystemCommand() {
|
||||
super(COMMAND_DEFAULT_NAME);
|
||||
}
|
||||
|
||||
/** @param manager the console manager for input and outputs */
|
||||
public ExecSystemCommand(ConsoleManager manager) {
|
||||
super(COMMAND_DEFAULT_NAME);
|
||||
this.manager = manager;
|
||||
/** @param name the name of the command (the input from the manager that
|
||||
* should trigger this command) */
|
||||
public ExecSystemCommand(final String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#execute(java.lang.String[]) */
|
||||
@SuppressWarnings("resource")
|
||||
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.manager.
|
||||
* ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
|
||||
* java.lang.String[]) */
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
public void execute(final ConsoleOutput out, final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
Process proc;
|
||||
try {
|
||||
proc = Runtime.getRuntime().exec(args);
|
||||
} catch (IOException e2) {
|
||||
} catch (final IOException e2) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to run process", e2); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
|
||||
final InputStream is = proc
|
||||
.getInputStream();
|
||||
Thread th = new Thread(new Runnable() {
|
||||
final Thread th = new Thread(new Runnable() {
|
||||
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
readToEnd(is);
|
||||
readToEnd(out, is);
|
||||
is.close();
|
||||
} catch (CommandRunException e) {
|
||||
} catch (final CommandRunException e) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Manager was closed in the meantime...", e); //$NON-NLS-1$
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Input stream was closed...", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
});
|
||||
th.start();
|
||||
manager.setPrompt(""); //$NON-NLS-1$
|
||||
in.setPrompt(""); //$NON-NLS-1$
|
||||
final OutputStream os = proc.getOutputStream();
|
||||
try (BufferedWriter writer = new BufferedWriter(
|
||||
new OutputStreamWriter(os))) {
|
||||
while (th.isAlive()) {
|
||||
String user;
|
||||
try {
|
||||
user = manager.prompt();
|
||||
} catch (IOException e) {
|
||||
user = in.prompt();
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException(
|
||||
CommandRunExceptionType.INTERACTION,
|
||||
"manager was closed", e, this); //$NON-NLS-1$
|
||||
}
|
||||
writer.write(user + EOL);
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
} catch (final IOException e1) {
|
||||
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
|
||||
"manager was closed", e1, this); //$NON-NLS-1$
|
||||
}
|
||||
@@ -102,23 +100,31 @@ public class ExecSystemCommand extends Command {
|
||||
/** @param is the input stream
|
||||
* @throws CommandRunException if the manager was closed while writing the
|
||||
* stream */
|
||||
protected void readToEnd(InputStream is) throws CommandRunException {
|
||||
protected void readToEnd(final ConsoleOutput out,
|
||||
final InputStream is) throws CommandRunException {
|
||||
int c;
|
||||
try {
|
||||
while ((c = is.read()) != -1) {
|
||||
try {
|
||||
manager.print(Character.valueOf((char) c).toString());
|
||||
} catch (IOException e) {
|
||||
out.print(Character.valueOf((char) c).toString());
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException(
|
||||
CommandRunExceptionType.INTERACTION,
|
||||
"manager was closed", e, this); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.INFO, "input stream reading failed", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@Override
|
||||
public String tip() {
|
||||
return "Execute a system command"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
@@ -139,11 +145,4 @@ public class ExecSystemCommand extends Command {
|
||||
return " CMD <system command>"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@Override
|
||||
public String tip() {
|
||||
return "Execute a system command"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>gclc</artifactId>
|
||||
<version>1.3.3-SNAPSHOT</version>
|
||||
<version>2.0.6-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<url>http://www.bigeon.fr/emmanuel</url>
|
||||
<properties>
|
||||
@@ -53,7 +53,7 @@
|
||||
<parent>
|
||||
<groupId>fr.bigeon</groupId>
|
||||
<artifactId>ebigeon-config</artifactId>
|
||||
<version>1.7.0</version>
|
||||
<version>1.7.1</version>
|
||||
</parent>
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -83,6 +83,6 @@
|
||||
<scm>
|
||||
|
||||
<developerConnection>scm:git:gogs@git.code.bigeon.net:emmanuel/gclc.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
<tag>gclc-1.3.5</tag>
|
||||
</scm>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.ApplicationAttachement.java
|
||||
* Created on: Apr 19, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc;
|
||||
|
||||
import fr.bigeon.gclc.command.ICommandProvider;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
|
||||
/** Represent a functionnality set that can be added to a console application.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public interface ApplicationAttachement {
|
||||
/** Attach this object to a console application.
|
||||
* <p>
|
||||
* The attaching usually consist in the addition of commands in the console
|
||||
* application. The attached command should be specific to the attachement
|
||||
* (typically, the generic help command or the script command should not be
|
||||
* added through this mechanism).
|
||||
*
|
||||
* @param application the application
|
||||
* @throws InvalidCommandName if a command name is invalid for the
|
||||
* application. */
|
||||
void attach(ICommandProvider application) throws InvalidCommandName;
|
||||
}
|
||||
@@ -38,13 +38,12 @@
|
||||
*/
|
||||
package fr.bigeon.gclc;
|
||||
|
||||
/** <p>
|
||||
* Command Request Listeners are listeners that are notified before a command is
|
||||
* executed by the ConsoleApplication,
|
||||
/** Command Request Listeners are listeners that are notified before a command
|
||||
* is executed by the ConsoleApplication.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public interface CommandRequestListener {
|
||||
/** Indicates that the given command was requested to the application
|
||||
/** Indicates that the given command was requested to the application.
|
||||
*
|
||||
* @param command the command */
|
||||
void commandRequest(String command);
|
||||
|
||||
@@ -52,19 +52,21 @@ import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.CommandRunExceptionType;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.i18n.Messages;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
/**
|
||||
* A {@link ConsoleApplication} is an application that require the user to input
|
||||
* commands.
|
||||
* <p>
|
||||
* A typical use case is the following:
|
||||
*
|
||||
* <pre>
|
||||
* {@link ConsoleManager} manager = new {@link fr.bigeon.gclc.manager.SystemConsoleManager#SystemConsoleManager()}
|
||||
* {@link ConsoleApplication} app = new {@link ConsoleApplication#ConsoleApplication(ConsoleManager, String, String) ConsoleApplication(manager, "welcome", "see you latter")};
|
||||
* {@link ConsoleOutput} out = new {@link fr.bigeon.gclc.manager.StreamConsoleOutput StreamConsoleOutput}();
|
||||
* {@link ConsoleInput} in = new {@link fr.bigeon.gclc.manager.StreamConsoleInput StreamConsoleInput}();
|
||||
* {@link ConsoleApplication} app = new {@link ConsoleApplication}(out, in, "welcome", "see you latter")};
|
||||
* app.{@link ConsoleApplication#add(ICommand) add}("my_command", new {@link ICommand MyCommand()});
|
||||
* app.{@link ConsoleApplication#start start}();
|
||||
* app.{@link ConsoleApplication#start() start()};
|
||||
* </pre>
|
||||
* <p>
|
||||
* That will launch in the console application that will display "welcome",
|
||||
@@ -73,42 +75,51 @@ import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
* start method.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class ConsoleApplication implements ICommandProvider {
|
||||
public final class ConsoleApplication implements ICommandProvider {
|
||||
|
||||
/** The class logger */
|
||||
/** The class logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(ConsoleApplication.class.getName());
|
||||
/** The welcome message */
|
||||
private final String header;
|
||||
/** The good bye message */
|
||||
private final String footer;
|
||||
/** The console manager */
|
||||
protected final ConsoleManager manager;
|
||||
/** The container of commands */
|
||||
private final SubedCommand root;
|
||||
/** The state of this application */
|
||||
/** The welcome message. */
|
||||
public final String header;
|
||||
/** The good bye message. */
|
||||
public final String footer;
|
||||
/** The standard output for the application. */
|
||||
private final ConsoleOutput out;
|
||||
/** The standard input for the application. */
|
||||
private final ConsoleInput in;
|
||||
/** The container of commands. */
|
||||
public final SubedCommand root;
|
||||
/** The state of this application. */
|
||||
private boolean running;
|
||||
/** The listeners */
|
||||
/** The listeners. */
|
||||
private final List<CommandRequestListener> listeners = new ArrayList<>();
|
||||
|
||||
/** @param manager the manager
|
||||
/** Create a console application.
|
||||
*
|
||||
* @param out the output
|
||||
* @param in the input
|
||||
* @param welcome the welcoming message
|
||||
* @param goodbye the goodbye message */
|
||||
public ConsoleApplication(ConsoleManager manager, String welcome,
|
||||
String goodbye) {
|
||||
this.header = welcome;
|
||||
this.footer = goodbye;
|
||||
this.manager = manager;
|
||||
public ConsoleApplication(final ConsoleOutput out, final ConsoleInput in,
|
||||
final String welcome,
|
||||
final String goodbye) {
|
||||
header = welcome;
|
||||
footer = goodbye;
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
root = new SubedCommand(""); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean add(ICommand cmd) throws InvalidCommandName {
|
||||
public boolean add(final ICommand cmd) throws InvalidCommandName {
|
||||
return root.add(cmd);
|
||||
}
|
||||
|
||||
/** @param listener the command listener */
|
||||
public final void addListener(CommandRequestListener listener) {
|
||||
/** Add a listener for command requests.
|
||||
*
|
||||
* @param listener the command listener */
|
||||
public void addListener(final CommandRequestListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@@ -116,98 +127,70 @@ public class ConsoleApplication implements ICommandProvider {
|
||||
* @see fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String,
|
||||
* java.lang.String[]) */
|
||||
@Override
|
||||
public final void executeSub(String command,
|
||||
String... args) throws CommandRunException {
|
||||
root.executeSub(command, args);
|
||||
public void executeSub(final ConsoleOutput output, final ConsoleInput input,
|
||||
final String command,
|
||||
final String... args) throws CommandRunException {
|
||||
root.executeSub(output, input, command, args);
|
||||
}
|
||||
|
||||
/** Signify to the application that no command should be inputed anymore */
|
||||
public final void exit() {
|
||||
/** Signify to the application that no command should be inputed anymore. */
|
||||
public void exit() {
|
||||
LOGGER.fine("Request exiting application..."); //$NON-NLS-1$
|
||||
running = false;
|
||||
manager.interruptPrompt();
|
||||
in.interruptPrompt();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommandProvider#get(java.lang.String) */
|
||||
@Override
|
||||
public final ICommand get(String command) {
|
||||
public ICommand get(final String command) {
|
||||
return root.get(command);
|
||||
}
|
||||
|
||||
/** @return the manager */
|
||||
public final ConsoleManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/** @param cmd the command
|
||||
/** Interpret a command line.
|
||||
* <p>
|
||||
* This method will split the command in its part and execute the command
|
||||
* with {@link #executeSub(ConsoleOutput, ConsoleInput, String, String...)}.
|
||||
*
|
||||
* @param cmd the command
|
||||
* @throws IOException if the command could not be parsed */
|
||||
public final void interpretCommand(String cmd) throws IOException {
|
||||
public void interpretCommand(final String cmd) throws IOException {
|
||||
List<String> args;
|
||||
try {
|
||||
args = GCLCConstants.splitCommand(cmd);
|
||||
} catch (CommandParsingException e1) {
|
||||
manager.println("Command line cannot be parsed"); //$NON-NLS-1$
|
||||
LOGGER.log(Level.INFO, "Invalid user command " + cmd, e1); //$NON-NLS-1$
|
||||
} catch (final CommandParsingException e1) {
|
||||
out.println("Command line cannot be parsed"); //$NON-NLS-1$
|
||||
LOGGER.log(Level.FINE, "Invalid user command " + cmd, e1); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
if (!args.isEmpty()) {
|
||||
try {
|
||||
executeSub(args.get(0), Arrays.copyOfRange(
|
||||
executeSub(out, in, args.get(0), Arrays.copyOfRange(
|
||||
args.toArray(new String[0]), 1, args.size()));
|
||||
} catch (final CommandRunException e) {
|
||||
LOGGER.log(Level.WARNING, "Command failed: " + cmd, e); //$NON-NLS-1$
|
||||
manager.println(Messages
|
||||
LOGGER.log(Level.FINE, "Command failed: " + cmd, e); //$NON-NLS-1$
|
||||
out.println(Messages
|
||||
.getString("ConsoleApplication.cmd.failed", cmd)); //$NON-NLS-1$
|
||||
manager.println(e.getLocalizedMessage());
|
||||
out.println(e.getLocalizedMessage());
|
||||
if (e.getType() == CommandRunExceptionType.USAGE) {
|
||||
e.getSource().help(getManager());
|
||||
e.getSource().help(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param listener the command listener to remove */
|
||||
public final void removeListener(CommandRequestListener listener) {
|
||||
for (int i = 0; i < listeners.size(); i++) {
|
||||
if (listeners.get(i) == listener) {
|
||||
listeners.remove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/** Test if the application is running.
|
||||
*
|
||||
* @return the running status */
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
/** Start the application */
|
||||
public final void start() {
|
||||
try {
|
||||
running = true;
|
||||
if (header != null) {
|
||||
manager.println(header);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// The manager was closed
|
||||
running = false;
|
||||
LOGGER.log(Level.WARNING,
|
||||
"The console manager was closed. Closing the application as no one can reach it.", //$NON-NLS-1$
|
||||
e);
|
||||
return;
|
||||
}
|
||||
do {
|
||||
runLoop();
|
||||
} while (running);
|
||||
if (footer != null) {
|
||||
try {
|
||||
manager.println(footer);
|
||||
} catch (IOException e) {
|
||||
// The manager was closed
|
||||
running = false;
|
||||
LOGGER.log(Level.WARNING,
|
||||
"The console manager was closed.", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
}
|
||||
running = false;
|
||||
LOGGER.fine("Exiting application."); //$NON-NLS-1$
|
||||
/** Remove a listener from this application.
|
||||
*
|
||||
* @param listener the command listener to remove */
|
||||
public void removeListener(final CommandRequestListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
/** The running loop content.
|
||||
@@ -216,7 +199,7 @@ public class ConsoleApplication implements ICommandProvider {
|
||||
* (restarting the loop). */
|
||||
private void runLoop() {
|
||||
try {
|
||||
final String cmd = manager.prompt();
|
||||
final String cmd = in.prompt();
|
||||
if (cmd == null || cmd.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -224,36 +207,55 @@ public class ConsoleApplication implements ICommandProvider {
|
||||
listener.commandRequest(cmd);
|
||||
}
|
||||
interpretCommand(cmd);
|
||||
} catch (InterruptedIOException e) {
|
||||
LOGGER.log(Level.INFO,
|
||||
"Prompt interrupted. It is likely the application is closing.", //$NON-NLS-1$
|
||||
} catch (final InterruptedIOException e) {
|
||||
LOGGER.info(
|
||||
"Prompt interrupted. It is likely the application is closing."); //$NON-NLS-1$
|
||||
LOGGER.log(Level.FINER, "Interruption of the prompt.", //$NON-NLS-1$
|
||||
e);
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException e) {
|
||||
// The manager was closed
|
||||
running = false;
|
||||
LOGGER.log(Level.WARNING,
|
||||
"The console manager was closed. Closing the application as no one can reach it.", //$NON-NLS-1$
|
||||
LOGGER.warning(
|
||||
"The console manager was closed. Closing the application as no one can reach it."); //$NON-NLS-1$
|
||||
LOGGER.log(Level.FINE,
|
||||
"An exception caused the closing of the application", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the running status */
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
/** @return the root */
|
||||
public SubedCommand getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/** @return the header */
|
||||
public String getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/** @return the footer */
|
||||
public String getFooter() {
|
||||
return footer;
|
||||
/** Start the application. */
|
||||
public void start() {
|
||||
try {
|
||||
running = true;
|
||||
if (header != null) {
|
||||
out.println(header);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
// The manager was closed
|
||||
running = false;
|
||||
LOGGER.warning(
|
||||
"The console manager was closed. Closing the application as no one can reach it."); //$NON-NLS-1$
|
||||
LOGGER.log(Level.FINE,
|
||||
"An exception caused the closing of the application", //$NON-NLS-1$
|
||||
e);
|
||||
return;
|
||||
}
|
||||
do {
|
||||
runLoop();
|
||||
} while (running);
|
||||
if (footer != null) {
|
||||
try {
|
||||
out.println(footer);
|
||||
} catch (final IOException e) {
|
||||
// The manager was closed
|
||||
running = false;
|
||||
LOGGER.warning("Console manager alreaady closed."); //$NON-NLS-1$
|
||||
LOGGER.log(Level.FINE,
|
||||
"Exception raised by goodbye message printing... Application will still close.", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
}
|
||||
running = false;
|
||||
LOGGER.fine("Exiting application."); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,27 +49,62 @@ import fr.bigeon.gclc.exception.CommandParsingException;
|
||||
* arguments.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class GCLCConstants {
|
||||
public final class GCLCConstants {
|
||||
|
||||
/** The escaping character */
|
||||
/** The escaping character. */
|
||||
private static final char ESCAPING_CHAR = getSystemEscapingChar();
|
||||
|
||||
/** Hide utility class constructor */
|
||||
/** Hide utility class constructor. */
|
||||
private GCLCConstants() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/** @return the escaping character */
|
||||
/** Get the end of a string argument.
|
||||
*
|
||||
* @param cmd the command to parse
|
||||
* @param startIndex the starting point of the parsing
|
||||
* @param index the index of the current position
|
||||
* @return the argument
|
||||
* @throws CommandParsingException if the end of string does not mark end of
|
||||
* command and is not followed by a space */
|
||||
private static String endOfString(final String cmd, final int startIndex,
|
||||
final int index) throws CommandParsingException {
|
||||
if (index < cmd.length() && cmd.charAt(index) != ' ') {
|
||||
throw new CommandParsingException("Misplaced quote"); //$NON-NLS-1$
|
||||
}
|
||||
return cmd.substring(startIndex + 1, index - 1);
|
||||
}
|
||||
|
||||
/** Get the excaping character.
|
||||
*
|
||||
* @return the escaping character */
|
||||
private static char getSystemEscapingChar() {
|
||||
return '\\';
|
||||
}
|
||||
|
||||
/** Splits a command in the diferrent arguments
|
||||
*
|
||||
/** Remove escaping characters from the string.
|
||||
*
|
||||
* @param arg the string to remove excaping character from
|
||||
* @return the string without escape character */
|
||||
private static String removeEscaped(final String arg) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
int index = 0;
|
||||
int endIndex = arg.indexOf(ESCAPING_CHAR);
|
||||
while (endIndex != -1) {
|
||||
builder.append(arg.subSequence(index, endIndex));
|
||||
index = endIndex + 1;
|
||||
endIndex = arg.indexOf(ESCAPING_CHAR, index + 1);
|
||||
}
|
||||
builder.append(arg.substring(index));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/** Splits a command in the diferrent arguments.
|
||||
*
|
||||
* @param cmd the command to split in its parts
|
||||
* @return the list of argument preceded by the command name
|
||||
* @throws CommandParsingException if the parsing of the command failed */
|
||||
public static List<String> splitCommand(String cmd) throws CommandParsingException {
|
||||
public static List<String> splitCommand(final String cmd) throws CommandParsingException {
|
||||
final List<String> args = new ArrayList<>();
|
||||
// parse the string to separate arguments
|
||||
int index = 0;
|
||||
@@ -77,7 +112,7 @@ public class GCLCConstants {
|
||||
boolean escaped = false;
|
||||
boolean inString = false;
|
||||
while (index < cmd.length()) {
|
||||
char c = cmd.charAt(index);
|
||||
final char c = cmd.charAt(index);
|
||||
index++;
|
||||
if (escaped || c == ESCAPING_CHAR) {
|
||||
escaped = !escaped;
|
||||
@@ -105,33 +140,4 @@ public class GCLCConstants {
|
||||
return args;
|
||||
}
|
||||
|
||||
/** @param arg the string to remove excaping character from
|
||||
* @return the string without escape character */
|
||||
private static String removeEscaped(String arg) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int index = 0;
|
||||
int endIndex = arg.indexOf(ESCAPING_CHAR);
|
||||
while (endIndex != -1) {
|
||||
builder.append(arg.subSequence(index, endIndex));
|
||||
index = endIndex + 1;
|
||||
endIndex = arg.indexOf(ESCAPING_CHAR, index + 1);
|
||||
}
|
||||
builder.append(arg.substring(index));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/** @param cmd the command to parse
|
||||
* @param startIndex the starting point of the parsing
|
||||
* @param index the index of the current position
|
||||
* @return the argument
|
||||
* @throws CommandParsingException if the end of string does not mark end of
|
||||
* command and is not followed by a space */
|
||||
private static String endOfString(String cmd, int startIndex,
|
||||
int index) throws CommandParsingException {
|
||||
if (index < cmd.length() && cmd.charAt(index) != ' ') {
|
||||
throw new CommandParsingException("Misplaced quote"); //$NON-NLS-1$
|
||||
}
|
||||
return cmd.substring(startIndex + 1, index - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,11 +40,12 @@ package fr.bigeon.gclc.command;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
* A command to execute. It is mandatory that it has a name and that name cannot
|
||||
* start with minus character or contain spaces.
|
||||
/** A command to execute.
|
||||
* <p>
|
||||
* It is mandatory that it has a name and that name cannot start with minus
|
||||
* character or contain spaces.
|
||||
* <p>
|
||||
* A command can be executed, with parameters that will be provided as an array
|
||||
* of strings.
|
||||
@@ -65,22 +66,26 @@ import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
* @author Emmanuel BIGEON */
|
||||
public abstract class Command implements ICommand {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/** The linux end of line character. */
|
||||
private static final String EOL_LINUX = "\n"; //$NON-NLS-1$
|
||||
/** The empty string constant */
|
||||
private static final String EMPTY = ""; //$NON-NLS-1$
|
||||
/** The name of the command */
|
||||
/** The name of the command. */
|
||||
protected final String name;
|
||||
|
||||
/** @param name the command name */
|
||||
public Command(String name) {
|
||||
/** Create the command.
|
||||
*
|
||||
* @param name the command name */
|
||||
public Command(final String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/** @return a brief description of the command */
|
||||
/** Get the brief part of the command help.
|
||||
* <p>
|
||||
* This method may be overriden by implementations to improve the help
|
||||
* content. The default behavior is to print the tip.
|
||||
*
|
||||
* @return a brief description of the command
|
||||
* @see Command#help(ConsoleOutput, String...) */
|
||||
protected String brief() {
|
||||
return " " + tip(); //$NON-NLS-1$
|
||||
}
|
||||
@@ -92,12 +97,24 @@ public abstract class Command implements ICommand {
|
||||
return name;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#help(fr.bigeon.gclc.ConsoleManager,
|
||||
* java.lang.String) */
|
||||
/** Prints a help content for this command to the console output.
|
||||
* <p>
|
||||
* This help is following the given format:
|
||||
*
|
||||
* <pre>
|
||||
* [Command name]
|
||||
* [brief message]
|
||||
*
|
||||
* Usage:
|
||||
* [Usage pattern]
|
||||
*
|
||||
* [Usage details]
|
||||
* </pre>
|
||||
*
|
||||
* @see fr.bigeon.gclc.command.ICommand#help(ConsoleOutput, String...) */
|
||||
@Override
|
||||
public final void help(ConsoleManager manager,
|
||||
String... args) throws IOException {
|
||||
public final void help(final ConsoleOutput manager,
|
||||
final String... args) throws IOException {
|
||||
manager.println(getCommandName());
|
||||
manager.println(brief());
|
||||
manager.println();
|
||||
@@ -114,19 +131,16 @@ public abstract class Command implements ICommand {
|
||||
}
|
||||
}
|
||||
|
||||
/** <p>
|
||||
* This method return the detail of the help. It immediatly follows the
|
||||
* {@link #usagePattern() usage pattern}.
|
||||
/** This method return the detail of the help.
|
||||
* <p>
|
||||
* It immediatly follows the {@link #usagePattern() usage pattern}.
|
||||
*
|
||||
* @return the detailed help (should end with end of line or be empty) */
|
||||
@SuppressWarnings("static-method")
|
||||
protected String usageDetail() {
|
||||
return EMPTY;
|
||||
}
|
||||
protected abstract String usageDetail();
|
||||
|
||||
/** <p>
|
||||
* This prints the usage pattern for the command. It follows the brief
|
||||
* introduction on the command ({@link #brief()})
|
||||
/** This prints the usage pattern for the command.
|
||||
* <p>
|
||||
* It follows the brief introduction on the command ({@link #brief()})
|
||||
*
|
||||
* @return the usage pattern */
|
||||
protected String usagePattern() {
|
||||
|
||||
@@ -45,33 +45,34 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** <p>
|
||||
* An object representing a collection of parameters. It is used for defaulting
|
||||
* values.
|
||||
import fr.bigeon.gclc.exception.CommandParsingException;
|
||||
|
||||
/** An object representing a collection of parameters.
|
||||
* <p>
|
||||
* It is used for defaulting values.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class CommandParameters {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final class CommandParameters {
|
||||
/** Number of element for a string argument. */
|
||||
private static final int STRINGARG_NUMBER_OF_ELEMENTS = 2;
|
||||
/** Boolean arguments */
|
||||
/** Boolean arguments. */
|
||||
private final Map<String, Boolean> booleanArguments = new HashMap<>();
|
||||
/** String arguments */
|
||||
/** String arguments. */
|
||||
private final Map<String, String> stringArguments = new HashMap<>();
|
||||
/** Arguments restriction on the named ones */
|
||||
/** Arguments restriction on the named ones. */
|
||||
private final boolean strict;
|
||||
/** additional (unnamed) parameters */
|
||||
/** additional (unnamed) parameters. */
|
||||
private final List<String> additional = new ArrayList<>();
|
||||
|
||||
/** @param bools the boolean parameters
|
||||
/** Create a command parameter object.
|
||||
*
|
||||
* @param bools the boolean parameters
|
||||
* @param strings the string parameters
|
||||
* @param strict if the argument are restricted to the declared ones */
|
||||
@SuppressWarnings("boxing")
|
||||
public CommandParameters(Set<String> bools, Set<String> strings,
|
||||
boolean strict) {
|
||||
public CommandParameters(final Set<String> bools, final Set<String> strings,
|
||||
final boolean strict) {
|
||||
for (final String string : bools) {
|
||||
booleanArguments.put(string, false);
|
||||
booleanArguments.put(string, Boolean.FALSE);
|
||||
}
|
||||
for (final String string : strings) {
|
||||
stringArguments.put(string, null);
|
||||
@@ -79,56 +80,70 @@ public class CommandParameters {
|
||||
this.strict = strict;
|
||||
}
|
||||
|
||||
/** @param key the key
|
||||
/** Get the value of a string argument.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the associated value, null if it was not specified */
|
||||
public String get(String key) {
|
||||
public String get(final String key) {
|
||||
return stringArguments.get(key);
|
||||
}
|
||||
|
||||
/** @return additional non parsed parameters */
|
||||
/** Get the additional (unrecognized) arguments.
|
||||
*
|
||||
* @return additional non parsed parameters */
|
||||
public List<String> getAdditionals() {
|
||||
return Collections.unmodifiableList(additional);
|
||||
}
|
||||
|
||||
/** @param key the key
|
||||
/** Get the value of a boolean argument.
|
||||
*
|
||||
* @param key the key
|
||||
* @return if the key was specified */
|
||||
public boolean getBool(String key) {
|
||||
public boolean getBool(final String key) {
|
||||
return booleanArguments.containsKey(key) &&
|
||||
booleanArguments.get(key).booleanValue();
|
||||
}
|
||||
|
||||
/** @param args the arguments to parse
|
||||
* @return if the arguments were parsed */
|
||||
public boolean parseArgs(String... args) {
|
||||
int i = 0;
|
||||
while (i < args.length) {
|
||||
String next = null;
|
||||
if (i < args.length - 1) {
|
||||
next = args[i + 1];
|
||||
}
|
||||
int p = parseArg(args[i], next);
|
||||
if (p == 0) {
|
||||
return false;
|
||||
}
|
||||
i += p;
|
||||
}
|
||||
return true;
|
||||
/** Get the boolean arguments.
|
||||
*
|
||||
* @return the boolean arguments */
|
||||
public Set<String> getBooleanArgumentKeys() {
|
||||
return booleanArguments.keySet();
|
||||
}
|
||||
|
||||
/** Get the string arguments.
|
||||
*
|
||||
* @return the boolean arguments */
|
||||
public Set<String> getStringArgumentKeys() {
|
||||
return stringArguments.keySet();
|
||||
}
|
||||
|
||||
/** Test if an argument exists in this object.
|
||||
*
|
||||
* @param key the key
|
||||
* @return if the key is present in string arguments or boolean ones. */
|
||||
public boolean hasArgument(final String key) {
|
||||
return stringArguments.containsKey(key) ||
|
||||
booleanArguments.containsKey(key);
|
||||
}
|
||||
|
||||
/** Attempt to parse an argument.
|
||||
* <p>
|
||||
* This method return 0 if the parsing was incorrect, or the number of
|
||||
* parsed elements.
|
||||
*
|
||||
*
|
||||
* @param arg the argument
|
||||
* @param next the next element
|
||||
* @return the number of element read */
|
||||
private int parseArg(String arg, String next) {
|
||||
private int parseArg(final String arg, final String next) {
|
||||
if (!arg.startsWith("-")) { //$NON-NLS-1$
|
||||
if (strict) {
|
||||
return 0;
|
||||
}
|
||||
additional.add(arg);
|
||||
return 1;
|
||||
}
|
||||
String name = arg.substring(1);
|
||||
final String name = arg.substring(1);
|
||||
if (booleanArguments.containsKey(name)) {
|
||||
booleanArguments.put(name, Boolean.TRUE);
|
||||
return 1;
|
||||
@@ -143,12 +158,33 @@ public class CommandParameters {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Add a string arg value
|
||||
*
|
||||
/** Parse arguments.
|
||||
*
|
||||
* @param args the arguments to parse
|
||||
* @throws CommandParsingException if the arguments parsing failed */
|
||||
public void parseArgs(final String... args) throws CommandParsingException {
|
||||
int i = 0;
|
||||
while (i < args.length) {
|
||||
String next = null;
|
||||
if (i < args.length - 1) {
|
||||
next = args[i + 1];
|
||||
}
|
||||
final int p = parseArg(args[i], next);
|
||||
if (p == 0) {
|
||||
throw new CommandParsingException(
|
||||
"Invalid parameter " + args[i]); //$NON-NLS-1$
|
||||
}
|
||||
i += p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Add a string arg value.
|
||||
*
|
||||
* @param name the string arg name
|
||||
* @param next the string arg value
|
||||
* @return 2 or 0 if next is invalid */
|
||||
private int parseStringArg(String name, String next) {
|
||||
private int parseStringArg(final String name, final String next) {
|
||||
if (next == null) {
|
||||
return 0;
|
||||
}
|
||||
@@ -156,30 +192,23 @@ public class CommandParameters {
|
||||
return STRINGARG_NUMBER_OF_ELEMENTS;
|
||||
}
|
||||
|
||||
/** @param string the key
|
||||
/** Set a boolean parameter value.
|
||||
*
|
||||
* @param string the key
|
||||
* @param value the value */
|
||||
@SuppressWarnings("boxing")
|
||||
public void set(String string, boolean value) {
|
||||
public void set(final String string, final boolean value) {
|
||||
if (booleanArguments.containsKey(string)) {
|
||||
booleanArguments.put(string, value);
|
||||
booleanArguments.put(string, Boolean.valueOf(value));
|
||||
}
|
||||
}
|
||||
|
||||
/** @param string the key
|
||||
/** Set a string parameter value.
|
||||
*
|
||||
* @param string the key
|
||||
* @param value the value */
|
||||
public void set(String string, String value) {
|
||||
public void set(final String string, final String value) {
|
||||
if (stringArguments.containsKey(string)) {
|
||||
stringArguments.put(string, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the boolean arguments */
|
||||
public Set<String> getBooleanArgumentKeys() {
|
||||
return booleanArguments.keySet();
|
||||
}
|
||||
|
||||
/** @return the boolean arguments */
|
||||
public Set<String> getStringArgumentKeys() {
|
||||
return stringArguments.keySet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,36 +42,48 @@ import java.util.List;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.i18n.Messages;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
* A command provider is a map of key word to command to execute
|
||||
/** A command provider is a map of key word to command to execute.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class CommandProvider implements ICommandProvider {
|
||||
/** The minus character */
|
||||
/** The minus character. */
|
||||
private static final String MINUS = "-"; //$NON-NLS-1$
|
||||
/** The space character */
|
||||
/** The space character. */
|
||||
private static final String SPACE = " "; //$NON-NLS-1$
|
||||
/** The commands map */
|
||||
/** The commands map. */
|
||||
protected final List<ICommand> commands;
|
||||
|
||||
/** Create a command provider */
|
||||
/** Create a command provider. */
|
||||
public CommandProvider() {
|
||||
super();
|
||||
commands = new ArrayList<>();
|
||||
}
|
||||
|
||||
/** Test the command name validity.
|
||||
*
|
||||
* @param name the command name
|
||||
* @throws InvalidCommandName if the name is invalid */
|
||||
private static void testCommandName(final String name) throws InvalidCommandName {
|
||||
if (name == null || name.isEmpty() || name.startsWith(MINUS) ||
|
||||
name.contains(SPACE)) {
|
||||
throw new InvalidCommandName();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommandProvider#add(java.lang.String,
|
||||
* fr.bigeon.gclc.command.Command) */
|
||||
@Override
|
||||
public boolean add(ICommand value) throws InvalidCommandName {
|
||||
public final boolean add(final ICommand value) throws InvalidCommandName {
|
||||
final String name = value.getCommandName();
|
||||
testCommandName(name);
|
||||
if (commands.contains(value)) {
|
||||
return true;
|
||||
}
|
||||
for (ICommand iCommand : commands) {
|
||||
for (final ICommand iCommand : commands) {
|
||||
if (iCommand.getCommandName().equals(value.getCommandName())) {
|
||||
throw new InvalidCommandName(
|
||||
"Name already used: " + value.getCommandName()); //$NON-NLS-1$
|
||||
@@ -80,21 +92,18 @@ public class CommandProvider implements ICommandProvider {
|
||||
return commands.add(value);
|
||||
}
|
||||
|
||||
/** @param name the command name
|
||||
* @throws InvalidCommandName if the name is invalid */
|
||||
private static void testCommandName(String name) throws InvalidCommandName {
|
||||
if (name == null || name.isEmpty() || name.startsWith(MINUS) ||
|
||||
name.contains(SPACE)) {
|
||||
throw new InvalidCommandName();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see
|
||||
* fr.bigeon.gclc.command.ICommandProvider#executeSub(fr.bigeon.gclc.manager
|
||||
* .ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String,
|
||||
* java.lang.String[]) */
|
||||
@Override
|
||||
public void executeSub(String cmd,
|
||||
String... args) throws CommandRunException {
|
||||
public final void executeSub(final ConsoleOutput out, final ConsoleInput in,
|
||||
final String cmd,
|
||||
final String... args) throws CommandRunException {
|
||||
for (final ICommand command : commands) {
|
||||
if (command.getCommandName().equals(cmd)) {
|
||||
command.execute(args);
|
||||
command.execute(out, in, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -105,7 +114,7 @@ public class CommandProvider implements ICommandProvider {
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommandProvider#get(java.lang.String) */
|
||||
@Override
|
||||
public ICommand get(String commandName) {
|
||||
public final ICommand get(final String commandName) {
|
||||
for (final ICommand command : commands) {
|
||||
if (command.getCommandName().equals(commandName)) {
|
||||
return command;
|
||||
|
||||
@@ -41,37 +41,42 @@ package fr.bigeon.gclc.command;
|
||||
import java.io.IOException;
|
||||
|
||||
import fr.bigeon.gclc.ConsoleApplication;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
import fr.bigeon.gclc.prompt.CLIPrompterMessages;
|
||||
|
||||
/** <p>
|
||||
* A command to exit a {@link ConsoleApplication}.
|
||||
/** A command to exit a {@link ConsoleApplication}.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class ExitCommand implements ICommand {
|
||||
/** The exit command manual message key */
|
||||
/** The exit command manual message key. */
|
||||
private static final String EXIT_MAN = "exit.man"; //$NON-NLS-1$
|
||||
/** The tip of the exit command */
|
||||
/** The tip of the exit command. */
|
||||
private static final String EXIT = "exit.tip"; //$NON-NLS-1$
|
||||
/** The application that will be exited when this command runs */
|
||||
/** The application that will be exited when this command runs. */
|
||||
private final ConsoleApplication app;
|
||||
/** The exit command name */
|
||||
/** The exit command name. */
|
||||
private final String name;
|
||||
|
||||
/** @param name the name of the command
|
||||
/** Create the exiting command.
|
||||
*
|
||||
* @param name the name of the command
|
||||
* @param app the application to exit */
|
||||
public ExitCommand(String name, ConsoleApplication app) {
|
||||
public ExitCommand(final String name, final ConsoleApplication app) {
|
||||
this.name = name;
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
/** The actions to take before exiting */
|
||||
public void beforeExit() {
|
||||
/** The actions to take before exiting.
|
||||
* <p>
|
||||
* This method is intended to be overriden by sub classes. */
|
||||
protected void beforeExit() {
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void execute(String... args) {
|
||||
public final void execute(final ConsoleOutput output,
|
||||
final ConsoleInput input, final String... args) {
|
||||
beforeExit();
|
||||
app.exit();
|
||||
}
|
||||
@@ -84,14 +89,13 @@ public class ExitCommand implements ICommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(ConsoleManager manager,
|
||||
String... args) throws IOException {
|
||||
public final void help(final ConsoleOutput manager,
|
||||
final String... args) throws IOException {
|
||||
manager.println(
|
||||
CLIPrompterMessages.getString(EXIT_MAN, (Object[]) args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
public final String tip() {
|
||||
return CLIPrompterMessages.getString(EXIT);
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,8 @@ import java.io.IOException;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.CommandRunExceptionType;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
import fr.bigeon.gclc.prompt.CLIPrompterMessages;
|
||||
|
||||
/** A command to print help of an other command.
|
||||
@@ -50,37 +51,19 @@ import fr.bigeon.gclc.prompt.CLIPrompterMessages;
|
||||
* This command will display the help of an other command
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class HelpExecutor extends Command {
|
||||
public final class HelpExecutor extends Command {
|
||||
|
||||
/** The command to execute the help of */
|
||||
/** The command to execute the help of. */
|
||||
private final ICommand cmd;
|
||||
/** The console manager */
|
||||
private final ConsoleManager consoleManager;
|
||||
|
||||
/** @param cmdName the command name
|
||||
* @param consoleManager the manager for the console
|
||||
/** Create the help command.
|
||||
*
|
||||
* @param cmdName the command name
|
||||
* @param cmd the command to execute the help of */
|
||||
public HelpExecutor(String cmdName, ConsoleManager consoleManager,
|
||||
ICommand cmd) {
|
||||
public HelpExecutor(final String cmdName,
|
||||
final ICommand cmd) {
|
||||
super(cmdName);
|
||||
this.cmd = cmd;
|
||||
if (consoleManager == null) {
|
||||
throw new NullPointerException(
|
||||
"Argument cannot be null: ConsoleManager"); //$NON-NLS-1$
|
||||
}
|
||||
this.consoleManager = consoleManager;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
try {
|
||||
cmd.help(consoleManager, args);
|
||||
} catch (IOException e) {
|
||||
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
|
||||
"Console manager closed", e, this); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -94,13 +77,17 @@ public class HelpExecutor extends Command {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usagePattern() */
|
||||
* @see fr.bigeon.gclc.command.ICommand#execute(ConsoleOutput, ConsoleInput,
|
||||
* String[]) */
|
||||
@Override
|
||||
protected String usagePattern() {
|
||||
if (cmd instanceof SubedCommand) {
|
||||
return getCommandName() + " <otherCommand>"; //$NON-NLS-1$
|
||||
public void execute(final ConsoleOutput out, final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
try {
|
||||
cmd.help(out, args);
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
|
||||
"Console manager closed", e, this); //$NON-NLS-1$
|
||||
}
|
||||
return getCommandName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -109,4 +96,22 @@ public class HelpExecutor extends Command {
|
||||
public String tip() {
|
||||
return CLIPrompterMessages.getString("help.cmd.tip"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail()
|
||||
*/
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usagePattern() */
|
||||
@Override
|
||||
protected String usagePattern() {
|
||||
if (cmd instanceof SubedCommand) {
|
||||
return getCommandName() + " <otherCommand>"; //$NON-NLS-1$
|
||||
}
|
||||
return getCommandName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,43 +41,40 @@ package fr.bigeon.gclc.command;
|
||||
import java.io.IOException;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** The contract of commands
|
||||
/** The contract of commands.
|
||||
* <p>
|
||||
* This interface describe the contract of commands
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public interface ICommand {
|
||||
|
||||
/** @param args the arguments of the command (some expect an empty array)
|
||||
* @throws CommandRunException if the execution of the command failed for
|
||||
* any reason */
|
||||
void execute(String... args) throws CommandRunException;
|
||||
/** Execute the command on the given output and input.
|
||||
*
|
||||
* @param out the normal output
|
||||
* @param in the input
|
||||
* @param args the arguments
|
||||
* @throws CommandRunException if the command failed */
|
||||
void execute(ConsoleOutput out, ConsoleInput in,
|
||||
String... args) throws CommandRunException;
|
||||
|
||||
/** @return the command's name */
|
||||
/** Get teh command name.
|
||||
*
|
||||
* @return the command's name */
|
||||
String getCommandName();
|
||||
|
||||
/** This prints the help associated to this command.
|
||||
* <p>
|
||||
* The default behavior is to print:
|
||||
*
|
||||
* <pre>
|
||||
* [Command name]
|
||||
* [brief message]
|
||||
*
|
||||
* Usage:
|
||||
* [Usage pattern]
|
||||
*
|
||||
* [Usage details]
|
||||
* </pre>
|
||||
*
|
||||
* @param manager the manager to print the data
|
||||
* @param output the output to print the data
|
||||
* @param args the arguments called with the help
|
||||
* @throws IOException if the manager was closed */
|
||||
void help(ConsoleManager manager, String... args) throws IOException;
|
||||
void help(ConsoleOutput output, String... args) throws IOException;
|
||||
|
||||
/** @return a tip on the command */
|
||||
/** Get a tip (brief helping message) for the command.
|
||||
*
|
||||
* @return a tip on the command */
|
||||
String tip();
|
||||
|
||||
}
|
||||
@@ -38,45 +38,47 @@ package fr.bigeon.gclc.command;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
* An ICommadProvider is a provider of commands that can register commands under
|
||||
* some keywords.
|
||||
/** An ICommadProvider is a provider of commands that can register commands
|
||||
* under some keywords.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public interface ICommandProvider {
|
||||
|
||||
/** <p>
|
||||
* Adds a command to this provider, if no command was associated with the
|
||||
* given key
|
||||
/** Adds a command to this provider, if no command was associated with the
|
||||
* given key.
|
||||
*
|
||||
* @param value the command to execute
|
||||
* @return if the command was added
|
||||
* @throws InvalidCommandName if the command name is invalid */
|
||||
public boolean add(ICommand value) throws InvalidCommandName;
|
||||
boolean add(ICommand value) throws InvalidCommandName;
|
||||
|
||||
/** <p>
|
||||
* This method executes the command with the given name found. If no command
|
||||
* with this name is found, an error command is usually executed. If there
|
||||
* are several commands with the same name, the behavior is unspecified.
|
||||
* Depending on the implementation, it may run an error command or prompt
|
||||
* the user for a choice.
|
||||
/** Execute the command with the given name.
|
||||
* <p>
|
||||
* If no command with this name is found, an error command is usually
|
||||
* executed. If there are several commands with the same name, the behavior
|
||||
* is unspecified. Depending on the implementation, it may run an error
|
||||
* command or prompt the user for a choice.
|
||||
*
|
||||
* @param out the output
|
||||
* @param in the input
|
||||
* @param command the name of the command the user wishes to execute
|
||||
* @param args the arguments for the command
|
||||
* @throws CommandRunException if the command failed to run */
|
||||
public void executeSub(String command,
|
||||
String... args) throws CommandRunException;
|
||||
void executeSub(ConsoleOutput out, ConsoleInput in, String command,
|
||||
String... args) throws CommandRunException;
|
||||
|
||||
/** <p>
|
||||
* This method provide the command with the given name found. If no command
|
||||
* with this name is found, an error command is usually returned. If there
|
||||
* are several commands with the same name, the behavior is unspecified.
|
||||
* Depending on the implementation, it may return an error command or the
|
||||
* first command with this name found.
|
||||
/** Get the command with the given name.
|
||||
* <p>
|
||||
* If no command with this name is found, an error command is usually
|
||||
* returned. If there are several commands with the same name, the behavior
|
||||
* is unspecified. Depending on the implementation, it may return an error
|
||||
* command or the first command with this name found.
|
||||
*
|
||||
* @param command the name of the command the user wishes to execute
|
||||
* @return the command to execute */
|
||||
public ICommand get(String command);
|
||||
ICommand get(String command);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,8 @@
|
||||
*/
|
||||
package fr.bigeon.gclc.command;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** This implement a command that does nothing.
|
||||
* <p>
|
||||
@@ -50,28 +48,25 @@ import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class MockCommand implements ICommand {
|
||||
|
||||
/** The command name */
|
||||
/** The command name. */
|
||||
private final String name;
|
||||
|
||||
/** @param name the command name */
|
||||
public MockCommand(String name) {
|
||||
/** Create the command.
|
||||
*
|
||||
* @param name the command name */
|
||||
public MockCommand(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#execute(java.lang.String[]) */
|
||||
* @see fr.bigeon.gclc.command.ICommand#execute(ConsoleOutput, ConsoleInput,
|
||||
* String[]) */
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
public void execute(final ConsoleOutput out, final ConsoleInput in,
|
||||
final String... args) {
|
||||
//
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@Override
|
||||
public String tip() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#getCommandName() */
|
||||
@Override
|
||||
@@ -83,9 +78,16 @@ public final class MockCommand implements ICommand {
|
||||
* @see fr.bigeon.gclc.command.ICommand#help(fr.bigeon.gclc.manager.
|
||||
* ConsoleManager, java.lang.String[]) */
|
||||
@Override
|
||||
public void help(ConsoleManager manager,
|
||||
String... args) throws IOException {
|
||||
public void help(final ConsoleOutput manager,
|
||||
final String... args) {
|
||||
//
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@Override
|
||||
public String tip() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,203 +39,122 @@
|
||||
package fr.bigeon.gclc.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.CommandRunExceptionType;
|
||||
import fr.bigeon.gclc.exception.InvalidParameterException;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
* A command relying on the {@link CommandParameters} to store parameters values
|
||||
/** A command relying on the {@link CommandParameters} to store parameters
|
||||
* values.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public abstract class ParametrizedCommand extends Command {
|
||||
|
||||
/** If the command may use interactive prompting for required parameters
|
||||
* that
|
||||
* were not provided on execution */
|
||||
private boolean interactive = true;
|
||||
/** The manager */
|
||||
protected final ConsoleManager manager;
|
||||
/** The boolean parameters mandatory status */
|
||||
private final Set<String> boolParams = new HashSet<>();
|
||||
/** The string parameters mandatory status */
|
||||
private final Map<String, Boolean> stringParams = new HashMap<>();
|
||||
/** The parameters mandatory status */
|
||||
private final Map<String, Boolean> params = new HashMap<>();
|
||||
/** The restriction of provided parameters on execution to declared
|
||||
* paramters
|
||||
* in the status maps. */
|
||||
private final boolean strict;
|
||||
private final ParametrizedCommandData data;
|
||||
|
||||
/** @param manager the manager
|
||||
/** Create a parametrized command.
|
||||
* <p>
|
||||
* Implementation are supposed to call the
|
||||
* {@link #addBooleanParameter(String)} and
|
||||
* {@link #addStringParameter(String, boolean)} method to set the
|
||||
* parameters.
|
||||
*
|
||||
* @param name the name */
|
||||
public ParametrizedCommand(ConsoleManager manager, String name) {
|
||||
this(manager, name, true);
|
||||
public ParametrizedCommand(final String name) {
|
||||
this(name, true);
|
||||
}
|
||||
|
||||
/** @param name the name */
|
||||
public ParametrizedCommand(String name) {
|
||||
this(null, name, true);
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Create a parametrized command.
|
||||
* <p>
|
||||
* Implementation are supposed to call the
|
||||
* {@link #addBooleanParameter(String)} and
|
||||
* {@link #addStringParameter(String, boolean)} method to set the
|
||||
* parameters.
|
||||
*
|
||||
* @param name the name
|
||||
* @param strict if the arguments are restricted to the declared ones */
|
||||
public ParametrizedCommand(ConsoleManager manager, String name,
|
||||
boolean strict) {
|
||||
public ParametrizedCommand(final String name, final boolean strict) {
|
||||
super(name);
|
||||
this.manager = manager;
|
||||
interactive = manager != null;
|
||||
this.strict = strict;
|
||||
data = new ParametrizedCommandData(strict);
|
||||
}
|
||||
|
||||
/** @param name the name
|
||||
* @param strict if the arguments are restricted to the declared ones */
|
||||
public ParametrizedCommand(String name, boolean strict) {
|
||||
this(null, name, strict);
|
||||
}
|
||||
|
||||
/** <p>
|
||||
* Add a parameter to the defined parameters
|
||||
/** Add a boolean parameter to defined parmaters.
|
||||
*
|
||||
* @param param the parameter identification
|
||||
* @param stringParameter if the parameter is a parameter with an argument
|
||||
* @param needed if the parameter is required
|
||||
* @throws InvalidParameterException if the parameter was invalid */
|
||||
protected void addParameter(String param, boolean stringParameter,
|
||||
boolean needed) throws InvalidParameterException {
|
||||
if (params.containsKey(param)) {
|
||||
checkParam(param, stringParameter, needed);
|
||||
return;
|
||||
}
|
||||
if (stringParameter) {
|
||||
stringParams.put(param, Boolean.valueOf(needed));
|
||||
params.put(param, Boolean.valueOf(needed));
|
||||
} else {
|
||||
if (needed) {
|
||||
// ERROR the boolean parameters cannot be needed
|
||||
throw new InvalidParameterException(
|
||||
"Boolean parameter are present by their very nature. They should not be defined as needed"); //$NON-NLS-1$
|
||||
}
|
||||
boolParams.add(param);
|
||||
params.put(param, Boolean.valueOf(false));
|
||||
}
|
||||
* @param flag the boolean flag
|
||||
* @throws InvalidParameterException if the parameter is already defined as
|
||||
* a string parameter */
|
||||
protected final void addBooleanParameter(final String flag) throws InvalidParameterException {
|
||||
data.addBooleanParameter(flag);
|
||||
}
|
||||
|
||||
/** @param param the parameter
|
||||
* @param stringParameter the string parameter type
|
||||
* @param needed if the parameter is needed
|
||||
* @throws InvalidParameterException if the new definition is invalid */
|
||||
private void checkParam(String param, boolean stringParameter,
|
||||
boolean needed) throws InvalidParameterException {
|
||||
if (stringParameter) {
|
||||
if (stringParams.containsKey(param)) {
|
||||
Boolean need = Boolean.valueOf(
|
||||
needed || stringParams.get(param).booleanValue());
|
||||
stringParams.put(param, need);
|
||||
params.put(param, need);
|
||||
return;
|
||||
}
|
||||
throw new InvalidParameterException(
|
||||
"Parameter is already defined as boolean"); //$NON-NLS-1$
|
||||
}
|
||||
if (stringParams.containsKey(param) || needed) {
|
||||
throw new InvalidParameterException(
|
||||
"Parameter is already defined as string"); //$NON-NLS-1$
|
||||
}
|
||||
/** Add a string parameter to defined parmaters.
|
||||
*
|
||||
* @param flag the parameter flag
|
||||
* @param needed if the parameter's absence should cause an exception
|
||||
* @throws InvalidParameterException if the parameter is already defined as
|
||||
* a boolean parameter */
|
||||
protected final void addStringParameter(final String flag,
|
||||
final boolean needed) throws InvalidParameterException {
|
||||
data.addStringParameter(flag, needed);
|
||||
}
|
||||
|
||||
/** @param parameters the command parameters
|
||||
/** Actually performs the execution after parsing the parameters.
|
||||
*
|
||||
* @param out the output
|
||||
* @param in the input
|
||||
* @param parameters the command parameters
|
||||
* @throws CommandRunException if the command failed */
|
||||
protected abstract void doExecute(CommandParameters parameters) throws CommandRunException;
|
||||
protected abstract void doExecute(ConsoleOutput out, ConsoleInput in,
|
||||
CommandParameters parameters) throws CommandRunException;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */
|
||||
@SuppressWarnings("boxing")
|
||||
@Override
|
||||
public final void execute(String... args) throws CommandRunException {
|
||||
final CommandParameters parameters = new CommandParameters(
|
||||
boolParams, stringParams.keySet(), strict);
|
||||
if (!parameters.parseArgs(args)) {
|
||||
// the parameters could not be correctly parsed
|
||||
throw new CommandRunException(CommandRunExceptionType.USAGE,
|
||||
"Unable to read arguments", this); //$NON-NLS-1$
|
||||
}
|
||||
final List<String> toProvide = new ArrayList<>();
|
||||
for (final Entry<String, Boolean> string : params.entrySet()) {
|
||||
if (string.getValue() && parameters.get(string.getKey()) == null) {
|
||||
if (!interactive) {
|
||||
throw new CommandRunException(
|
||||
CommandRunExceptionType.INTERACTION,
|
||||
"Missing required parameter " + string.getKey(), //$NON-NLS-1$
|
||||
this);
|
||||
}
|
||||
toProvide.add(string.getKey());
|
||||
}
|
||||
}
|
||||
// for each needed parameters that is missing, prompt the user.
|
||||
fillParameters(toProvide, parameters);
|
||||
doExecute(parameters);
|
||||
}
|
||||
|
||||
/** @param parameters the parameter list to complete
|
||||
* @param toProvide the parameters to ask for
|
||||
* @throws CommandRunException if the manager was closed */
|
||||
private void fillParameters(List<String> toProvide,
|
||||
CommandParameters parameters) throws CommandRunException {
|
||||
for (final String string : toProvide) {
|
||||
String value;
|
||||
try {
|
||||
value = manager.prompt(string);
|
||||
while (value.isEmpty()) {
|
||||
value = manager.prompt(string);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new CommandRunException(
|
||||
CommandRunExceptionType.INTERACTION,
|
||||
"Interactive command but manager closed...", e, this); //$NON-NLS-1$
|
||||
}
|
||||
parameters.set(string, value);
|
||||
public final void execute(final ConsoleOutput output,
|
||||
final ConsoleInput input,
|
||||
final String... args) throws CommandRunException {
|
||||
try {
|
||||
doExecute(output, input, data.getParameters(input, args));
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException("Unable to get parameters", e, this);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the set of boolean parameters */
|
||||
public Set<String> getBooleanParameters() {
|
||||
return Collections.unmodifiableSet(boolParams);
|
||||
/** Retrieve the boolean parameters (aka flags).
|
||||
*
|
||||
* @return the set of boolean parameters */
|
||||
public final Set<String> getBooleanParameters() {
|
||||
return data.getBooleanParameters();
|
||||
}
|
||||
|
||||
/** @return the stringParams */
|
||||
public Set<String> getStringParameters() {
|
||||
return stringParams.keySet();
|
||||
/** Retrieve the parameter names.
|
||||
*
|
||||
* @return the stringParams */
|
||||
public final Set<String> getParameters() {
|
||||
return data.getParameters();
|
||||
}
|
||||
|
||||
/** @return the stringParams */
|
||||
public Set<String> getParameters() {
|
||||
return params.keySet();
|
||||
/** Get the string parameters names.
|
||||
*
|
||||
* @return the stringParams */
|
||||
public final Set<String> getStringParameters() {
|
||||
return data.getStringParameters();
|
||||
}
|
||||
|
||||
/** @param param the parameter name
|
||||
/** Test if a parameter is needed.
|
||||
*
|
||||
* @param param the parameter name
|
||||
* @return if the parameter is needed */
|
||||
public boolean isNeeded(String param) {
|
||||
return params.containsKey(param) && params.get(param).booleanValue();
|
||||
public final boolean isNeeded(final String param) {
|
||||
return data.isNeeded(param);
|
||||
}
|
||||
|
||||
/** @return the strict */
|
||||
public boolean isStrict() {
|
||||
return strict;
|
||||
}
|
||||
|
||||
/** @return the interactive */
|
||||
public boolean isInteractive() {
|
||||
return interactive;
|
||||
/** If the command refuse unrecognized parameters.
|
||||
*
|
||||
* @return the strict */
|
||||
public final boolean isStrict() {
|
||||
return data.isStrict();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.command.ParametrizedCommand.java
|
||||
* Created on: Dec 24, 2014
|
||||
*/
|
||||
package fr.bigeon.gclc.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandParsingException;
|
||||
import fr.bigeon.gclc.exception.InvalidParameterException;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.EmptyInput;
|
||||
|
||||
/** An object to handle standardized command parameters.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public final class ParametrizedCommandData {
|
||||
|
||||
/** The boolean parameters mandatory status. */
|
||||
private final Set<String> boolParams = new HashSet<>();
|
||||
/** The string parameters mandatory status. */
|
||||
private final Map<String, Boolean> stringParams = new HashMap<>();
|
||||
/** The parameters mandatory status. */
|
||||
private final Map<String, Boolean> params = new HashMap<>();
|
||||
/** The restriction of provided parameters on execution to declared paramters in
|
||||
* the status maps. */
|
||||
private final boolean strict;
|
||||
|
||||
public ParametrizedCommandData() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public ParametrizedCommandData(final boolean strict) {
|
||||
this.strict = strict;
|
||||
}
|
||||
|
||||
/** Add a boolean parameter to defined parmaters.
|
||||
*
|
||||
* @param flag the boolean flag
|
||||
* @throws InvalidParameterException if the parameter is already defined as a
|
||||
* string parameter */
|
||||
public final void addBooleanParameter(final String flag)
|
||||
throws InvalidParameterException {
|
||||
if (params.containsKey(flag) && stringParams.containsKey(flag)) {
|
||||
throw new InvalidParameterException("Parameter is already defined as string"); //$NON-NLS-1$
|
||||
}
|
||||
boolParams.add(flag);
|
||||
params.put(flag, Boolean.FALSE);
|
||||
}
|
||||
|
||||
/** Add a string parameter to defined parmaters.
|
||||
*
|
||||
* @param flag the parameter flag
|
||||
* @param needed if the parameter's absence should cause an exception
|
||||
* @throws InvalidParameterException if the parameter is already defined as a
|
||||
* boolean parameter */
|
||||
public final void addStringParameter(final String flag, final boolean needed)
|
||||
throws InvalidParameterException {
|
||||
if (params.containsKey(flag)) {
|
||||
checkParam(flag, needed);
|
||||
return;
|
||||
}
|
||||
stringParams.put(flag, Boolean.valueOf(needed));
|
||||
params.put(flag, Boolean.valueOf(needed));
|
||||
}
|
||||
|
||||
/** Check a parameter.
|
||||
*
|
||||
* @param param the string parameter
|
||||
* @param needed if the parameter is needed
|
||||
* @throws InvalidParameterException if the new definition is invalid */
|
||||
private void checkParam(final String param, final boolean needed)
|
||||
throws InvalidParameterException {
|
||||
if (stringParams.containsKey(param)) {
|
||||
final Boolean need = Boolean
|
||||
.valueOf(needed || stringParams.get(param).booleanValue());
|
||||
stringParams.put(param, need);
|
||||
params.put(param, need);
|
||||
return;
|
||||
}
|
||||
throw new InvalidParameterException("Parameter is already defined as boolean"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public final CommandParameters getParameters(final ConsoleInput input,
|
||||
final String... args)
|
||||
throws IOException {
|
||||
final CommandParameters parameters = new CommandParameters(boolParams,
|
||||
stringParams.keySet(), strict);
|
||||
try {
|
||||
parameters.parseArgs(args);
|
||||
} catch (final CommandParsingException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
final List<String> toProvide = new ArrayList<>();
|
||||
for (final Entry<String, Boolean> string : params.entrySet()) {
|
||||
if (string.getValue().booleanValue()
|
||||
&& parameters.get(string.getKey()) == null) {
|
||||
if (input == null || input == EmptyInput.INSTANCE) {
|
||||
throw new IOException();
|
||||
}
|
||||
toProvide.add(string.getKey());
|
||||
}
|
||||
}
|
||||
// for each needed parameters that is missing, prompt the user.
|
||||
fillParameters(input, toProvide, parameters);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/** Fill the undefined parameters.
|
||||
* <p>
|
||||
* This method prompts the user to fill the needed parameters.
|
||||
*
|
||||
* @param input the input to prompt through
|
||||
* @param parameters the parameter list to complete
|
||||
* @param toProvide the parameters to ask for
|
||||
* @throws IOException if the manager was closed */
|
||||
private final static void fillParameters(final ConsoleInput input,
|
||||
final List<String> toProvide, final CommandParameters parameters)
|
||||
throws IOException {
|
||||
for (final String string : toProvide) {
|
||||
String value;
|
||||
value = input.prompt(MessageFormat.format("value of {0}? ", string)); //$NON-NLS-1$
|
||||
while (value.isEmpty()) {
|
||||
value = input
|
||||
.prompt(MessageFormat.format("value of {0}? (cannot be empty) ", //$NON-NLS-1$
|
||||
string));
|
||||
}
|
||||
parameters.set(string, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** Retrieve the boolean parameters (aka flags).
|
||||
*
|
||||
* @return the set of boolean parameters */
|
||||
public final Set<String> getBooleanParameters() {
|
||||
return Collections.unmodifiableSet(boolParams);
|
||||
}
|
||||
|
||||
/** Retrieve the parameter names.
|
||||
*
|
||||
* @return the stringParams */
|
||||
public final Set<String> getParameters() {
|
||||
return params.keySet();
|
||||
}
|
||||
|
||||
/** Get the string parameters names.
|
||||
*
|
||||
* @return the stringParams */
|
||||
public final Set<String> getStringParameters() {
|
||||
return stringParams.keySet();
|
||||
}
|
||||
|
||||
/** Test if a parameter is needed.
|
||||
*
|
||||
* @param param the parameter name
|
||||
* @return if the parameter is needed */
|
||||
public final boolean isNeeded(final String param) {
|
||||
return params.containsKey(param) && params.get(param).booleanValue();
|
||||
}
|
||||
|
||||
/** If the command refuse unrecognized parameters.
|
||||
*
|
||||
* @return the strict */
|
||||
public final boolean isStrict() {
|
||||
return strict;
|
||||
}
|
||||
}
|
||||
@@ -52,45 +52,64 @@ import fr.bigeon.gclc.GCLCConstants;
|
||||
import fr.bigeon.gclc.exception.CommandParsingException;
|
||||
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 command that will laucnh a series of command from a file
|
||||
/** A command that will launch a series of command from a file.
|
||||
* <p>
|
||||
* This command will read a file and execute each non empty non commented line
|
||||
* as a command of the application.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class ScriptExecution extends Command {
|
||||
public final class ScriptExecution extends Command {
|
||||
|
||||
/** The tab character */
|
||||
/** The tab character. */
|
||||
private static final String TAB = "\t"; //$NON-NLS-1$
|
||||
/** the space character */
|
||||
/** the space character. */
|
||||
private static final String SPACE = " "; //$NON-NLS-1$
|
||||
/** The application */
|
||||
/** The application. */
|
||||
private final ConsoleApplication application;
|
||||
/** The commenting prefix */
|
||||
/** The commenting prefix. */
|
||||
private final String commentPrefix;
|
||||
/** The charset for files */
|
||||
/** The charset for files. */
|
||||
private final Charset charset;
|
||||
|
||||
/** @param name the name of the command
|
||||
/** Create the script command.
|
||||
*
|
||||
* @param name the name of the command
|
||||
* @param application the application
|
||||
* @param commentPrefix the comment prefix in the script files
|
||||
* @param charset the charset to use for files */
|
||||
public ScriptExecution(String name, ConsoleApplication application,
|
||||
String commentPrefix, Charset charset) {
|
||||
public ScriptExecution(final String name, final ConsoleApplication application,
|
||||
final String commentPrefix, final Charset charset) {
|
||||
super(name);
|
||||
this.application = application;
|
||||
this.commentPrefix = commentPrefix;
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
/** Check the arguments.
|
||||
*
|
||||
* @param args the arguments
|
||||
* @throws CommandRunException if the arguments were not the ones
|
||||
* expected */
|
||||
private void checkArgs(final String[] args) throws CommandRunException {
|
||||
if (args.length == 0) {
|
||||
throw new CommandRunException(CommandRunExceptionType.USAGE,
|
||||
"Expecting a file", this); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#execute(java.lang.String[]) */
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
public void execute(final ConsoleOutput out, final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
checkArgs(args);
|
||||
String scriptFile = args[0];
|
||||
String[] params = Arrays.copyOfRange(args, 1, args.length);
|
||||
final String scriptFile = args[0];
|
||||
final String[] params = Arrays.copyOfRange(args, 1, args.length);
|
||||
String cmd;
|
||||
int lineNo = -1;
|
||||
try (InputStreamReader fReader = new InputStreamReader(
|
||||
@@ -98,44 +117,35 @@ public class ScriptExecution extends Command {
|
||||
BufferedReader reader = new BufferedReader(fReader)) {
|
||||
while ((cmd = reader.readLine()) != null) {
|
||||
lineNo++;
|
||||
String cmdLine = readCommandLine(cmd, params);
|
||||
final String cmdLine = readCommandLine(cmd, params);
|
||||
if (cmdLine == null) {
|
||||
continue;
|
||||
}
|
||||
List<String> ps = GCLCConstants.splitCommand(cmdLine);
|
||||
String command = ps.remove(0);
|
||||
application.executeSub(command, ps.toArray(new String[0]));
|
||||
final List<String> ps = GCLCConstants.splitCommand(cmdLine);
|
||||
final String command = ps.remove(0);
|
||||
application.executeSub(out, in, command,
|
||||
ps.toArray(new String[0]));
|
||||
}
|
||||
} catch (CommandParsingException e) {
|
||||
throw new CommandRunException("Invalid command in script (" + //$NON-NLS-1$
|
||||
e.getLocalizedMessage() + ")", //$NON-NLS-1$
|
||||
} catch (final CommandParsingException e) {
|
||||
throw new CommandRunException(MessageFormat.format(
|
||||
"Invalid command in script ({0})", e.getLocalizedMessage()), //$NON-NLS-1$
|
||||
e, this);
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException("Unable to read script", //$NON-NLS-1$
|
||||
e, this);
|
||||
} catch (CommandRunException e) {
|
||||
} catch (final CommandRunException e) {
|
||||
throw manageRunException(e, lineNo);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param args the arguments
|
||||
* @throws CommandRunException if the arguments were not the ones
|
||||
* expected */
|
||||
private void checkArgs(String[] args) throws CommandRunException {
|
||||
if (args.length == 0) {
|
||||
throw new CommandRunException(CommandRunExceptionType.USAGE,
|
||||
"Expecting a file", this); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
/** This method will create the correct exception. The exception source must
|
||||
* be this command.
|
||||
*
|
||||
*
|
||||
* @param e the exception
|
||||
* @param lineNo the line nu;ber
|
||||
* @return the exception to actually throw */
|
||||
private CommandRunException manageRunException(CommandRunException e,
|
||||
int lineNo) {
|
||||
private CommandRunException manageRunException(final CommandRunException e,
|
||||
final int lineNo) {
|
||||
if (e.getSource() == this) {
|
||||
// ensure closing?
|
||||
return e;
|
||||
@@ -147,28 +157,38 @@ public class ScriptExecution extends Command {
|
||||
e, this);
|
||||
}
|
||||
|
||||
/** @param cmd the line
|
||||
/** Read a line of the script.
|
||||
*
|
||||
* @param cmd the line
|
||||
* @param params the formatting parameters
|
||||
* @return the command if it is indeed one, null otherwise
|
||||
* @throws CommandRunException if the line stqrted with a space character */
|
||||
private String readCommandLine(String cmd,
|
||||
Object[] params) throws CommandRunException {
|
||||
private String readCommandLine(final String cmd,
|
||||
final Object[] params) throws CommandRunException {
|
||||
if (cmd.startsWith(SPACE) || cmd.startsWith(TAB)) {
|
||||
throw new CommandRunException(
|
||||
"Invalid command in script (line starts with space character)", //$NON-NLS-1$
|
||||
"Invalid line in script (line starts with space character)", //$NON-NLS-1$
|
||||
this);
|
||||
}
|
||||
if (cmd.isEmpty() || cmd.startsWith(commentPrefix)) {
|
||||
// Comment line
|
||||
return null;
|
||||
}
|
||||
return MessageFormat.format(cmd, params);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
@Override
|
||||
public String tip() {
|
||||
return "Execute a script"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#usageDetail() */
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(
|
||||
" scriptfile: path to the file containing the script to execute."); //$NON-NLS-1$
|
||||
builder.append(System.lineSeparator());
|
||||
@@ -179,18 +199,19 @@ public class ScriptExecution extends Command {
|
||||
builder.append(
|
||||
"start with whitespace characters. The lines starting with"); //$NON-NLS-1$
|
||||
builder.append(System.lineSeparator());
|
||||
builder.append("\"" + commentPrefix + //$NON-NLS-1$
|
||||
"\" will be ignored as well as empty lines."); //$NON-NLS-1$
|
||||
builder.append('"');
|
||||
builder.append(commentPrefix);
|
||||
builder.append("\" will be ignored as well as empty lines."); //$NON-NLS-1$
|
||||
builder.append(System.lineSeparator());
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#tip() */
|
||||
* @see fr.bigeon.gclc.command.Command#usagePattern() */
|
||||
@Override
|
||||
public String tip() {
|
||||
return "Execute a script"; //$NON-NLS-1$
|
||||
protected String usagePattern() {
|
||||
return super.usagePattern() + " <scriptfile>"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,87 +41,92 @@ import java.util.Arrays;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.CommandRunExceptionType;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
/**
|
||||
* <p>
|
||||
* A subed command is a command that can execute sub commands depending on the
|
||||
* first argument.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class SubedCommand implements ICommandProvider, ICommand {
|
||||
public final class SubedCommand extends CommandProvider implements ICommand {
|
||||
|
||||
/** The tab character */
|
||||
/** The tab character. */
|
||||
private static final String TAB = "\t"; //$NON-NLS-1$
|
||||
/** <p>
|
||||
* The command to execute when this command is called with no sub arguments.
|
||||
/** The command to execute when this command is called with no sub
|
||||
* arguments.
|
||||
* <p>
|
||||
* This may be null, in which case the command should have arguments. */
|
||||
private final ICommand noArgCommand;
|
||||
/** A tip on this command. */
|
||||
private final String tip;
|
||||
/** The provider */
|
||||
private final CommandProvider provider;
|
||||
/** The name of the command */
|
||||
/** The name of the command. */
|
||||
private final String name;
|
||||
|
||||
/** @param name the name of the command */
|
||||
public SubedCommand(String name) {
|
||||
/** Create the command that defines sub commands.
|
||||
*
|
||||
* @param name the name of the command */
|
||||
public SubedCommand(final String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
provider = new CommandProvider();
|
||||
noArgCommand = null;
|
||||
tip = null;
|
||||
}
|
||||
|
||||
/** @param name the name of the command
|
||||
/** Create the command that defines sub commands.
|
||||
*
|
||||
* @param name the name of the command
|
||||
* @param noArgCommand the command to execute when no extra parameter are
|
||||
* provided */
|
||||
public SubedCommand(String name, ICommand noArgCommand) {
|
||||
public SubedCommand(final String name, final ICommand noArgCommand) {
|
||||
super();
|
||||
this.name = name;
|
||||
provider = new CommandProvider();
|
||||
this.noArgCommand = noArgCommand;
|
||||
tip = null;
|
||||
}
|
||||
|
||||
/** @param name the name of the command
|
||||
/** Create the command that defines sub commands.
|
||||
*
|
||||
* @param name the name of the command
|
||||
* @param noArgCommand the command to execute
|
||||
* @param tip the help tip associated */
|
||||
public SubedCommand(String name, ICommand noArgCommand,
|
||||
String tip) {
|
||||
public SubedCommand(final String name, final ICommand noArgCommand,
|
||||
final String tip) {
|
||||
super();
|
||||
this.name = name;
|
||||
provider = new CommandProvider();
|
||||
this.noArgCommand = noArgCommand;
|
||||
this.tip = tip;
|
||||
}
|
||||
|
||||
/** @param name the name of the command
|
||||
/** Create the command that defines sub commands.
|
||||
*
|
||||
* @param name the name of the command
|
||||
* @param tip the help tip associated */
|
||||
public SubedCommand(String name, String tip) {
|
||||
public SubedCommand(final String name, final String tip) {
|
||||
super();
|
||||
this.name = name;
|
||||
provider = new CommandProvider();
|
||||
noArgCommand = null;
|
||||
this.tip = tip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(ICommand value) throws InvalidCommandName {
|
||||
return provider.add(value);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.acide.Command#execute(java.lang.String[]) */
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
public void execute(final ConsoleOutput output, final ConsoleInput input,
|
||||
final String... args) throws CommandRunException {
|
||||
if (args.length == 0 || args[0].startsWith("-")) { //$NON-NLS-1$
|
||||
if (noArgCommand != null) {
|
||||
noArgCommand.execute(args);
|
||||
noArgCommand.execute(output, input, args);
|
||||
} else {
|
||||
throw new CommandRunException("Unrecognized command", this); //$NON-NLS-1$
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
try {
|
||||
executeSub(args[0], Arrays.copyOfRange(args, 1, args.length));
|
||||
} catch (CommandRunException e) {
|
||||
executeSub(output, input, args[0],
|
||||
Arrays.copyOfRange(args, 1, args.length));
|
||||
} catch (final CommandRunException e) {
|
||||
if (e.getSource() != null) {
|
||||
throw e;
|
||||
}
|
||||
@@ -131,21 +136,6 @@ public class SubedCommand implements ICommandProvider, ICommand {
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see
|
||||
* fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String,
|
||||
* java.lang.String[]) */
|
||||
@Override
|
||||
public void executeSub(String command,
|
||||
String... args) throws CommandRunException {
|
||||
provider.executeSub(command, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICommand get(String commandName) {
|
||||
return provider.get(commandName);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#getCommandName() */
|
||||
@Override
|
||||
@@ -156,27 +146,29 @@ public class SubedCommand implements ICommandProvider, ICommand {
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#help() */
|
||||
@Override
|
||||
public void help(ConsoleManager manager,
|
||||
String... args) throws IOException {
|
||||
public void help(final ConsoleOutput manager,
|
||||
final String... args) throws IOException {
|
||||
if (args.length != 0 && !args[0].startsWith("-")) { //$NON-NLS-1$
|
||||
// Specific
|
||||
final ICommand c = get(args[0]);
|
||||
if (c != null) {
|
||||
c.help(manager, Arrays.copyOfRange(args, 1, args.length));
|
||||
} else {
|
||||
manager.println("No command "+Arrays.toString(args));
|
||||
}
|
||||
} else {
|
||||
// Generic
|
||||
if (noArgCommand != null && noArgCommand.tip() != null) {
|
||||
manager.println(TAB + noArgCommand.tip());
|
||||
}
|
||||
for (final ICommand cmd : provider.commands) {
|
||||
if (cmd.tip() == null) {
|
||||
manager.println(TAB + cmd.getCommandName());
|
||||
} else {
|
||||
manager.println(TAB + cmd.getCommandName() + ": " + //$NON-NLS-1$
|
||||
cmd.tip());
|
||||
}
|
||||
if (noArgCommand != null && noArgCommand.tip() != null) {
|
||||
manager.println(TAB + noArgCommand.tip());
|
||||
}
|
||||
for (final ICommand cmd : commands) {
|
||||
if (cmd.tip() == null) {
|
||||
manager.println(TAB + cmd.getCommandName());
|
||||
} else {
|
||||
manager.println(TAB + cmd.getCommandName() + ": " + //$NON-NLS-1$
|
||||
cmd.tip());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +183,6 @@ public class SubedCommand implements ICommandProvider, ICommand {
|
||||
* @see java.lang.Object#toString() */
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SubedCommand " + provider; //$NON-NLS-1$
|
||||
return "SubedCommand " + super.toString(); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,27 +38,33 @@
|
||||
*/
|
||||
package fr.bigeon.gclc.exception;
|
||||
|
||||
/** An exception raised during command parsing
|
||||
/** An exception raised during command parsing.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class CommandParsingException extends Exception {
|
||||
|
||||
/** svuid */
|
||||
/** svuid. */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** @param message an explaination
|
||||
* @param cause the cause */
|
||||
public CommandParsingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/** @param message an explaination */
|
||||
public CommandParsingException(String message) {
|
||||
/** Create the exception with a message.
|
||||
*
|
||||
* @param message the message */
|
||||
public CommandParsingException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/** @param cause the cause */
|
||||
public CommandParsingException(Throwable cause) {
|
||||
/** Create the exception with a message and a cause.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the cause */
|
||||
public CommandParsingException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/** Create the exception with a message.
|
||||
*
|
||||
* @param cause the cause */
|
||||
public CommandParsingException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,61 +40,69 @@ package fr.bigeon.gclc.exception;
|
||||
|
||||
import fr.bigeon.gclc.command.ICommand;
|
||||
|
||||
/** <p>
|
||||
* An exception thrown when a command failed to run correctly.
|
||||
/** An exception thrown when a command failed to run correctly.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class CommandRunException extends Exception {
|
||||
public final class CommandRunException extends Exception {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/** the SVUID. */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** The type of run exception */
|
||||
/** The type of run exception. */
|
||||
private final CommandRunExceptionType type;
|
||||
/** The command that caused the error */
|
||||
private final ICommand source;
|
||||
/** The command that caused the error. */
|
||||
private transient ICommand source;
|
||||
|
||||
/** @param message a message
|
||||
* @param source the source */
|
||||
public CommandRunException(String message, ICommand source) {
|
||||
super(message);
|
||||
type = CommandRunExceptionType.EXECUTION;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/** @param message a message
|
||||
* @param cause the cause
|
||||
* @param source the source */
|
||||
public CommandRunException(String message, Throwable cause,
|
||||
ICommand source) {
|
||||
super(message, cause);
|
||||
type = CommandRunExceptionType.EXECUTION;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/** @param type the type of exception
|
||||
/** Create the exception.
|
||||
*
|
||||
* @param type the type of exception
|
||||
* @param message the message
|
||||
* @param source the source */
|
||||
public CommandRunException(CommandRunExceptionType type, String message,
|
||||
ICommand source) {
|
||||
public CommandRunException(final CommandRunExceptionType type,
|
||||
final String message, final ICommand source) {
|
||||
super(message);
|
||||
this.type = type;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/** @param type the type of exception
|
||||
/** Create the exception with a cause.
|
||||
*
|
||||
* @param type the type of exception
|
||||
* @param message a message
|
||||
* @param cause the cause
|
||||
* @param source the source */
|
||||
public CommandRunException(CommandRunExceptionType type, String message,
|
||||
Throwable cause, ICommand source) {
|
||||
public CommandRunException(final CommandRunExceptionType type,
|
||||
final String message, final Throwable cause,
|
||||
final ICommand source) {
|
||||
super(message, cause);
|
||||
this.type = type;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/** Create the exception with type
|
||||
* {@link CommandRunExceptionType#EXECUTION}.
|
||||
*
|
||||
* @param message a message
|
||||
* @param source the source */
|
||||
public CommandRunException(final String message, final ICommand source) {
|
||||
super(message);
|
||||
type = CommandRunExceptionType.EXECUTION;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/** Create the exception with type {@link CommandRunExceptionType#EXECUTION}
|
||||
* and a cause.
|
||||
*
|
||||
* @param message a message
|
||||
* @param cause the cause
|
||||
* @param source the source */
|
||||
public CommandRunException(final String message, final Throwable cause,
|
||||
final ICommand source) {
|
||||
super(message, cause);
|
||||
type = CommandRunExceptionType.EXECUTION;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Throwable#getLocalizedMessage() */
|
||||
@Override
|
||||
@@ -106,12 +114,16 @@ public class CommandRunException extends Exception {
|
||||
return super.getLocalizedMessage();
|
||||
}
|
||||
|
||||
/** @return the source */
|
||||
/** Get the exception raising command.
|
||||
*
|
||||
* @return the source */
|
||||
public ICommand getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/** @return the type */
|
||||
/** Get the exception type.
|
||||
*
|
||||
* @return the type */
|
||||
public CommandRunExceptionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -38,14 +38,14 @@
|
||||
*/
|
||||
package fr.bigeon.gclc.exception;
|
||||
|
||||
/** The command run exception possible types
|
||||
/** The command run exception possible types.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public enum CommandRunExceptionType {
|
||||
/** Type of exception due to a wrong usage */
|
||||
/** Type of exception due to a wrong usage. */
|
||||
USAGE,
|
||||
/** Type of exception due to a problem in execution */
|
||||
/** Type of exception due to a problem in execution. */
|
||||
EXECUTION,
|
||||
/** Type of exception due to the impossibility to interact with user */
|
||||
/** Type of exception due to the impossibility to interact with user. */
|
||||
INTERACTION;
|
||||
}
|
||||
|
||||
@@ -38,36 +38,39 @@
|
||||
*/
|
||||
package fr.bigeon.gclc.exception;
|
||||
|
||||
/** <p>
|
||||
* Exception sent from the application when a command is added but the name of
|
||||
* the command is already used
|
||||
/** Exception sent from the application when a command is added but the name of
|
||||
* the command is already used.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class InvalidCommandName extends Exception {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/** the SVUID. */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Default constructor */
|
||||
/** Default constructor. */
|
||||
public InvalidCommandName() {
|
||||
super();
|
||||
}
|
||||
|
||||
/** @param message the message
|
||||
* @param cause the cause */
|
||||
public InvalidCommandName(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/** @param message the message */
|
||||
public InvalidCommandName(String message) {
|
||||
/** Create the exception with a message.
|
||||
*
|
||||
* @param message the message */
|
||||
public InvalidCommandName(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/** @param cause the cause */
|
||||
public InvalidCommandName(Throwable cause) {
|
||||
/** Create the exception with a message and a cause.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the cause */
|
||||
public InvalidCommandName(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/** Create the exception with a cause.
|
||||
*
|
||||
* @param cause the cause */
|
||||
public InvalidCommandName(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,24 +47,28 @@ package fr.bigeon.gclc.exception;
|
||||
* @author Emmanuel Bigeon */
|
||||
public class InvalidParameterException extends Exception {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/** the SVUID. */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** @param message the message
|
||||
* @param cause the cause */
|
||||
public InvalidParameterException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/** @param message the message */
|
||||
public InvalidParameterException(String message) {
|
||||
/** Create the exception with a message.
|
||||
*
|
||||
* @param message the message */
|
||||
public InvalidParameterException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/** @param cause the cause */
|
||||
public InvalidParameterException(Throwable cause) {
|
||||
/** Create the exception with a message and a cause.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the cause */
|
||||
public InvalidParameterException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/** Create the exception with a cause.
|
||||
*
|
||||
* @param cause the cause */
|
||||
public InvalidParameterException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.exception.package-info.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
/** Exceptions package.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
package fr.bigeon.gclc.exception;
|
||||
@@ -47,32 +47,32 @@ import java.util.logging.Logger;
|
||||
/** Internationalization class.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class Messages {
|
||||
/** The resource bundle name */
|
||||
public final class Messages {
|
||||
/** The resource bundle name. */
|
||||
private static final String BUNDLE_NAME = "fr.bigeon.gclc.l10n.messages"; //$NON-NLS-1$
|
||||
|
||||
/** The resource bundle */
|
||||
/** The resource bundle. */
|
||||
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
|
||||
.getBundle(BUNDLE_NAME);
|
||||
|
||||
/** The class logger */
|
||||
/** The class logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(Messages.class.getName());
|
||||
|
||||
/** Utility class */
|
||||
/** Utility class. */
|
||||
private Messages() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
/** Get formatted internationalized messages
|
||||
*
|
||||
/** Get formatted internationalized messages.
|
||||
*
|
||||
* @param key the message key
|
||||
* @param args the formatting arguments
|
||||
* @return the formatted internationalized message */
|
||||
public static String getString(String key, Object... args) {
|
||||
public static String getString(final String key, final Object... args) {
|
||||
try {
|
||||
return MessageFormat.format(RESOURCE_BUNDLE.getString(key), args);
|
||||
} catch (MissingResourceException e) {
|
||||
} catch (final MissingResourceException e) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Unrecognized internationalization message key: " + key, e); //$NON-NLS-1$
|
||||
return '!' + key + '!';
|
||||
|
||||
42
gclc/src/main/java/fr/bigeon/gclc/i18n/package-info.java
Normal file
42
gclc/src/main/java/fr/bigeon/gclc/i18n/package-info.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.i18n.package-info.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
/** Internationalization package.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
package fr.bigeon.gclc.i18n;
|
||||
@@ -33,69 +33,33 @@
|
||||
* knowledge of the CeCILL license and that you accept its terms.
|
||||
*/
|
||||
/**
|
||||
* gclc:fr.bigeon.gclc.ConsoleManager.java
|
||||
* Created on: Dec 19, 2014
|
||||
* gclc:fr.bigeon.gclc.manager.ConsoleInput.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.manager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
/** <p>
|
||||
* A console manager is in charge of the basic prompts and prints on a console.
|
||||
* <p>
|
||||
* Depending on the implementation, some manager may not support the prompt with
|
||||
* a message, in that case, the usual behavior is to prompt normally.
|
||||
import fr.bigeon.gclc.tools.StringProvider;
|
||||
|
||||
/** A console application input.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public interface ConsoleManager extends AutoCloseable {
|
||||
* @author Emmanuel Bigeon */
|
||||
public interface ConsoleInput extends AutoCloseable {
|
||||
|
||||
/** @return the prompt prefix */
|
||||
String getPrompt();
|
||||
|
||||
/** @param text the message to print (without line break at the end).
|
||||
* @throws IOException if the manager is closed or could not read the
|
||||
* prompt */
|
||||
void print(String text) throws IOException;
|
||||
|
||||
/** Prints an end of line
|
||||
*
|
||||
* @throws IOException if the manager is closed or could not read the
|
||||
* prompt */
|
||||
void println() throws IOException;
|
||||
|
||||
/** @param message the message to print
|
||||
* @throws IOException if the manager is closed or could not read the
|
||||
* prompt */
|
||||
void println(String message) throws IOException;
|
||||
|
||||
/** @return the user inputed string
|
||||
* @throws IOException if the manager is closed or could not read the prompt
|
||||
* @throws InterruptedIOException if the prompt was interrupted */
|
||||
String prompt() throws IOException;
|
||||
|
||||
/** @param message the message to prompt the user
|
||||
* @return the user inputed string
|
||||
* @throws IOException if the manager is closed or could not read the prompt
|
||||
* @throws InterruptedIOException if the prompt was interrupted */
|
||||
String prompt(String message) throws IOException;
|
||||
|
||||
/** <p>
|
||||
* Set a prompting prefix.
|
||||
/** Closes the input.
|
||||
*
|
||||
* @param prompt the prompt */
|
||||
void setPrompt(String prompt);
|
||||
|
||||
/** Closes the manager.
|
||||
*
|
||||
* @throws IOException if the close raised an exception */
|
||||
@Override
|
||||
void close() throws IOException;
|
||||
|
||||
/** @return if the manager is closed. */
|
||||
boolean isClosed();
|
||||
/** Get the prompt string.
|
||||
*
|
||||
* @return the prompt prefix */
|
||||
StringProvider getPrompt();
|
||||
|
||||
/** Indicate to the manager that is should interrompt the prompting, if
|
||||
/** Indicate to the input that is should interrompt the prompting, if
|
||||
* possible.
|
||||
* <p>
|
||||
* The pending {@link #prompt()} or {@link #prompt(String)} operations
|
||||
@@ -103,4 +67,52 @@ public interface ConsoleManager extends AutoCloseable {
|
||||
* (from the partial prompt content to an empty string or even a null
|
||||
* pointer). */
|
||||
void interruptPrompt();
|
||||
|
||||
/** Test if the input is closed.
|
||||
* <p>
|
||||
* If this is true, {@link #prompt()} methods will return immediatly and a
|
||||
* null chain.
|
||||
*
|
||||
* @return if the manager is closed. */
|
||||
boolean isClosed();
|
||||
|
||||
/** Prompt the user.
|
||||
*
|
||||
* @return the user inputed string
|
||||
* @throws IOException if the manager is closed or could not read the prompt
|
||||
* @throws InterruptedIOException if the prompt was interrupted */
|
||||
String prompt() throws IOException;
|
||||
|
||||
/** Prompt the user, with an allotated time to answer.
|
||||
*
|
||||
* @param timeout the time to wait in milliseconds
|
||||
* @return the user inputed string, null if the timeout was reached
|
||||
* @throws IOException if the manager is closed or could not read the prompt
|
||||
* @throws InterruptedIOException if the prompt was interrupted */
|
||||
String prompt(long timeout) throws IOException;
|
||||
|
||||
/** Prompt the user, with a hint on what is prompted.
|
||||
*
|
||||
* @param message the message to prompt the user
|
||||
* @return the user inputed string
|
||||
* @throws IOException if the manager is closed or could not read the prompt
|
||||
* @throws InterruptedIOException if the prompt was interrupted */
|
||||
String prompt(String message) throws IOException;
|
||||
|
||||
/** Prompt the user, with a hint on what is prompted and an allotated time
|
||||
* to answer.
|
||||
*
|
||||
* @param timeout the time to wait in milliseconds
|
||||
* @param message the message to prompt the user
|
||||
* @return the user inputed string, null if the timeout was reached
|
||||
* @throws IOException if the manager is closed or could not read the prompt
|
||||
* @throws InterruptedIOException if the prompt was interrupted */
|
||||
String prompt(String message, long timeout) throws IOException;
|
||||
|
||||
/** Set a prompting prefix.
|
||||
*
|
||||
* @param prompt the prompt */
|
||||
void setPrompt(String prompt);
|
||||
|
||||
void setPrompt(StringProvider string);
|
||||
}
|
||||
76
gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleOutput.java
Normal file
76
gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleOutput.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.manager.ConsoleOutput.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.manager;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** A console output definition.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public interface ConsoleOutput extends AutoCloseable {
|
||||
|
||||
/** Test if the output is closed.
|
||||
*
|
||||
* @return if the manager is closed. */
|
||||
boolean isClosed();
|
||||
|
||||
/** Print a string.
|
||||
*
|
||||
* @param text the message to print (without line break at the end).
|
||||
* @throws IOException if the manager is closed or could not read the
|
||||
* prompt */
|
||||
void print(String text) throws IOException;
|
||||
|
||||
/** Prints an end of line.
|
||||
*
|
||||
* @throws IOException if the manager is closed or could not read the
|
||||
* prompt */
|
||||
void println() throws IOException;
|
||||
|
||||
/** Print a string followed by an end of line.
|
||||
* <p>
|
||||
* This is the same as calling successively {@link #print(String)} and
|
||||
* {@link #println()}.
|
||||
*
|
||||
* @param message the message to print
|
||||
* @throws IOException if the manager is closed or could not read the
|
||||
* prompt */
|
||||
void println(String message) throws IOException;
|
||||
|
||||
}
|
||||
124
gclc/src/main/java/fr/bigeon/gclc/manager/EmptyInput.java
Normal file
124
gclc/src/main/java/fr/bigeon/gclc/manager/EmptyInput.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.manager.EmptyInput.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.manager;
|
||||
|
||||
import fr.bigeon.gclc.tools.ConstantString;
|
||||
import fr.bigeon.gclc.tools.StringProvider;
|
||||
|
||||
/** A console input that return empty to all prompting.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class EmptyInput implements ConsoleInput {
|
||||
|
||||
/** The empty prompter. */
|
||||
public static final ConsoleInput INSTANCE = new EmptyInput();
|
||||
/** The empty input. */
|
||||
private EmptyInput() {
|
||||
//
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#close() */
|
||||
@Override
|
||||
public void close() {
|
||||
//
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#getPrompt() */
|
||||
@Override
|
||||
public StringProvider getPrompt() {
|
||||
return new ConstantString(""); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#interruptPrompt() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
//
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt() */
|
||||
@Override
|
||||
public String prompt() {
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(long) */
|
||||
@Override
|
||||
public String prompt(final long timeout) {
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String) */
|
||||
@Override
|
||||
public String prompt(final String message) {
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String,
|
||||
* long) */
|
||||
@Override
|
||||
public String prompt(final String message,
|
||||
final long timeout) {
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */
|
||||
@Override
|
||||
public void setPrompt(final String prompt) {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrompt(StringProvider string) {
|
||||
//
|
||||
}
|
||||
}
|
||||
160
gclc/src/main/java/fr/bigeon/gclc/manager/PipedConsoleInput.java
Normal file
160
gclc/src/main/java/fr/bigeon/gclc/manager/PipedConsoleInput.java
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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-test:fr.bigeon.gclc.test.TestConsoleManager.java
|
||||
* Created on: Nov 18, 2016
|
||||
*/
|
||||
package fr.bigeon.gclc.manager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import fr.bigeon.gclc.tools.StringProvider;
|
||||
|
||||
/** This console input allows to enter commands and retrieve the output as an
|
||||
* input.
|
||||
* <p>
|
||||
* This console input is used to internally pilot an application. This can be
|
||||
* used to test application behavior.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class PipedConsoleInput
|
||||
implements ConsoleInput {
|
||||
|
||||
/** THe inner manager. */
|
||||
private final StreamConsoleInput innerManager;
|
||||
/** The stream to pipe commands into. */
|
||||
private final PipedOutputStream commandInput;
|
||||
/** The stream for the application to read commands from. */
|
||||
private final PipedInputStream in;
|
||||
/** The writing thread. */
|
||||
private final WritingRunnable writing;
|
||||
|
||||
/** Create a manager that will write and read through piped stream.
|
||||
*
|
||||
* @param outPrint the stream to write the prompting messages to
|
||||
* @throws IOException if the piping failed for streams */
|
||||
public PipedConsoleInput(final PrintStream outPrint) throws IOException {
|
||||
commandInput = new PipedOutputStream();
|
||||
in = new PipedInputStream(commandInput);
|
||||
innerManager = new StreamConsoleInput(outPrint, in,
|
||||
StandardCharsets.UTF_8);
|
||||
writing = new WritingRunnable(commandInput, StandardCharsets.UTF_8);
|
||||
final Thread th = new Thread(writing,
|
||||
"GCLC console piped input stream"); //$NON-NLS-1$
|
||||
th.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
writing.setRunning(false);
|
||||
in.close();
|
||||
innerManager.close();
|
||||
commandInput.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringProvider getPrompt() {
|
||||
return innerManager.getPrompt();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
innerManager.interruptPrompt();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return innerManager.isClosed();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt() */
|
||||
@Override
|
||||
public String prompt() throws IOException {
|
||||
return innerManager
|
||||
.prompt(innerManager.getPrompt() + System.lineSeparator());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#prompt(long) */
|
||||
@Override
|
||||
public String prompt(final long timeout) throws IOException {
|
||||
return innerManager.prompt(timeout);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#prompt(java.lang.String) */
|
||||
@Override
|
||||
public String prompt(final String message) throws IOException {
|
||||
return innerManager.prompt(message + System.lineSeparator());
|
||||
}
|
||||
|
||||
/* (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 {
|
||||
return innerManager.prompt(message + System.lineSeparator(), timeout);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */
|
||||
@Override
|
||||
public void setPrompt(final String prompt) {
|
||||
innerManager.setPrompt(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrompt(StringProvider string) {
|
||||
innerManager.setPrompt(string);
|
||||
}
|
||||
|
||||
/** Type a message in the input.
|
||||
*
|
||||
* @param content the content to type to the application
|
||||
* @throws IOException if the typing failed */
|
||||
public void type(final String content) throws IOException {
|
||||
writing.addMessage(content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,132 +44,110 @@ import java.io.InputStreamReader;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/** This console manager allows to enter commands and retrieve the output as an
|
||||
* input.
|
||||
/** This console output allows to retrieve the output as an input.
|
||||
* <p>
|
||||
* This console manager is used to internally pilot an application. This can be
|
||||
* This console output is used to internally pilot an application. This can be
|
||||
* used to test application behavior.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class PipedConsoleManager
|
||||
implements ConsoleManager {
|
||||
public final class PipedConsoleOutput
|
||||
implements ConsoleOutput {
|
||||
|
||||
/** The encoding between streams */
|
||||
/** The encoding between streams. */
|
||||
private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$
|
||||
/** THe inner manager */
|
||||
private final SystemConsoleManager innerManager;
|
||||
/** The stream to pipe commands into */
|
||||
private final PipedOutputStream commandInput;
|
||||
/** The reader to get application return from */
|
||||
/** THe inner manager. */
|
||||
private final StreamConsoleOutput innerManager;
|
||||
/** The reader to get application return from. */
|
||||
private final BufferedReader commandBuffOutput;
|
||||
/** The stream to get application return from */
|
||||
/** The stream to get application return from. */
|
||||
private final PipedInputStream commandOutput;
|
||||
/** The print writer for application to write return to */
|
||||
/** The print writer for application to write return to. */
|
||||
private final PrintStream outPrint;
|
||||
/** The stream for the application to read commands from */
|
||||
private final PipedInputStream in;
|
||||
/** The writing thread */
|
||||
private final WritingRunnable writing;
|
||||
/** The reading thread */
|
||||
/** The reading thread. */
|
||||
private final ReadingRunnable reading;
|
||||
|
||||
/** @throws IOException if the piping failed for streams */
|
||||
public PipedConsoleManager() throws IOException {
|
||||
commandInput = new PipedOutputStream();
|
||||
in = new PipedInputStream(commandInput);
|
||||
/** Create a manager that will write and read through piped stream.
|
||||
*
|
||||
* @throws IOException if the piping failed for streams */
|
||||
public PipedConsoleOutput() throws IOException {
|
||||
commandOutput = new PipedInputStream();
|
||||
PipedOutputStream out = new PipedOutputStream(commandOutput);
|
||||
final PipedOutputStream out = new PipedOutputStream(commandOutput);
|
||||
commandBuffOutput = new BufferedReader(
|
||||
new InputStreamReader(commandOutput, Charset.forName(UTF_8)));
|
||||
new InputStreamReader(commandOutput, StandardCharsets.UTF_8));
|
||||
outPrint = new PrintStream(out, true, UTF_8);
|
||||
innerManager = new SystemConsoleManager(outPrint, in,
|
||||
Charset.forName(UTF_8));
|
||||
writing = new WritingRunnable(commandInput, Charset.forName(UTF_8));
|
||||
innerManager = new StreamConsoleOutput(outPrint);
|
||||
reading = new ReadingRunnable(commandBuffOutput);
|
||||
Thread th = new Thread(writing, "write"); //$NON-NLS-1$
|
||||
th.start();
|
||||
th = new Thread(reading, "read"); //$NON-NLS-1$
|
||||
final Thread th = new Thread(reading, "GCLC console output forward"); //$NON-NLS-1$
|
||||
th.setDaemon(true);
|
||||
th.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrompt() {
|
||||
return innerManager.getPrompt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(String object) throws IOException {
|
||||
innerManager.print(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println() throws IOException {
|
||||
innerManager.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(String object) throws IOException {
|
||||
innerManager.println(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prompt() throws IOException {
|
||||
return innerManager
|
||||
.prompt(innerManager.getPrompt() + System.lineSeparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prompt(String message) throws IOException {
|
||||
return innerManager.prompt(message + System.lineSeparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrompt(String prompt) {
|
||||
innerManager.setPrompt(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
innerManager.close();
|
||||
reading.setRunning(false);
|
||||
writing.setRunning(false);
|
||||
outPrint.close();
|
||||
commandBuffOutput.close();
|
||||
commandOutput.close();
|
||||
in.close();
|
||||
commandInput.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return innerManager.isClosed();
|
||||
}
|
||||
|
||||
/** @param content the content to type to the application
|
||||
* @throws IOException if the typing failed */
|
||||
public void type(String content) throws IOException {
|
||||
writing.addMessage(content);
|
||||
}
|
||||
|
||||
/** @return the content of the next line written by the application
|
||||
* @throws IOException if the reading failed */
|
||||
public String readNextLine() throws IOException {
|
||||
return reading.getMessage();
|
||||
}
|
||||
|
||||
/** @return the content of the next line written by the application
|
||||
/** Test if there is available data.
|
||||
*
|
||||
* @return the content of the next line written by the application
|
||||
* @throws IOException if the reading failed */
|
||||
public boolean available() throws IOException {
|
||||
return reading.hasMessage();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
|
||||
* @see java.lang.AutoCloseable#close() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
innerManager.interruptPrompt();
|
||||
public void close() throws IOException {
|
||||
reading.setRunning(false);
|
||||
innerManager.close();
|
||||
outPrint.close();
|
||||
commandBuffOutput.close();
|
||||
commandOutput.close();
|
||||
}
|
||||
|
||||
/** Wait for a specific message to arrive.
|
||||
* <p>
|
||||
* When this method returns, the message was appended to the data, it
|
||||
* <em>may or may not</em> be the next line of data.
|
||||
*
|
||||
* @param message the message
|
||||
* @return the thread to join to wait for message delivery
|
||||
* @see fr.bigeon.gclc.manager.ReadingRunnable#getWaitForDelivery(java.lang.String) */
|
||||
public Thread getWaitForDelivery(final String message) {
|
||||
return reading.getWaitForDelivery(message);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return innerManager.isClosed();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#print(java.lang.String) */
|
||||
@Override
|
||||
public void print(final String object) throws IOException {
|
||||
innerManager.print(object);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#println() */
|
||||
@Override
|
||||
public void println() throws IOException {
|
||||
innerManager.println();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#println(java.lang.String) */
|
||||
@Override
|
||||
public void println(final String object) throws IOException {
|
||||
innerManager.println(object);
|
||||
}
|
||||
|
||||
/** Read the next line of data.
|
||||
*
|
||||
* @return the content of the next line written by the application
|
||||
* @throws IOException if the reading failed */
|
||||
public String readNextLine() throws IOException {
|
||||
return reading.getMessage();
|
||||
}
|
||||
}
|
||||
@@ -43,44 +43,263 @@ import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/** A runnable to read the piped output.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class ReadingRunnable implements Runnable {
|
||||
public final class ReadingRunnable implements Runnable {
|
||||
|
||||
/** The closed pipe message */
|
||||
/** The runnable to wait for arrival of a message in the queue.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
private final class ToWaitRunnable implements Runnable {
|
||||
/** The Object. */
|
||||
private final Object obj;
|
||||
/** The locking object. */
|
||||
private final Object start;
|
||||
/** The message. */
|
||||
private final String message;
|
||||
/** The started status. */
|
||||
private boolean started = false;
|
||||
|
||||
/** Create the waiting runnable.
|
||||
*
|
||||
* @param obj the object to lock on
|
||||
* @param start the object to notify when ready to wait
|
||||
* @param message the message to wait for */
|
||||
public ToWaitRunnable(final Object obj, final Object start,
|
||||
final String message) {
|
||||
this.obj = obj;
|
||||
this.start = start;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/** Test if the waiting runnable is started.
|
||||
*
|
||||
* @return the started */
|
||||
public boolean isStarted() {
|
||||
synchronized (start) {
|
||||
return started;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Runnable#run() */
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (obj) {
|
||||
synchronized (start) {
|
||||
started = true;
|
||||
start.notifyAll();
|
||||
}
|
||||
while (isRunning()) {
|
||||
try {
|
||||
obj.wait();
|
||||
if (delivering.equals(message)) {
|
||||
return;
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, THREAD_INTERRUPTION_EXCEPTION,
|
||||
e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The thread intteruption logging message. */
|
||||
private static final String THREAD_INTERRUPTION_EXCEPTION = "Thread interruption exception."; //$NON-NLS-1$
|
||||
/** The closed pipe message. */
|
||||
private static final String CLOSED_PIPE = "Closed pipe"; //$NON-NLS-1$
|
||||
/** Wait timeout */
|
||||
/** Wait timeout. */
|
||||
private static final long TIMEOUT = 1000;
|
||||
/** Class logger */
|
||||
/** Class logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(ReadingRunnable.class.getName());
|
||||
/** Read messages */
|
||||
/** Read messages. */
|
||||
private final Deque<String> messages = new ArrayDeque<>();
|
||||
/** the reader */
|
||||
/** the reader. */
|
||||
private final BufferedReader reader;
|
||||
/** the state of this runnable */
|
||||
/** the state of this runnable. */
|
||||
private boolean running = true;
|
||||
|
||||
/** Synchro object */
|
||||
/** Synchro object. */
|
||||
private final Object lock = new Object();
|
||||
/** The waiting status for a message */
|
||||
/** The waiting status for a message. */
|
||||
private boolean waiting;
|
||||
/** The blocker for a given message. */
|
||||
private final Map<String, Object> messageBlocker = new HashMap<>();
|
||||
/** The lock. */
|
||||
private final Object messageBlockerLock = new Object();
|
||||
/** The message being delivered. */
|
||||
private String delivering;
|
||||
|
||||
/** @param reader the input to read from */
|
||||
public ReadingRunnable(BufferedReader reader) {
|
||||
/** Create a reading runnable.
|
||||
*
|
||||
* @param reader the input to read from */
|
||||
public ReadingRunnable(final BufferedReader reader) {
|
||||
super();
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
/** Strip the string from head NULL characters.
|
||||
*
|
||||
* @param line the line to strip the null character from
|
||||
* @return the resulting string */
|
||||
private static String stripNull(final String line) {
|
||||
String res = line;
|
||||
while (res.length() > 0 && res.charAt(0) == 0) {
|
||||
LOGGER.severe(
|
||||
"NULL character heading the result of the read. This is a stream problem..."); //$NON-NLS-1$
|
||||
res = res.substring(1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** do wait for a message.
|
||||
* <p>This method should only be called inside a loop.
|
||||
* @param timeout the timeout of the wait.
|
||||
* @throws IOException if the runnable was stopped and no essage was found.
|
||||
*/
|
||||
private void doWaitMessage(final long timeout) throws IOException {
|
||||
try {
|
||||
lock.wait(timeout);
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, THREAD_INTERRUPTION_EXCEPTION, e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (messages.isEmpty() && !running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the next message.
|
||||
*
|
||||
* @return the next read message
|
||||
* @throws IOException if the pipe is closed */
|
||||
public String getMessage() throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
}
|
||||
waiting = true;
|
||||
waitMessage(TIMEOUT);
|
||||
LOGGER.finest("Polled: " + messages.peek()); //$NON-NLS-1$
|
||||
waiting = false;
|
||||
notifyMessage(messages.peek());
|
||||
return messages.poll();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the next message, but wait only a given time for it.
|
||||
*
|
||||
* @param timeout the read time out
|
||||
* @return The next message that was in the input
|
||||
* @throws IOException if the input was closed */
|
||||
public String getNextMessage(final long timeout) throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
}
|
||||
waiting = true;
|
||||
doWaitMessage(timeout);
|
||||
waiting = false;
|
||||
if (messages.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return messages.poll();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get a waiting thread for a specific message delivery.
|
||||
*
|
||||
* @param message the message
|
||||
* @return the thread to join to wait for message delivery */
|
||||
public Thread getWaitForDelivery(final String message) {
|
||||
synchronized (messageBlockerLock) {
|
||||
if (!messageBlocker.containsKey(message)) {
|
||||
messageBlocker.put(message, new Object());
|
||||
}
|
||||
final Object obj = messageBlocker.get(message);
|
||||
final Object start = new Object();
|
||||
final ToWaitRunnable waitRunn = new ToWaitRunnable(obj, start,
|
||||
message);
|
||||
final Thread th = new Thread(waitRunn);
|
||||
|
||||
synchronized (start) {
|
||||
th.start();
|
||||
while (!waitRunn.isStarted()) {
|
||||
try {
|
||||
start.wait(TIMEOUT);
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, THREAD_INTERRUPTION_EXCEPTION,
|
||||
e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
return th;
|
||||
}
|
||||
}
|
||||
|
||||
/** Test if some data is available.
|
||||
*
|
||||
* @return if a message is waiting
|
||||
* @throws IOException if the pipe is closed */
|
||||
public boolean hasMessage() throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
}
|
||||
return !messages.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Interrupts the wait on the next message by providing an empty
|
||||
* message. */
|
||||
public void interrupt() {
|
||||
synchronized (lock) {
|
||||
if (waiting) {
|
||||
messages.offer(""); //$NON-NLS-1$
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Test if this element is still running.
|
||||
*
|
||||
* @return the running */
|
||||
public boolean isRunning() {
|
||||
synchronized (lock) {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
|
||||
/** Notify the arrival of a given message.
|
||||
*
|
||||
* @param message the message */
|
||||
private void notifyMessage(final String message) {
|
||||
synchronized (messageBlockerLock) {
|
||||
delivering = message;
|
||||
if (messageBlocker.containsKey(message)) {
|
||||
final Object mLock = messageBlocker.get(message);
|
||||
synchronized (mLock) {
|
||||
mLock.notifyAll();
|
||||
}
|
||||
messageBlocker.remove(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Runnable#run() */
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
while (running) {
|
||||
try {
|
||||
String line = reader.readLine();
|
||||
@@ -93,90 +312,43 @@ public class ReadingRunnable implements Runnable {
|
||||
line = stripNull(line);
|
||||
synchronized (lock) {
|
||||
messages.add(line);
|
||||
lock.notify();
|
||||
lock.notifyAll();
|
||||
}
|
||||
} catch (final InterruptedIOException e) {
|
||||
if (running) {
|
||||
LOGGER.info("Reading interrupted"); //$NON-NLS-1$
|
||||
}
|
||||
LOGGER.log(Level.FINER,
|
||||
"Read interruption was caused by an exception", e); //$NON-NLS-1$
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.FINE, "The stream reading threw an exception", //$NON-NLS-1$
|
||||
e);
|
||||
if (running) {
|
||||
LOGGER.severe("Unable to read from stream"); //$NON-NLS-1$
|
||||
running = false;
|
||||
}
|
||||
} catch (InterruptedIOException e) {
|
||||
LOGGER.log(Level.INFO, "Reading interrupted", e); //$NON-NLS-1$
|
||||
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to read from stream", e); //$NON-NLS-1$
|
||||
running = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Strip the string from head NULL characters.
|
||||
*
|
||||
* @param line the line to strip the null character from
|
||||
* @return the resulting string */
|
||||
private static String stripNull(String line) {
|
||||
String res = line;
|
||||
while (res.length() > 0 && res.charAt(0) == 0) {
|
||||
LOGGER.severe(
|
||||
"NULL character heading the result of the read. This is a stream problem..."); //$NON-NLS-1$
|
||||
res = res.substring(1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @return the next read message
|
||||
* @throws IOException if the pipe is closed */
|
||||
public String getMessage() throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
}
|
||||
waiting = true;
|
||||
while (messages.isEmpty()) {
|
||||
try {
|
||||
lock.wait(TIMEOUT);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Thread interruption exception.", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
if (messages.isEmpty() && !running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
}
|
||||
}
|
||||
LOGGER.finest("Polled: " + messages.peek()); //$NON-NLS-1$
|
||||
waiting = false;
|
||||
return messages.poll();
|
||||
}
|
||||
}
|
||||
|
||||
/** @param running the running to set */
|
||||
public void setRunning(boolean running) {
|
||||
/** Set the running status for this reading runnable.
|
||||
*
|
||||
* @param running the running to set */
|
||||
public void setRunning(final boolean running) {
|
||||
synchronized (lock) {
|
||||
this.running = running;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the running */
|
||||
public boolean isRunning() {
|
||||
synchronized (lock) {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return if a message is waiting
|
||||
* @throws IOException if the pipe is closed */
|
||||
public boolean hasMessage() throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!running) {
|
||||
throw new IOException(CLOSED_PIPE);
|
||||
}
|
||||
return !messages.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Interrupts the wait on the next message by providing an empty message */
|
||||
public void interrupt() {
|
||||
synchronized (lock) {
|
||||
if (waiting) {
|
||||
messages.offer(""); //$NON-NLS-1$
|
||||
lock.notify();
|
||||
}
|
||||
/** Wait for the next message to be integratted.
|
||||
*
|
||||
* @param timeout the timeout to wait
|
||||
* @throws IOException if the next message was not delivered and the
|
||||
* runnable stopped. */
|
||||
private void waitMessage(final long timeout) throws IOException {
|
||||
while (messages.isEmpty()) {
|
||||
doWaitMessage(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
89
gclc/src/main/java/fr/bigeon/gclc/manager/SinkOutput.java
Normal file
89
gclc/src/main/java/fr/bigeon/gclc/manager/SinkOutput.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.manager.SinkOutput.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
package fr.bigeon.gclc.manager;
|
||||
|
||||
/** A console output that absorbs every message.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public final class SinkOutput implements ConsoleOutput {
|
||||
|
||||
/** The sink output. */
|
||||
public static final ConsoleOutput INSTANCE = new SinkOutput();
|
||||
|
||||
/** Singleton constructor. */
|
||||
private SinkOutput() {
|
||||
//
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#close() */
|
||||
@Override
|
||||
public void close() {
|
||||
//
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#print(java.lang.String) */
|
||||
@Override
|
||||
public void print(final String text) {
|
||||
//
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#println() */
|
||||
@Override
|
||||
public void println() {
|
||||
//
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleOutput#println(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void println(final String message) {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,44 +45,49 @@ import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import fr.bigeon.gclc.tools.ConstantString;
|
||||
import fr.bigeon.gclc.tools.StringProvider;
|
||||
|
||||
/** A console using the input stream and print stream.
|
||||
* <p>
|
||||
* The default constructor will use the system standart input and output.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public final class SystemConsoleManager implements ConsoleManager { // NOSONAR
|
||||
public final class StreamConsoleInput implements ConsoleInput {
|
||||
|
||||
/** The default prompt */
|
||||
public static final String DEFAULT_PROMPT = "> "; //$NON-NLS-1$
|
||||
/** The default prompt. */
|
||||
public static final StringProvider DEFAULT_PROMPT = new ConstantString("> "); //$NON-NLS-1$
|
||||
|
||||
/** The command prompt. It can be changed. */
|
||||
private String prompt = DEFAULT_PROMPT;
|
||||
private StringProvider prompt = DEFAULT_PROMPT;
|
||||
|
||||
/** The print stream */
|
||||
/** The print stream. */
|
||||
private final PrintStream out;
|
||||
/** The input stream */
|
||||
/** The input stream. */
|
||||
private final BufferedReader in;
|
||||
|
||||
/** If the manager is closed */
|
||||
/** If the manager is closed. */
|
||||
private boolean closed = false;
|
||||
|
||||
/** The prompting thread */
|
||||
/** The prompting thread. */
|
||||
private final Thread promptThread;
|
||||
|
||||
/** The reading runnable */
|
||||
/** The reading runnable. */
|
||||
private final ReadingRunnable reading;
|
||||
|
||||
/** This default constructor relies on the system defined standart output
|
||||
* and input stream. */
|
||||
public SystemConsoleManager() {
|
||||
public StreamConsoleInput() {
|
||||
this(System.out, System.in, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
/** @param out the output stream
|
||||
/** Create the stream base console input.
|
||||
*
|
||||
* @param out the output stream
|
||||
* @param in the input stream
|
||||
* @param charset the charset for the input */
|
||||
public SystemConsoleManager(PrintStream out, InputStream in,
|
||||
Charset charset) {
|
||||
public StreamConsoleInput(final PrintStream out, final InputStream in,
|
||||
final Charset charset) {
|
||||
super();
|
||||
this.out = out;
|
||||
this.in = new BufferedReader(new InputStreamReader(in, charset));
|
||||
@@ -92,65 +97,15 @@ public final class SystemConsoleManager implements ConsoleManager { // NOSONAR
|
||||
promptThread.start();
|
||||
}
|
||||
|
||||
/** @return the prompt */
|
||||
@Override
|
||||
public String getPrompt() {
|
||||
return prompt;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#print(java.lang.Object) */
|
||||
@Override
|
||||
public void print(String object) throws IOException {
|
||||
checkOpen();
|
||||
out.print(object);
|
||||
}
|
||||
|
||||
/** @throws IOException if the stream was closed */
|
||||
/** Check that the console input is not closed.
|
||||
*
|
||||
* @throws IOException if the stream was closed */
|
||||
private void checkOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#println() */
|
||||
@Override
|
||||
public void println() throws IOException {
|
||||
checkOpen();
|
||||
out.println();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#println(java.lang.Object) */
|
||||
@Override
|
||||
public void println(String object) throws IOException {
|
||||
checkOpen();
|
||||
out.println(object);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#prompt() */
|
||||
@Override
|
||||
public String prompt() throws IOException {
|
||||
return prompt(prompt);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */
|
||||
@Override
|
||||
public String prompt(String message) throws IOException {
|
||||
checkOpen();
|
||||
out.print(message);
|
||||
return reading.getMessage();
|
||||
}
|
||||
|
||||
/** @param prompt the prompt to set */
|
||||
@Override
|
||||
public void setPrompt(String prompt) {
|
||||
this.prompt = prompt;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#close() */
|
||||
@Override
|
||||
@@ -160,6 +115,21 @@ public final class SystemConsoleManager implements ConsoleManager { // NOSONAR
|
||||
promptThread.interrupt();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#getPrompt() */
|
||||
@Override
|
||||
public StringProvider getPrompt() {
|
||||
return prompt;
|
||||
}
|
||||
|
||||
/** Beware, in this implementation this is the same as closing the manager.
|
||||
*
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#interruptPrompt() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
reading.interrupt();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
|
||||
@Override
|
||||
@@ -167,12 +137,54 @@ public final class SystemConsoleManager implements ConsoleManager { // NOSONAR
|
||||
return closed;
|
||||
}
|
||||
|
||||
/** Beware, in this implementation this is the same as closing the manager.
|
||||
*
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#prompt() */
|
||||
@Override
|
||||
public void interruptPrompt() {
|
||||
reading.interrupt();
|
||||
public String prompt() throws IOException {
|
||||
return prompt(prompt.apply());
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#prompt(long) */
|
||||
@Override
|
||||
public String prompt(final long timeout) throws IOException {
|
||||
return prompt(prompt.apply(), timeout);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */
|
||||
@Override
|
||||
public String prompt(final String message) throws IOException {
|
||||
checkOpen();
|
||||
if (out != null) {
|
||||
out.print(message);
|
||||
out.flush();
|
||||
}
|
||||
return reading.getMessage();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */
|
||||
@Override
|
||||
public String prompt(final String message,
|
||||
final long timeout) throws IOException {
|
||||
checkOpen();
|
||||
if (out != null) {
|
||||
out.print(message);
|
||||
out.flush();
|
||||
}
|
||||
return reading.getNextMessage(timeout);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */
|
||||
@Override
|
||||
public void setPrompt(final String prompt) {
|
||||
this.prompt = new ConstantString(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrompt(StringProvider string) {
|
||||
this.prompt = string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.system.SystemConsoleManager.java
|
||||
* Created on: Dec 19, 2014
|
||||
*/
|
||||
package fr.bigeon.gclc.manager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
/** A console using the input stream and print stream.
|
||||
* <p>
|
||||
* The default constructor will use the system standart input and output.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public final class StreamConsoleOutput implements ConsoleOutput {
|
||||
/** The print stream. */
|
||||
private final PrintStream out;
|
||||
/** If the manager is closed. */
|
||||
private boolean closed = false;
|
||||
|
||||
/** This default constructor relies on the system defined standart output
|
||||
* and input stream. */
|
||||
public StreamConsoleOutput() {
|
||||
this(System.out);
|
||||
}
|
||||
|
||||
/** Create a print stream based console output.
|
||||
*
|
||||
* @param out the output stream */
|
||||
public StreamConsoleOutput(final PrintStream out) {
|
||||
super();
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/** Check the open status.
|
||||
*
|
||||
* @throws IOException if the stream was closed */
|
||||
private void checkOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#close() */
|
||||
@Override
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#print(java.lang.Object) */
|
||||
@Override
|
||||
public void print(final String object) throws IOException {
|
||||
checkOpen();
|
||||
out.print(object);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#println() */
|
||||
@Override
|
||||
public void println() throws IOException {
|
||||
checkOpen();
|
||||
out.println();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.ConsoleManager#println(java.lang.Object) */
|
||||
@Override
|
||||
public void println(final String object) throws IOException {
|
||||
checkOpen();
|
||||
out.println(object);
|
||||
}
|
||||
}
|
||||
@@ -52,89 +52,109 @@ import java.util.logging.Logger;
|
||||
* Messages are queued to be retrieved latter on.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class WritingRunnable implements Runnable {
|
||||
public final class WritingRunnable implements Runnable {
|
||||
|
||||
/** Wait timeout */
|
||||
/** Wait timeout. */
|
||||
private static final long TIMEOUT = 1000;
|
||||
/** Class logger */
|
||||
/** Class logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(WritingRunnable.class.getName());
|
||||
/** Messages to write */
|
||||
/** Messages to write. */
|
||||
private final Deque<String> messages = new ArrayDeque<>();
|
||||
/** Stream to write to */
|
||||
/** Stream to write to. */
|
||||
private final OutputStream outPrint;
|
||||
/** The charset */
|
||||
/** The charset. */
|
||||
private final Charset charset;
|
||||
/** Runnable state */
|
||||
/** Runnable state. */
|
||||
private boolean running = true;
|
||||
|
||||
/** Synchro object */
|
||||
/** Synchro object. */
|
||||
private final Object lock = new Object();
|
||||
|
||||
/** @param outPrint the output to print to
|
||||
/** Create the writing runnable.
|
||||
*
|
||||
* @param outPrint the output to print to
|
||||
* @param charset the charset of the stream */
|
||||
public WritingRunnable(OutputStream outPrint, Charset charset) {
|
||||
public WritingRunnable(final OutputStream outPrint, final Charset charset) {
|
||||
super();
|
||||
this.outPrint = outPrint;
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
/** Add a message in the queue.
|
||||
*
|
||||
* @param message the message
|
||||
* @throws IOException if the pipe is closed */
|
||||
public void addMessage(final String message) throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!running) {
|
||||
throw new IOException("Closed pipe"); //$NON-NLS-1$
|
||||
}
|
||||
messages.offer(message);
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/** Test if the message is running.
|
||||
*
|
||||
* @return the running */
|
||||
public boolean isRunning() {
|
||||
synchronized (lock) {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Runnable#run() */
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
while (running) {
|
||||
synchronized (lock) {
|
||||
while (messages.isEmpty()) {
|
||||
try {
|
||||
lock.wait(TIMEOUT);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE,
|
||||
"Thread interruption exception.", e); //$NON-NLS-1$
|
||||
}
|
||||
waitNextMessage();
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
String message = messages.poll();
|
||||
ByteBuffer buff = charset
|
||||
.encode(message + System.lineSeparator());
|
||||
if (buff.hasArray()) {
|
||||
try {
|
||||
outPrint.write(buff.array());
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to write to stream", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
}
|
||||
writeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param message the message
|
||||
* @throws IOException if the pipe is closed */
|
||||
public void addMessage(String message) throws IOException {
|
||||
synchronized (lock) {
|
||||
if (!running) {
|
||||
throw new IOException("Closed pipe"); //$NON-NLS-1$
|
||||
}
|
||||
messages.offer(message);
|
||||
lock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/** @param running the running to set */
|
||||
public void setRunning(boolean running) {
|
||||
/** Set the running status.
|
||||
*
|
||||
* @param running the running to set */
|
||||
public void setRunning(final boolean running) {
|
||||
synchronized (lock) {
|
||||
this.running = running;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the running */
|
||||
public boolean isRunning() {
|
||||
synchronized (lock) {
|
||||
return running;
|
||||
/** Wait for next message. */
|
||||
private void waitNextMessage() {
|
||||
try {
|
||||
lock.wait(TIMEOUT);
|
||||
} catch (final InterruptedException e) {
|
||||
if (running) {
|
||||
LOGGER.log(Level.SEVERE,
|
||||
"Thread interruption exception.", e); //$NON-NLS-1$
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/** Write next message to output. */
|
||||
private void writeMessage() {
|
||||
final String message = messages.poll();
|
||||
final ByteBuffer buff = charset
|
||||
.encode(message + System.lineSeparator());
|
||||
if (buff.hasArray()) {
|
||||
try {
|
||||
outPrint.write(buff.array());
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to write to stream", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
gclc/src/main/java/fr/bigeon/gclc/manager/package-info.java
Normal file
42
gclc/src/main/java/fr/bigeon/gclc/manager/package-info.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.manager.package-info.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
/** The console input and output definitions.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
package fr.bigeon.gclc.manager;
|
||||
42
gclc/src/main/java/fr/bigeon/gclc/package-info.java
Normal file
42
gclc/src/main/java/fr/bigeon/gclc/package-info.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.package-info.java
|
||||
* Created on: Nov 5, 2017
|
||||
*/
|
||||
/** A framework for commad line applications.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
package fr.bigeon.gclc;
|
||||
@@ -43,65 +43,95 @@ import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
* The {@link CLIPrompter} class is a utility class that provides method to
|
||||
/** The {@link CLIPrompter} class is a utility class that provides method to
|
||||
* prompt the user.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class CLIPrompter {
|
||||
public final class CLIPrompter {
|
||||
|
||||
/** message key for format error in prompting a choice */
|
||||
/** The zero integer. */
|
||||
private static final Integer ZERO = Integer.valueOf(0);
|
||||
/** message key for format error in prompting a choice. */
|
||||
private static final String PROMPTCHOICE_FORMATERR = "promptchoice.formaterr"; //$NON-NLS-1$
|
||||
/** message key for out of bound error in prompting a choice */
|
||||
/** message key for out of bound error in prompting a choice. */
|
||||
private static final String PROMPTCHOICE_OUTOFBOUNDS = "promptchoice.outofbounds"; //$NON-NLS-1$
|
||||
/** message key for first form of no in prompting a choice */
|
||||
/** message key for first form of no in prompting a choice. */
|
||||
private static final String PROMPTBOOL_CHOICES_NO1 = "promptbool.choices.no1"; //$NON-NLS-1$
|
||||
/** message key for first form of yes in prompting a choice */
|
||||
/** message key for first form of yes in prompting a choice. */
|
||||
private static final String PROMPTBOOL_CHOICES_YES1 = "promptbool.choices.yes1"; //$NON-NLS-1$
|
||||
@SuppressWarnings("javadoc")
|
||||
/** Message key for boolean choosing. */
|
||||
private static final String BOOL_CHOICES = "promptbool.choices"; //$NON-NLS-1$
|
||||
@SuppressWarnings("javadoc")
|
||||
/** Message key for the list end of prompt symbol. */
|
||||
private static final String LIST_DISP_KEY = "promptlist.exit.dispkey"; //$NON-NLS-1$
|
||||
@SuppressWarnings("javadoc")
|
||||
/** Message key for the line prompt. */
|
||||
private static final String PROMPT = "prompt.lineprompt"; //$NON-NLS-1$
|
||||
@SuppressWarnings("javadoc")
|
||||
/** Message key for the separation of choices selection. */
|
||||
private static final String LIST_CHOICE_SEP = "promptlist.multi.sepkey"; //$NON-NLS-1$
|
||||
/** The class logger */
|
||||
/** The class logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(CLIPrompter.class.getName());
|
||||
|
||||
/** Utility class */
|
||||
/** Utility class. */
|
||||
private CLIPrompter() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Add a user choice to the list.
|
||||
*
|
||||
* @param val the string to parse
|
||||
* @param chs the list of integers
|
||||
* @param index the max index of choice
|
||||
* @return if the parsing was done correctly */
|
||||
private static boolean addUserChoice(final String val,
|
||||
final List<Integer> chs,
|
||||
final int index) {
|
||||
if (val.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
final int r;
|
||||
r = Integer.parseInt(val);
|
||||
if (r >= 0 && r <= index) {
|
||||
chs.add(Integer.valueOf(r));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** List the choices on the output.
|
||||
*
|
||||
* @param output the manager
|
||||
* @param choices the choices
|
||||
* @param cancel the cancel option if it exists
|
||||
* @return the number of choices plus one (or the number of choices if there
|
||||
* is a cancel)
|
||||
* @throws IOException if the manager was closed */
|
||||
private static <U> int listChoices(ConsoleManager manager, List<U> choices,
|
||||
String cancel) throws IOException {
|
||||
private static <U> Integer listChoices(final ConsoleOutput output,
|
||||
final List<U> choices,
|
||||
final String cancel) throws IOException {
|
||||
int index = 0;
|
||||
for (final U u : choices) {
|
||||
manager.println(index++ + ") " + u); //$NON-NLS-1$
|
||||
output.println(index++ + ") " + u); //$NON-NLS-1$
|
||||
}
|
||||
if (cancel != null) {
|
||||
manager.println(index++ + ") " + cancel); //$NON-NLS-1$
|
||||
output.println(index++ + ") " + cancel); //$NON-NLS-1$
|
||||
}
|
||||
return index - 1;
|
||||
return Integer.valueOf(index - 1);
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt for a boolean value.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param message the prompting message
|
||||
* @return the choice
|
||||
* @throws IOException if the manager was closed */
|
||||
public static boolean promptBoolean(ConsoleManager manager,
|
||||
String message) throws IOException {
|
||||
String result = manager
|
||||
public static boolean promptBoolean(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final String message) throws IOException {
|
||||
String result = input
|
||||
.prompt(message + CLIPrompterMessages.getString(BOOL_CHOICES));
|
||||
boolean first = true;
|
||||
final String choices = CLIPrompterMessages
|
||||
@@ -120,7 +150,7 @@ public class CLIPrompter {
|
||||
|
||||
manager.println(CLIPrompterMessages
|
||||
.getString("promptbool.choices.invalid", choices)); //$NON-NLS-1$
|
||||
result = manager.prompt(
|
||||
result = input.prompt(
|
||||
message + CLIPrompterMessages.getString(BOOL_CHOICES));
|
||||
}
|
||||
first = false;
|
||||
@@ -131,7 +161,10 @@ public class CLIPrompter {
|
||||
.getString("promptbool.choices.yes2")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt for a choice.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param keys the keys to be printed
|
||||
* @param choices the real choices
|
||||
* @param message the message
|
||||
@@ -139,18 +172,24 @@ public class CLIPrompter {
|
||||
* @param <U> the type of elements
|
||||
* @return the choice
|
||||
* @throws IOException if the manager was closed */
|
||||
@SuppressWarnings("boxing")
|
||||
public static <U> U promptChoice(ConsoleManager manager, List<String> keys,
|
||||
List<U> choices, String message,
|
||||
String cancel) throws IOException {
|
||||
final Integer index = promptChoice(manager, keys, message, cancel);
|
||||
public static <U> U promptChoice(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final List<String> keys,
|
||||
final List<U> choices,
|
||||
final String message,
|
||||
final String cancel) throws IOException {
|
||||
final Integer index = promptChoice(manager, input, keys, message,
|
||||
cancel);
|
||||
if (index == null) {
|
||||
return null;
|
||||
}
|
||||
return choices.get(index);
|
||||
return choices.get(index.intValue());
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt for a choice.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param <U> The choices labels type
|
||||
* @param <T> The real choices objects
|
||||
* @param choices the list of labels (in order to be displayed)
|
||||
@@ -159,60 +198,66 @@ public class CLIPrompter {
|
||||
* @param cancel the cancel option if it exists (null otherwise)
|
||||
* @return the chosen object
|
||||
* @throws IOException if the manager was closed */
|
||||
public static <U, T> T promptChoice(ConsoleManager manager, List<U> choices,
|
||||
Map<U, T> choicesMap, String message,
|
||||
String cancel) throws IOException {
|
||||
Integer res = promptChoice(manager, choices, message, cancel);
|
||||
public static <U, T> T promptChoice(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final List<U> choices,
|
||||
final Map<U, T> choicesMap,
|
||||
final String message,
|
||||
final String cancel) throws IOException {
|
||||
final Integer res = promptChoice(manager, input, choices, message,
|
||||
cancel);
|
||||
if (res == null) {
|
||||
return null;
|
||||
}
|
||||
return choicesMap.get(choices.get(res.intValue()));
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt for a choice.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param <U> the type of choices
|
||||
* @param choices the list of choices
|
||||
* @param message the prompting message
|
||||
* @param cancel the cancel option, or null
|
||||
* @return the index of the choice
|
||||
* @throws IOException if the manager was closed */
|
||||
@SuppressWarnings("boxing")
|
||||
public static <U> Integer promptChoice(ConsoleManager manager,
|
||||
List<U> choices, String message,
|
||||
String cancel) throws IOException {
|
||||
public static <U> Integer promptChoice(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final List<U> choices,
|
||||
final String message,
|
||||
final String cancel) throws IOException {
|
||||
manager.println(message);
|
||||
final int index = listChoices(manager, choices, cancel);
|
||||
String result = ""; //$NON-NLS-1$
|
||||
boolean keepOn = true;
|
||||
final Integer index = listChoices(manager, choices, cancel);
|
||||
String result;
|
||||
int r = -1;
|
||||
while (keepOn) {
|
||||
result = manager.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
while (true) {
|
||||
result = input.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
try {
|
||||
r = Integer.parseInt(result);
|
||||
if (r >= 0 && r <= index) {
|
||||
keepOn = false;
|
||||
} else {
|
||||
manager.println(CLIPrompterMessages
|
||||
.getString(PROMPTCHOICE_OUTOFBOUNDS, 0, index));
|
||||
listChoices(manager, choices, cancel);
|
||||
if (r >= 0 && r <= index.intValue()) {
|
||||
break;
|
||||
}
|
||||
|
||||
manager.println(CLIPrompterMessages
|
||||
.getString(PROMPTCHOICE_OUTOFBOUNDS, ZERO, index));
|
||||
} catch (final NumberFormatException e) {
|
||||
LOGGER.log(Level.FINER,
|
||||
"Unrecognized number. Prompting user again.", e); //$NON-NLS-1$
|
||||
keepOn = true;
|
||||
manager.println(CLIPrompterMessages
|
||||
.getString(PROMPTCHOICE_FORMATERR, 0, index));
|
||||
listChoices(manager, choices, cancel);
|
||||
.getString(PROMPTCHOICE_FORMATERR, ZERO, index));
|
||||
}
|
||||
listChoices(manager, choices, cancel);
|
||||
}
|
||||
if (r == index && cancel != null) {
|
||||
if (r == index.intValue() && cancel != null) {
|
||||
return null;
|
||||
}
|
||||
return r;
|
||||
return Integer.valueOf(r);
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt for a choice.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param <U> The choices labels type
|
||||
* @param <T> The real choices objects
|
||||
* @param choicesMap the map of label to actual objects
|
||||
@@ -220,19 +265,24 @@ public class CLIPrompter {
|
||||
* @param cancel the cancel option (or null)
|
||||
* @return the chosen object
|
||||
* @throws IOException if the manager was closed */
|
||||
public static <U, T> T promptChoice(ConsoleManager manager,
|
||||
Map<U, T> choicesMap, String message,
|
||||
String cancel) throws IOException {
|
||||
return promptChoice(manager, new ArrayList<>(choicesMap.keySet()),
|
||||
choicesMap, message, cancel);
|
||||
public static <U, T> T promptChoice(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final Map<U, T> choicesMap,
|
||||
final String message,
|
||||
final String cancel) throws IOException {
|
||||
return promptChoice(manager, input,
|
||||
new ArrayList<>(choicesMap.keySet()), choicesMap, message,
|
||||
cancel);
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt for an integer.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param message the prompt message
|
||||
* @return the integer
|
||||
* @throws IOException if the manager was closed */
|
||||
public static int promptInteger(ConsoleManager manager,
|
||||
String message) throws IOException {
|
||||
public static int promptInteger(final ConsoleInput manager,
|
||||
final String message) throws IOException {
|
||||
boolean still = true;
|
||||
int r = 0;
|
||||
while (still) {
|
||||
@@ -245,42 +295,46 @@ public class CLIPrompter {
|
||||
r = Integer.parseInt(result);
|
||||
still = false;
|
||||
} catch (final NumberFormatException e) {
|
||||
LOGGER.log(Level.INFO,
|
||||
"User input a non parsable integer: " + result, e); //$NON-NLS-1$
|
||||
LOGGER.info("User input a non parsable integer: " + result); //$NON-NLS-1$
|
||||
LOGGER.log(Level.FINEST, "Unrecognized integer", e); //$NON-NLS-1$
|
||||
still = true;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** This methods prompt the user for a list of elements
|
||||
/** This methods prompt the user for a list of elements.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param message the message
|
||||
* @return the list of user inputs
|
||||
* @throws IOException if the manager was closed */
|
||||
public static List<String> promptList(ConsoleManager manager,
|
||||
String message) throws IOException {
|
||||
return promptList(manager, message,
|
||||
public static List<String> promptList(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final String message) throws IOException {
|
||||
return promptList(manager, input, message,
|
||||
CLIPrompterMessages.getString("promptlist.exit.defaultkey")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/** This methods prompt the user for a list of elements
|
||||
/** This methods prompt the user for a list of elements.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param message the message
|
||||
* @param ender the ending sequence for the list
|
||||
* @return the list of user inputs
|
||||
* @throws IOException if the manager was closed */
|
||||
public static List<String> promptList(ConsoleManager manager,
|
||||
String message,
|
||||
String ender) throws IOException {
|
||||
public static List<String> promptList(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final String message,
|
||||
final String ender) throws IOException {
|
||||
final List<String> strings = new ArrayList<>();
|
||||
manager.println(
|
||||
message + CLIPrompterMessages.getString(LIST_DISP_KEY, ender));
|
||||
String res = null;
|
||||
while (!ender.equals(res)) {
|
||||
res = manager.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
res = input.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
if (!res.equals(ender)) {
|
||||
strings.add(res);
|
||||
}
|
||||
@@ -291,49 +345,60 @@ public class CLIPrompter {
|
||||
/** Prompt for a text with several lines.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param message the prompting message
|
||||
* @return the text
|
||||
* @throws IOException if the manager was closed */
|
||||
public static String promptLongText(ConsoleManager manager,
|
||||
String message) throws IOException {
|
||||
return promptLongText(manager, message, CLIPrompterMessages
|
||||
public static String promptLongText(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final String message) throws IOException {
|
||||
return promptLongText(manager, input, message, CLIPrompterMessages
|
||||
.getString("promptlongtext.exit.defaultkey")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/** Prompt for a text with several lines.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param message the prompting message
|
||||
* @param ender the ender character
|
||||
* @return the text
|
||||
* @throws IOException if the manager was closed */
|
||||
public static String promptLongText(ConsoleManager manager, String message,
|
||||
String ender) throws IOException {
|
||||
public static String promptLongText(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final String message,
|
||||
final String ender) throws IOException {
|
||||
manager.println(message + CLIPrompterMessages
|
||||
.getString("promptlongtext.exit.dispkey", ender)); //$NON-NLS-1$
|
||||
String res = manager.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
String line = res;
|
||||
while (!line.equals(ender)) {
|
||||
line = manager.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
final StringBuilder res = new StringBuilder();
|
||||
String line;
|
||||
do {
|
||||
line = input.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
if (!line.equals(ender)) {
|
||||
res += System.lineSeparator() + line;
|
||||
res.append(line);
|
||||
res.append(System.lineSeparator());
|
||||
}
|
||||
}
|
||||
return res.equals(ender) ? "" : res; //$NON-NLS-1$
|
||||
} while (!line.equals(ender));
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt the user to select zero or more elements from a list.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param keys the keys to be printed
|
||||
* @param choices the real choices
|
||||
* @param message the message
|
||||
* @param <U> the type of elements
|
||||
* @return the choice
|
||||
* @throws IOException if the manager was closed */
|
||||
public static <U> List<U> promptMultiChoice(ConsoleManager manager,
|
||||
List<String> keys,
|
||||
List<U> choices,
|
||||
String message) throws IOException {
|
||||
final List<Integer> indices = promptMultiChoice(manager, keys, message);
|
||||
public static <U> List<U> promptMultiChoice(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final List<String> keys,
|
||||
final List<U> choices,
|
||||
final String message) throws IOException {
|
||||
final List<Integer> indices = promptMultiChoice(manager, input, keys,
|
||||
message);
|
||||
final List<U> userChoices = new ArrayList<>();
|
||||
for (final Integer integer : indices) {
|
||||
userChoices.add(choices.get(integer.intValue()));
|
||||
@@ -341,7 +406,10 @@ public class CLIPrompter {
|
||||
return userChoices;
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt the user to select zero or more elements from a list.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param <U> The choices labels type
|
||||
* @param <T> The real choices objects
|
||||
* @param choices the list of labels (in order to be displayed)
|
||||
@@ -349,11 +417,13 @@ public class CLIPrompter {
|
||||
* @param message the prompting message
|
||||
* @return the chosen objects (or an empty list)
|
||||
* @throws IOException if the manager was closed */
|
||||
public static <U, T> List<T> promptMultiChoice(ConsoleManager manager,
|
||||
List<U> choices,
|
||||
Map<U, T> choicesMap,
|
||||
String message) throws IOException {
|
||||
final List<Integer> chs = promptMultiChoice(manager, choices, message);
|
||||
public static <U, T> List<T> promptMultiChoice(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final List<U> choices,
|
||||
final Map<U, T> choicesMap,
|
||||
final String message) throws IOException {
|
||||
final List<Integer> chs = promptMultiChoice(manager, input, choices,
|
||||
message);
|
||||
final List<T> userChoices = new ArrayList<>();
|
||||
for (final Integer integer : chs) {
|
||||
userChoices.add(choicesMap.get(choices.get(integer.intValue())));
|
||||
@@ -361,42 +431,45 @@ public class CLIPrompter {
|
||||
return userChoices;
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt the user to select zero or more elements from a list.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param <U> the type of choices
|
||||
* @param choices the list of choices
|
||||
* @param message the prompting message
|
||||
* @return the indices of the choices
|
||||
* @throws IOException if the manager was closed */
|
||||
@SuppressWarnings("boxing")
|
||||
public static <U> List<Integer> promptMultiChoice(ConsoleManager manager,
|
||||
List<U> choices,
|
||||
String message) throws IOException {
|
||||
public static <U> List<Integer> promptMultiChoice(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final List<U> choices,
|
||||
final String message) throws IOException {
|
||||
manager.println(message);
|
||||
final int index = listChoices(manager, choices, null);
|
||||
String result = ""; //$NON-NLS-1$
|
||||
final Integer index = listChoices(manager, choices, null);
|
||||
String result;
|
||||
boolean keepOn = true;
|
||||
final List<Integer> chs = new ArrayList<>();
|
||||
while (keepOn) {
|
||||
keepOn = false;
|
||||
result = manager.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
result = input.prompt(CLIPrompterMessages.getString(PROMPT));
|
||||
final String[] vals = result
|
||||
.split(CLIPrompterMessages.getString(LIST_CHOICE_SEP));
|
||||
for (final String val : vals) {
|
||||
boolean added;
|
||||
try {
|
||||
added = addUserChoice(val, chs, index);
|
||||
added = addUserChoice(val, chs, index.intValue());
|
||||
} catch (final NumberFormatException e) {
|
||||
LOGGER.log(Level.FINER,
|
||||
"Unrecognized number. Prompting user again.", e); //$NON-NLS-1$
|
||||
keepOn = true;
|
||||
manager.println(CLIPrompterMessages
|
||||
.getString(PROMPTCHOICE_FORMATERR, 0, index));
|
||||
.getString(PROMPTCHOICE_FORMATERR, ZERO, index));
|
||||
listChoices(manager, choices, null);
|
||||
break;
|
||||
}
|
||||
if (!added) {
|
||||
manager.println(CLIPrompterMessages
|
||||
.getString(PROMPTCHOICE_OUTOFBOUNDS, 0, index));
|
||||
.getString(PROMPTCHOICE_OUTOFBOUNDS, ZERO, index));
|
||||
listChoices(manager, choices, null);
|
||||
keepOn = true;
|
||||
}
|
||||
@@ -405,45 +478,34 @@ public class CLIPrompter {
|
||||
return chs;
|
||||
}
|
||||
|
||||
/** @param val the string to parse
|
||||
* @param chs the list of integers
|
||||
* @param index the max index of choice
|
||||
* @return if the parsing was done correctly */
|
||||
private static boolean addUserChoice(String val, List<Integer> chs,
|
||||
int index) {
|
||||
if (val.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
final int r;
|
||||
r = Integer.parseInt(val);
|
||||
if (r >= 0 && r <= index) {
|
||||
chs.add(Integer.valueOf(r));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt the user to select zero or more elements from a list.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param input the input
|
||||
* @param <U> The choices labels type
|
||||
* @param <T> The real choices objects
|
||||
* @param choicesMap the map of label to actual objects
|
||||
* @param message the prompting message
|
||||
* @return the chosen objects
|
||||
* @throws IOException if the manager was closed */
|
||||
public static <U, T> List<T> promptMultiChoice(ConsoleManager manager,
|
||||
Map<U, T> choicesMap,
|
||||
String message) throws IOException {
|
||||
return promptMultiChoice(manager, new ArrayList<>(choicesMap.keySet()),
|
||||
choicesMap, message);
|
||||
public static <U, T> List<T> promptMultiChoice(final ConsoleOutput manager,
|
||||
final ConsoleInput input,
|
||||
final Map<U, T> choicesMap,
|
||||
final String message) throws IOException {
|
||||
return promptMultiChoice(manager, input,
|
||||
new ArrayList<>(choicesMap.keySet()), choicesMap, message);
|
||||
}
|
||||
|
||||
/** @param manager the manager
|
||||
/** Prompt the user for a non empty text.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param prompt the prompting message
|
||||
* @param reprompt the prompting message after empty input
|
||||
* @return the non empty input
|
||||
* @throws IOException if the manager was closed */
|
||||
public static String promptNonEmpty(ConsoleManager manager, String prompt,
|
||||
String reprompt) throws IOException {
|
||||
public static String promptNonEmpty(final ConsoleInput manager,
|
||||
final String prompt,
|
||||
final String reprompt) throws IOException {
|
||||
String res = manager.prompt(prompt);
|
||||
while (res.isEmpty()) {
|
||||
res = manager.prompt(reprompt);
|
||||
|
||||
@@ -44,37 +44,35 @@ import java.util.ResourceBundle;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/** <p>
|
||||
* Utility class for the messages of the CLIPrompter
|
||||
/** Utility class for the messages of the CLIPrompter.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class CLIPrompterMessages {
|
||||
/** The resource name */
|
||||
public final class CLIPrompterMessages {
|
||||
/** The resource name. */
|
||||
private static final String BUNDLE_NAME = "fr.bigeon.gclc.messages"; //$NON-NLS-1$
|
||||
|
||||
/** The resource */
|
||||
/** The resource. */
|
||||
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
|
||||
.getBundle(BUNDLE_NAME);
|
||||
|
||||
/** The logger */
|
||||
/** The logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(CLIPrompterMessages.class.getName());
|
||||
|
||||
/** Utility class */
|
||||
/** Utility class. */
|
||||
private CLIPrompterMessages() {
|
||||
// Utility constructor
|
||||
}
|
||||
|
||||
/** Return the formatted message corresponding to the given key
|
||||
/** Return the formatted message corresponding to the given key.
|
||||
*
|
||||
* @param key the message's key
|
||||
* @param args the arguments
|
||||
* @return the formatted message */
|
||||
public static String getString(String key, Object... args) {
|
||||
public static String getString(final String key, final Object... args) {
|
||||
try {
|
||||
return MessageFormat.format(RESOURCE_BUNDLE.getString(key), args);
|
||||
} catch (final MissingResourceException e) {
|
||||
LOGGER.log(Level.WARNING, "Unrecognized key: " + key, e); //$NON-NLS-1$
|
||||
LOGGER.warning("Unrecognized key: " + key); //$NON-NLS-1$
|
||||
LOGGER.log(Level.FINE, "Missing key in " + BUNDLE_NAME, e); //$NON-NLS-1$
|
||||
return '!' + key + '!';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleOutput;
|
||||
|
||||
/** An incomplete implematation used to forward messages from a piped console.
|
||||
* <p>
|
||||
@@ -51,16 +51,25 @@ import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
* @author Emmanuel Bigeon */
|
||||
public abstract class AOutputForwardRunnable implements Runnable {
|
||||
|
||||
/** The class logger */
|
||||
/** The class logger. */
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(AOutputForwardRunnable.class.getName());
|
||||
/** The default timeout (one tenth of second). */
|
||||
private static final long DEFAULT_TIMEOUT = 100;
|
||||
/** The manager. */
|
||||
private final PipedConsoleManager manager;
|
||||
/** The timeout */
|
||||
private final PipedConsoleOutput manager;
|
||||
/** The timeout. */
|
||||
private final long timeout;
|
||||
|
||||
/** Create a forwarding runnable.
|
||||
*
|
||||
* @param manager the manager */
|
||||
public AOutputForwardRunnable(final PipedConsoleOutput manager) {
|
||||
super();
|
||||
this.manager = manager;
|
||||
timeout = DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
/** Create a forward runnable with the given timeout.
|
||||
* <p>
|
||||
* Short timeout will be very responsive to the application actual messages,
|
||||
@@ -70,27 +79,31 @@ public abstract class AOutputForwardRunnable implements Runnable {
|
||||
* likely to depend on the application and the use of it.
|
||||
* <p>
|
||||
* If you do not know what timeout length to use, please use the
|
||||
* {@link #AOutputForwardRunnable(PipedConsoleManager)} constructor.
|
||||
*
|
||||
* {@link #AOutputForwardRunnable(PipedConsoleOutput)} constructor.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param timeout the timeout between message requests. */
|
||||
public AOutputForwardRunnable(PipedConsoleManager manager, long timeout) {
|
||||
public AOutputForwardRunnable(final PipedConsoleOutput manager,
|
||||
final long timeout) {
|
||||
super();
|
||||
this.manager = manager;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/** Create a forwarding runnable.
|
||||
*
|
||||
* @param manager the manager */
|
||||
public AOutputForwardRunnable(PipedConsoleManager manager) {
|
||||
super();
|
||||
this.manager = manager;
|
||||
timeout = DEFAULT_TIMEOUT;
|
||||
}
|
||||
/** Do forward the line.
|
||||
*
|
||||
* @param m the line to forward */
|
||||
protected abstract void forwardLine(String m);
|
||||
|
||||
/** Test if the runable is still running.
|
||||
*
|
||||
* @return if the thread should keep running */
|
||||
protected abstract boolean isRunning();
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Runnable#run() */
|
||||
@Override
|
||||
public void run() {
|
||||
public final void run() {
|
||||
try {
|
||||
while (isRunning()) {
|
||||
while (isRunning() && !manager.available()) {
|
||||
@@ -99,31 +112,25 @@ public abstract class AOutputForwardRunnable implements Runnable {
|
||||
if (!isRunning()) {
|
||||
return;
|
||||
}
|
||||
String m = manager.readNextLine();
|
||||
final String m = manager.readNextLine();
|
||||
forwardLine(m);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Unexpected problem in manager", //$NON-NLS-1$
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param m the line to forward */
|
||||
protected abstract void forwardLine(String m);
|
||||
|
||||
/** @return if the thread should keep running */
|
||||
protected abstract boolean isRunning();
|
||||
|
||||
/** a method to wait some time */
|
||||
protected void waitASec() {
|
||||
/** a method to wait some time. */
|
||||
protected final void waitASec() {
|
||||
try {
|
||||
synchronized (this) {
|
||||
wait(timeout);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Interrupted wait", //$NON-NLS-1$
|
||||
e);
|
||||
return;
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
50
gclc/src/main/java/fr/bigeon/gclc/tools/ConstantString.java
Normal file
50
gclc/src/main/java/fr/bigeon/gclc/tools/ConstantString.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package fr.bigeon.gclc.tools;
|
||||
|
||||
public class ConstantString implements StringProvider {
|
||||
private String string;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String apply() {
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
public ConstantString(String string) {
|
||||
this.string = string;
|
||||
}
|
||||
@@ -44,56 +44,61 @@ import java.util.List;
|
||||
/** A tool class for printing text in a console.
|
||||
*
|
||||
* @author Emmanuel BIGEON */
|
||||
public class PrintUtils {
|
||||
public final class PrintUtils {
|
||||
|
||||
/** The continuation dot string */
|
||||
/** The continuation dot string. */
|
||||
private static final String CONT_DOT = "..."; //$NON-NLS-1$
|
||||
/** The continuation dot string length */
|
||||
/** The continuation dot string length. */
|
||||
private static final int CONT_DOT_LENGTH = CONT_DOT.length();
|
||||
/** The empty string constant */
|
||||
/** The empty string constant. */
|
||||
private static final String EMPTY = ""; //$NON-NLS-1$
|
||||
|
||||
/** Utility class */
|
||||
/** Utility class. */
|
||||
private PrintUtils() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
/** @param text the text to print
|
||||
/** Print the text possibly cutting it if it goes over the authorized length
|
||||
* and adding a mark of continuation.
|
||||
*
|
||||
* @param text the text to print
|
||||
* @param nbCharacters the number of characters of the resulting text
|
||||
* @param indicateTooLong if an indication shell be given that the text
|
||||
* didn't fit
|
||||
* @return the text to print (will be of exactly nbCharacters). */
|
||||
public static String print(String text, int nbCharacters,
|
||||
boolean indicateTooLong) {
|
||||
String res = text;
|
||||
public static String print(final String text, final int nbCharacters,
|
||||
final boolean indicateTooLong) {
|
||||
StringBuilder res = new StringBuilder(text);
|
||||
if (res.length() > nbCharacters) {
|
||||
// Cut
|
||||
if (indicateTooLong) {
|
||||
// With suspension dots
|
||||
res = res.substring(0, nbCharacters - CONT_DOT_LENGTH) +
|
||||
CONT_DOT;
|
||||
res = res.replace(nbCharacters - CONT_DOT_LENGTH, text.length(),
|
||||
CONT_DOT);
|
||||
} else {
|
||||
res = res.substring(0, nbCharacters);
|
||||
res = res.replace(nbCharacters, text.length(), ""); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
while (res.length() < nbCharacters) {
|
||||
// Add trailing space
|
||||
res = res + ' ';
|
||||
res.append(' ');
|
||||
}
|
||||
return res;
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
/** @param description the element to wrap in lines
|
||||
/** Wrap the text, cutting at spaces.
|
||||
*
|
||||
* @param description the element to wrap in lines
|
||||
* @param i the length of the wrap
|
||||
* @return the list of resulting strings */
|
||||
public static List<String> wrap(String description, int i) {
|
||||
public static List<String> wrap(final String description, final int i) {
|
||||
final String[] originalLines = description
|
||||
.split(System.lineSeparator());
|
||||
final List<String> result = new ArrayList<>();
|
||||
for (final String string : originalLines) {
|
||||
String toCut = string;
|
||||
while (toCut.length() > i) {
|
||||
int index = toCut.lastIndexOf(" ", i); //$NON-NLS-1$
|
||||
int index = toCut.lastIndexOf(' ', i);
|
||||
if (index == -1) {
|
||||
result.add(toCut.substring(0, i));
|
||||
index = i - 1;
|
||||
|
||||
49
gclc/src/main/java/fr/bigeon/gclc/tools/StringProvider.java
Normal file
49
gclc/src/main/java/fr/bigeon/gclc/tools/StringProvider.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package fr.bigeon.gclc.tools;
|
||||
|
||||
/**
|
||||
* A string providing object.
|
||||
* <p>
|
||||
* Implementations of this interface will provide a string, this internal state
|
||||
* of the object may be so that successive calls to the apply method return
|
||||
* different results.
|
||||
*
|
||||
* @author Emmanuel
|
||||
*
|
||||
*/
|
||||
public interface StringProvider {
|
||||
String apply();
|
||||
}
|
||||
42
gclc/src/main/java/fr/bigeon/gclc/tools/package-info.java
Normal file
42
gclc/src/main/java/fr/bigeon/gclc/tools/package-info.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright Bigeon Emmanuel (2014)
|
||||
*
|
||||
* emmanuel@bigeon.fr
|
||||
*
|
||||
* This software is a computer program whose purpose is to
|
||||
* provide a generic framework for console applications.
|
||||
*
|
||||
* 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:fr.bigeon.gclc.tools.package-info.java
|
||||
* Created on: Nov 13, 2017
|
||||
*/
|
||||
/** Tool classes.
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
package fr.bigeon.gclc.tools;
|
||||
@@ -16,7 +16,8 @@ promptbool.choices.no1=N
|
||||
promptbool.choices.no2=no
|
||||
|
||||
promptchoice.outofbounds=Please choose something between {0} and {1}. The choices were:
|
||||
promptchoice.formaterr=The input seems to be something that is not an integer.\nPlease choose something between {0} and {1}. The choices were:
|
||||
promptchoice.formaterr=The input seems to be something that is not an integer.\
|
||||
Please choose something between {0} and {1}. The choices were:
|
||||
|
||||
promptlongtext.exit.defaultkey=\\q
|
||||
promptlongtext.exit.dispkey=\ (exit with a new line made of "{0}")
|
||||
|
||||
@@ -42,7 +42,8 @@ import java.io.IOException;
|
||||
|
||||
import fr.bigeon.gclc.command.ICommand;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleInput;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleOutput;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -54,14 +55,17 @@ import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
@SuppressWarnings("javadoc")
|
||||
public class CommandTestingApplication implements AutoCloseable {
|
||||
|
||||
private final ConsoleTestApplication application;
|
||||
private final ConsoleApplication application;
|
||||
private final Thread th;
|
||||
private final PipedConsoleManager manager;
|
||||
private final PipedConsoleOutput out;
|
||||
private final PipedConsoleInput in;
|
||||
|
||||
/** @throws IOException if the streams cannot be build */
|
||||
public CommandTestingApplication() throws IOException {
|
||||
manager = new PipedConsoleManager();
|
||||
application = new ConsoleTestApplication(manager);
|
||||
out = new PipedConsoleOutput();
|
||||
in = new PipedConsoleInput(null);
|
||||
application = new ConsoleApplication(out, in, "", "");
|
||||
new ConsoleTestApplication().attach(application);
|
||||
th = new Thread(new Runnable() {
|
||||
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@@ -74,49 +78,46 @@ public class CommandTestingApplication implements AutoCloseable {
|
||||
th.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
application.exit();
|
||||
manager.close();
|
||||
}
|
||||
|
||||
public void sendCommand(String cmd) throws IOException {
|
||||
manager.type(cmd);
|
||||
}
|
||||
|
||||
/** @param cmd the command
|
||||
* @return if the command was added
|
||||
* @throws InvalidCommandName if the command name is invalid
|
||||
* @see fr.bigeon.gclc.ConsoleApplication#add(fr.bigeon.gclc.command.ICommand) */
|
||||
public final boolean add(ICommand cmd) throws InvalidCommandName {
|
||||
public final boolean add(final ICommand cmd) throws InvalidCommandName {
|
||||
return application.add(cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
application.exit();
|
||||
out.close();
|
||||
in.close();
|
||||
}
|
||||
|
||||
/** @return the application */
|
||||
public ConsoleTestApplication getApplication() {
|
||||
public ConsoleApplication getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
/** @return the next written line, null if it is the prompt
|
||||
* @throws IOException if the reading failed */
|
||||
public String readNextLine() throws IOException {
|
||||
final String ret = out.readNextLine();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void sendCommand(final String cmd) throws IOException {
|
||||
in.type(cmd);
|
||||
}
|
||||
|
||||
/** @throws IOException if the writing failed */
|
||||
public void waitAndStop() throws IOException {
|
||||
manager.type(ConsoleTestApplication.EXIT);
|
||||
in.type(ConsoleTestApplication.EXIT);
|
||||
try {
|
||||
th.join();
|
||||
} catch (InterruptedException e) {
|
||||
} catch (final InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the next written line, null if it is the prompt
|
||||
* @throws IOException if the reading failed
|
||||
* @see fr.bigeon.gclc.manager.PipedConsoleManager#readNextLine() */
|
||||
public String readNextLine() throws IOException {
|
||||
String ret = manager.readNextLine();
|
||||
if (ret.equals(manager.getPrompt())) {
|
||||
return null;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,10 +41,16 @@ package fr.bigeon.gclc;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
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;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -54,9 +60,10 @@ import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.CommandRunExceptionType;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.i18n.Messages;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.SystemConsoleManager;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleInput;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleOutput;
|
||||
|
||||
/** Test class for ConsoleApplication
|
||||
*
|
||||
@@ -69,80 +76,99 @@ public class ConsoleApplicationTest {
|
||||
|
||||
/** Test the base of a console application */
|
||||
@Test
|
||||
public void test() {
|
||||
ConsoleTestApplication app = new ConsoleTestApplication(
|
||||
new SystemConsoleManager());
|
||||
app.exit();
|
||||
public void testConsoleApplication() {
|
||||
|
||||
try (PipedOutputStream pout = new PipedOutputStream();
|
||||
PipedInputStream pis = new PipedInputStream(pout);
|
||||
BufferedReader buf = new BufferedReader(
|
||||
new InputStreamReader(pis, StandardCharsets.UTF_8));
|
||||
PipedConsoleInput in = new PipedConsoleInput(
|
||||
new PrintStream(pout))) {
|
||||
final ConsoleApplication app = new ConsoleApplication(null, in,
|
||||
"", "");
|
||||
app.exit();
|
||||
} catch (final IOException e) {
|
||||
fail("System Console Manager failed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executionTest() {
|
||||
public void testExecution() throws IOException, InterruptedException,
|
||||
InvalidCommandName {
|
||||
try (CommandTestingApplication application = new CommandTestingApplication()) {
|
||||
|
||||
// remove welcome
|
||||
assertEquals(application.getApplication().getHeader(),
|
||||
assertEquals("Header should be preserved",
|
||||
application.getApplication().header,
|
||||
application.readNextLine());
|
||||
// Remove first prompt
|
||||
assertNull(application.readNextLine());
|
||||
application.sendCommand("");
|
||||
assertNull(application.readNextLine());
|
||||
application.sendCommand("test");
|
||||
assertEquals("Test command ran fine", application.readNextLine());
|
||||
assertNull(application.readNextLine());
|
||||
assertEquals("Test should run", "Test command ran fine",
|
||||
application.readNextLine());
|
||||
application.sendCommand("toto");
|
||||
assertEquals(
|
||||
assertEquals("Command fail should dispaly appropriate message",
|
||||
Messages.getString("ConsoleApplication.cmd.failed", "toto"),
|
||||
application.readNextLine());
|
||||
assertEquals(
|
||||
"Unrecognized comment should result in a specific message.",
|
||||
Messages.getString("CommandProvider.unrecognized", "toto"),
|
||||
application.readNextLine());
|
||||
assertNull(application.readNextLine());
|
||||
application.sendCommand("long");
|
||||
assertEquals("Waita minute", application.readNextLine());
|
||||
assertEquals("done!", application.readNextLine());
|
||||
assertEquals("Before wait should receive message", "Waita minute",
|
||||
application.readNextLine());
|
||||
assertEquals("Unexpected message", "done!",
|
||||
application.readNextLine());
|
||||
|
||||
CommandRequestListener crl = new CommandRequestListener() {
|
||||
final CommandRequestListener crl = new CommandRequestListener() {
|
||||
|
||||
@Override
|
||||
public void commandRequest(String command) {
|
||||
public void commandRequest(final String command) {
|
||||
//
|
||||
}
|
||||
};
|
||||
CommandRequestListener crl2 = new CommandRequestListener() {
|
||||
final CommandRequestListener crl2 = new CommandRequestListener() {
|
||||
|
||||
@Override
|
||||
public void commandRequest(String command) {
|
||||
public void commandRequest(final String command) {
|
||||
//
|
||||
}
|
||||
};
|
||||
application.getApplication().addListener(crl);
|
||||
application.getApplication().addListener(crl2);
|
||||
assertNull(application.readNextLine());
|
||||
application.sendCommand("test");
|
||||
assertEquals("Test command ran fine", application.readNextLine());
|
||||
assertEquals("Unexpected message", "Test command ran fine",
|
||||
application.readNextLine());
|
||||
application.getApplication().removeListener(crl2);
|
||||
application.getApplication().removeListener(crl);
|
||||
application.getApplication().removeListener(crl);
|
||||
|
||||
assertTrue(application.getApplication().isRunning());
|
||||
assertTrue("Unclosed application should be running",
|
||||
application.getApplication().isRunning());
|
||||
|
||||
assertNull(application.readNextLine());
|
||||
application.sendCommand("exit");
|
||||
assertEquals(application.getApplication().getFooter(),
|
||||
assertEquals("Footer should be preserved",
|
||||
application.getApplication().footer,
|
||||
application.readNextLine());
|
||||
assertFalse(application.getApplication().isRunning());
|
||||
} catch (IOException e1) {
|
||||
assertNull(e1);
|
||||
assertFalse("Stopped application should not be running",
|
||||
application.getApplication().isRunning());
|
||||
}
|
||||
|
||||
ConsoleApplication appli = null;
|
||||
try (PipedConsoleManager manager = new PipedConsoleManager()) {
|
||||
final ConsoleApplication app = new ConsoleApplication(manager, null,
|
||||
null);
|
||||
try (PipedConsoleOutput manager = new PipedConsoleOutput();
|
||||
PipedOutputStream pout = new PipedOutputStream();
|
||||
PipedInputStream pis = new PipedInputStream(pout);
|
||||
BufferedReader buf = new BufferedReader(
|
||||
new InputStreamReader(pis, StandardCharsets.UTF_8));
|
||||
PipedConsoleInput in = new PipedConsoleInput(
|
||||
new PrintStream(pout))) {
|
||||
final ConsoleApplication app = new ConsoleApplication(manager, in,
|
||||
null, null);
|
||||
appli = app;
|
||||
app.add(new ExitCommand("exit", app));
|
||||
|
||||
Thread th = new Thread(new Runnable() {
|
||||
final Thread th = new Thread(new Runnable() {
|
||||
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
@@ -150,70 +176,72 @@ public class ConsoleApplicationTest {
|
||||
app.start();
|
||||
}
|
||||
});
|
||||
|
||||
th.start();
|
||||
|
||||
manager.type("exit");
|
||||
in.type("exit");
|
||||
th.join();
|
||||
|
||||
} catch (IOException | InvalidCommandName | InterruptedException e) {
|
||||
assertNull(e);
|
||||
}
|
||||
|
||||
assertNotNull(appli);
|
||||
assertNotNull(
|
||||
"Application should still exist even if the console input and output are closed.",
|
||||
appli);
|
||||
appli.start();
|
||||
assertFalse(appli.isRunning());
|
||||
assertFalse(
|
||||
"Application should not start on closed console input and output",
|
||||
appli.isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interpretCommandTest() {
|
||||
try (PipedConsoleManager test = new PipedConsoleManager()) {
|
||||
ConsoleApplication appl = new ConsoleApplication(test, "", "");
|
||||
public void testInterpretCommand() throws InvalidCommandName, IOException {
|
||||
try (PipedConsoleInput test = new PipedConsoleInput(null);
|
||||
PipedConsoleOutput out = new PipedConsoleOutput()) {
|
||||
final ConsoleApplication appl = new ConsoleApplication(out, test,
|
||||
"", "");
|
||||
|
||||
appl.interpretCommand("invalid cmd \"due to misplaced\"quote");
|
||||
assertEquals("Command line cannot be parsed", test.readNextLine());
|
||||
assertEquals("Specific error message expected",
|
||||
"Command line cannot be parsed", out.readNextLine());
|
||||
|
||||
appl.interpretCommand("");
|
||||
|
||||
final String message = "message";
|
||||
try {
|
||||
appl.add(new ICommand() {
|
||||
appl.add(new ICommand() {
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
throw new CommandRunException(
|
||||
CommandRunExceptionType.USAGE, message, this);
|
||||
}
|
||||
/* (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 {
|
||||
throw new CommandRunException(CommandRunExceptionType.USAGE,
|
||||
message, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "fail";
|
||||
}
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "fail";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(ConsoleManager manager,
|
||||
String... args) throws IOException {
|
||||
manager.println(message);
|
||||
}
|
||||
@Override
|
||||
public void help(final ConsoleOutput manager,
|
||||
final String... args) throws IOException {
|
||||
manager.println(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
public String tip() {
|
||||
return "";
|
||||
}
|
||||
|
||||
});
|
||||
} catch (InvalidCommandName e) {
|
||||
assertNull(e);
|
||||
}
|
||||
});
|
||||
appl.interpretCommand("fail");
|
||||
assertEquals(
|
||||
assertEquals("Unexpected message",
|
||||
Messages.getString("ConsoleApplication.cmd.failed", "fail"),
|
||||
test.readNextLine());
|
||||
assertEquals(message, test.readNextLine());
|
||||
assertEquals(message, test.readNextLine());
|
||||
out.readNextLine());
|
||||
assertEquals("Unexpected message", message, out.readNextLine());
|
||||
assertEquals("Unexpected message", message, out.readNextLine());
|
||||
|
||||
} catch (IOException e) {
|
||||
assertNull(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,29 +39,47 @@ import java.io.IOException;
|
||||
import fr.bigeon.gclc.command.Command;
|
||||
import fr.bigeon.gclc.command.ExitCommand;
|
||||
import fr.bigeon.gclc.command.HelpExecutor;
|
||||
import fr.bigeon.gclc.command.ICommandProvider;
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
import fr.bigeon.gclc.manager.ConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
|
||||
/** A test-purpose application
|
||||
*
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class ConsoleTestApplication extends ConsoleApplication {
|
||||
public class ConsoleTestApplication implements ApplicationAttachement {
|
||||
|
||||
/** Exit command */
|
||||
public static final String EXIT = "exit"; //$NON-NLS-1$
|
||||
/** Two seconds in milliseconds */
|
||||
protected static final long TWO_SECONDS = 2000;
|
||||
|
||||
/** @param manager the manager */
|
||||
/***/
|
||||
@Override
|
||||
@SuppressWarnings("nls")
|
||||
public ConsoleTestApplication(final ConsoleManager manager) {
|
||||
super(manager, "Welcome to the test application. Type help or test.",
|
||||
"See you");
|
||||
public void attach(final ICommandProvider application) {
|
||||
try {
|
||||
add(new ExitCommand(EXIT, this));
|
||||
add(new HelpExecutor("help", manager, this.getRoot()));
|
||||
add(new Command("test") {
|
||||
application.add(new ExitCommand(EXIT, (ConsoleApplication) application));
|
||||
application.add(new HelpExecutor("help",
|
||||
((ConsoleApplication) application).root));
|
||||
application.add(new Command("test") {
|
||||
|
||||
/* (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 {
|
||||
try {
|
||||
out.println("Test command ran fine");
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException("manager closed", e,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
@@ -69,38 +87,31 @@ public class ConsoleTestApplication extends ConsoleApplication {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
try {
|
||||
manager.println("Test command ran fine");
|
||||
} catch (IOException e) {
|
||||
throw new CommandRunException("manager closed", e,
|
||||
this);
|
||||
}
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
add(new Command("long") {
|
||||
|
||||
application.add(new Command("long") {
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.ICommand#execute(fr.bigeon.gclc.
|
||||
* manager.ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput,
|
||||
* java.lang.String[]) */
|
||||
@Override
|
||||
public String tip() {
|
||||
return "A long execution command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
try {
|
||||
manager.println("Waita minute");
|
||||
out.println("Waita minute");
|
||||
Thread.sleep(TWO_SECONDS);
|
||||
manager.println("done!");
|
||||
} catch (IOException e) {
|
||||
out.println("done!");
|
||||
} catch (final IOException e) {
|
||||
throw new CommandRunException("manager closed", e,
|
||||
this);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (final InterruptedException e) {
|
||||
throw new CommandRunException("wait interrupted", e,
|
||||
this);
|
||||
}
|
||||
}
|
||||
});
|
||||
add(new Command("failingCmd") {
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
@@ -108,9 +119,31 @@ public class ConsoleTestApplication extends ConsoleApplication {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
application.add(new Command("failingCmd") {
|
||||
/* (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 {
|
||||
throw new CommandRunException("Failing command", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "A long execution command";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (final InvalidCommandName e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
*/
|
||||
package fr.bigeon.gclc;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@@ -53,103 +54,74 @@ import fr.bigeon.gclc.exception.CommandParsingException;
|
||||
@SuppressWarnings({"nls", "static-method"})
|
||||
public class GCLCConstantsTest {
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.GCLCConstants#splitCommand(java.lang.String)}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.GCLCConstants#splitCommand(java.lang.String)}.
|
||||
*
|
||||
* @throws CommandParsingException if an error occured */
|
||||
@Test
|
||||
public void testSplitCommand() {
|
||||
public void testSplitCommand() throws CommandParsingException {
|
||||
List<String> res;
|
||||
try {
|
||||
res = GCLCConstants.splitCommand("aCommand");
|
||||
} catch (CommandParsingException e) {
|
||||
fail("Unable to parse simple command " + e.getLocalizedMessage()); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
assertTrue(res.size() == 1);
|
||||
assertTrue(res.get(0).equals("aCommand"));
|
||||
res = GCLCConstants.splitCommand("aCommand");
|
||||
assertTrue("single word command should have one element",
|
||||
res.size() == 1);
|
||||
assertTrue("Command should be preserved",
|
||||
res.get(0).equals("aCommand"));
|
||||
|
||||
res = GCLCConstants.splitCommand("aCommand with some arguments");
|
||||
assertEquals("Command size", 4, res.size());
|
||||
assertEquals("Elements should be preserved", "aCommand", res.get(0));
|
||||
assertTrue("Elements should be preserved", res.get(1).equals("with"));
|
||||
assertTrue("Elements should be preserved", res.get(2).equals("some"));
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(3).equals("arguments"));
|
||||
res = GCLCConstants.splitCommand("aCommand with some arguments");
|
||||
assertEquals("Command size", 4, res.size());
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(0).equals("aCommand"));
|
||||
assertTrue("Elements should be preserved", res.get(1).equals("with"));
|
||||
assertTrue("Elements should be preserved", res.get(2).equals("some"));
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(3).equals("arguments"));
|
||||
res = GCLCConstants.splitCommand("aCommand \"with some\" arguments");
|
||||
assertEquals("Command size", 3, res.size());
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(0).equals("aCommand"));
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(1).equals("with some"));
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(2).equals("arguments"));
|
||||
res = GCLCConstants.splitCommand("aCommand with\\ some arguments");
|
||||
assertEquals("Command size", 3, res.size());
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(0).equals("aCommand"));
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(1).equals("with some"));
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(2).equals("arguments"));
|
||||
res = GCLCConstants.splitCommand("aCommand wi\\\"th some arguments");
|
||||
assertEquals("Command size", 4, res.size());
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(0).equals("aCommand"));
|
||||
assertTrue("Elements should be preserved", res.get(1).equals("wi\"th"));
|
||||
assertTrue("Elements should be preserved", res.get(2).equals("some"));
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(3).equals("arguments"));
|
||||
|
||||
res = GCLCConstants.splitCommand("aCommand with \"some arguments\"");
|
||||
assertEquals("Command size", 3, res.size());
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(0).equals("aCommand"));
|
||||
assertTrue("Elements should be preserved", res.get(1).equals("with"));
|
||||
assertTrue("Elements should be preserved",
|
||||
res.get(2).equals("some arguments"));
|
||||
|
||||
try {
|
||||
res = GCLCConstants.splitCommand("aCommand with some arguments");
|
||||
} catch (CommandParsingException e) {
|
||||
fail("Unable to parse command with arguments " + //$NON-NLS-1$
|
||||
e.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
assertTrue(res.size() == 4);
|
||||
assertTrue(res.get(0).equals("aCommand"));
|
||||
assertTrue(res.get(1).equals("with"));
|
||||
assertTrue(res.get(2).equals("some"));
|
||||
assertTrue(res.get(3).equals("arguments"));
|
||||
try {
|
||||
res = GCLCConstants.splitCommand("aCommand with some arguments");
|
||||
} catch (CommandParsingException e) {
|
||||
fail("Unable to parse command with arguments and double whitspaces " + //$NON-NLS-1$
|
||||
e.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
assertTrue(res.size() == 4);
|
||||
assertTrue(res.get(0).equals("aCommand"));
|
||||
assertTrue(res.get(1).equals("with"));
|
||||
assertTrue(res.get(2).equals("some"));
|
||||
assertTrue(res.get(3).equals("arguments"));
|
||||
try {
|
||||
res = GCLCConstants
|
||||
.splitCommand("aCommand \"with some\" arguments");
|
||||
} catch (CommandParsingException e) {
|
||||
fail("Unable to parse command with string argument " + //$NON-NLS-1$
|
||||
e.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
assertTrue(res.size() == 3);
|
||||
assertTrue(res.get(0).equals("aCommand"));
|
||||
assertTrue(res.get(1).equals("with some"));
|
||||
assertTrue(res.get(2).equals("arguments"));
|
||||
try {
|
||||
res = GCLCConstants.splitCommand("aCommand with\\ some arguments");
|
||||
} catch (CommandParsingException e) {
|
||||
fail("Unable to parse command with arguments with escaped whitspaces " + //$NON-NLS-1$
|
||||
e.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
assertTrue(res.size() == 3);
|
||||
assertTrue(res.get(0).equals("aCommand"));
|
||||
assertTrue(res.get(1).equals("with some"));
|
||||
assertTrue(res.get(2).equals("arguments"));
|
||||
try {
|
||||
res = GCLCConstants
|
||||
.splitCommand("aCommand wi\\\"th some arguments");
|
||||
} catch (CommandParsingException e) {
|
||||
fail("Unable to parse command with string argument " + //$NON-NLS-1$
|
||||
e.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
assertTrue(res.size() == 4);
|
||||
assertTrue(res.get(0).equals("aCommand"));
|
||||
assertTrue(res.get(1).equals("wi\"th"));
|
||||
assertTrue(res.get(2).equals("some"));
|
||||
assertTrue(res.get(3).equals("arguments"));
|
||||
|
||||
try {
|
||||
res = GCLCConstants
|
||||
.splitCommand("aCommand with \"some arguments\"");
|
||||
} catch (CommandParsingException e) {
|
||||
fail("Unable to parse command ending with string argument " + //$NON-NLS-1$
|
||||
e.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
assertTrue(res.size() == 3);
|
||||
assertTrue(res.get(0).equals("aCommand"));
|
||||
assertTrue(res.get(1).equals("with"));
|
||||
assertTrue(res.get(2).equals("some arguments"));
|
||||
|
||||
// Wrong lines?
|
||||
try {
|
||||
// Wrong lines?
|
||||
res = GCLCConstants
|
||||
.splitCommand("aCommand with \"some ar\"guments");
|
||||
fail("Parsing argument with string cut");
|
||||
} catch (CommandParsingException e) {
|
||||
// OK
|
||||
assertTrue(e.getLocalizedMessage(), true);
|
||||
fail("Misplaced quotes should fail");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,56 +42,69 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandParsingException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* TODO
|
||||
*
|
||||
* @author Emmanuel Bigeon
|
||||
*
|
||||
*/
|
||||
* @author Emmanuel Bigeon */
|
||||
@SuppressWarnings({"static-method", "nls"})
|
||||
public class CommandParametersTest {
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.CommandParameters#CommandParameters(java.util.Set, java.util.Set, boolean)}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.CommandParameters#CommandParameters(java.util.Set, java.util.Set, boolean)}.
|
||||
*
|
||||
* @throws CommandParsingException if an unexpected exception is thrown */
|
||||
@Test
|
||||
public final void testCommandParameters(){
|
||||
Set<String> strings = new HashSet<>();
|
||||
Set<String> bools = new HashSet<>();
|
||||
public final void testCommandParameters() throws CommandParsingException {
|
||||
final Set<String> strings = new HashSet<>();
|
||||
final Set<String> bools = new HashSet<>();
|
||||
CommandParameters parameters = new CommandParameters(bools, strings,
|
||||
true);
|
||||
|
||||
assertFalse(parameters.parseArgs("-ungivenFlag"));
|
||||
try {
|
||||
parameters.parseArgs("-ungivenFlag");
|
||||
fail("parse of unknown in strict should fail");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
parameters = new CommandParameters(bools, strings, false);
|
||||
|
||||
assertTrue(parameters.parseArgs("-ungivenFlag"));
|
||||
parameters.parseArgs("-ungivenFlag");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.CommandParameters#get(java.lang.String)}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.CommandParameters#get(java.lang.String)}.
|
||||
*
|
||||
* @throws CommandParsingException if an exception occired */
|
||||
@Test
|
||||
public final void testGet(){
|
||||
Set<String> strings = new HashSet<>();
|
||||
Set<String> bools = new HashSet<>();
|
||||
public final void testGet() throws CommandParsingException {
|
||||
final Set<String> strings = new HashSet<>();
|
||||
final Set<String> bools = new HashSet<>();
|
||||
|
||||
bools.add("boolFlag");
|
||||
strings.add("str");
|
||||
|
||||
CommandParameters parameters = new CommandParameters(bools, strings,
|
||||
true);
|
||||
final CommandParameters parameters = new CommandParameters(bools,
|
||||
strings, true);
|
||||
|
||||
assertNull(parameters.get("ungiven"));
|
||||
assertNull(parameters.get("str"));
|
||||
|
||||
parameters.parseArgs("-ungiven", "val");
|
||||
try {
|
||||
parameters.parseArgs("-ungiven", "val");
|
||||
fail("Missing parameter should fail for strict element");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
assertNull(parameters.get("ungiven"));
|
||||
assertNull(parameters.get("str"));
|
||||
|
||||
@@ -99,18 +112,22 @@ public class CommandParametersTest {
|
||||
assertNull(parameters.get("ungiven"));
|
||||
assertEquals("val", parameters.get("str"));
|
||||
|
||||
parameters.parseArgs("-ungiven");
|
||||
try {
|
||||
parameters.parseArgs("-ungiven");
|
||||
fail("Invalid argument type parsing should fail");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
assertNull(parameters.get("ungiven"));
|
||||
assertEquals("val", parameters.get("str"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.CommandParameters#getAdditionals()}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.CommandParameters#getAdditionals()}. */
|
||||
@Test
|
||||
public final void testGetAdditionals(){
|
||||
Set<String> strings = new HashSet<>();
|
||||
Set<String> bools = new HashSet<>();
|
||||
public final void testGetAdditionals() throws CommandParsingException {
|
||||
final Set<String> strings = new HashSet<>();
|
||||
final Set<String> bools = new HashSet<>();
|
||||
|
||||
bools.add("boolFlag");
|
||||
strings.add("str");
|
||||
@@ -121,7 +138,12 @@ public class CommandParametersTest {
|
||||
parameters.parseArgs("-boolFlag");
|
||||
assertTrue(parameters.getAdditionals().isEmpty());
|
||||
|
||||
parameters.parseArgs("-ungiven");
|
||||
try {
|
||||
parameters.parseArgs("-ungiven");
|
||||
fail("Should fail");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
assertTrue(parameters.getAdditionals().isEmpty());
|
||||
|
||||
parameters = new CommandParameters(bools, strings, false);
|
||||
@@ -129,18 +151,19 @@ public class CommandParametersTest {
|
||||
parameters.parseArgs("-boolFlag");
|
||||
assertTrue(parameters.getAdditionals().isEmpty());
|
||||
|
||||
parameters.parseArgs("-ungiven");
|
||||
parameters.parseArgs("ungiven");
|
||||
assertTrue(parameters.getAdditionals().contains("ungiven"));
|
||||
assertEquals(1, parameters.getAdditionals().size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.CommandParameters#getBool(java.lang.String)}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.CommandParameters#getBool(java.lang.String)}.
|
||||
*
|
||||
* @throws CommandParsingException if a command parsing failed */
|
||||
@Test
|
||||
public final void testGetBool(){
|
||||
Set<String> strings = new HashSet<>();
|
||||
Set<String> bools = new HashSet<>();
|
||||
public final void testGetBool() throws CommandParsingException {
|
||||
final Set<String> strings = new HashSet<>();
|
||||
final Set<String> bools = new HashSet<>();
|
||||
|
||||
bools.add("boolFlag");
|
||||
strings.add("str");
|
||||
@@ -155,7 +178,12 @@ public class CommandParametersTest {
|
||||
assertTrue(parameters.getBool("boolFlag"));
|
||||
assertFalse(parameters.getBool("ungiven"));
|
||||
|
||||
parameters.parseArgs("-ungiven");
|
||||
try {
|
||||
parameters.parseArgs("-ungiven");
|
||||
fail("unknown parameter should fail");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
assertFalse(parameters.getBool("ungiven"));
|
||||
assertTrue(parameters.getBool("boolFlag"));
|
||||
|
||||
@@ -164,44 +192,61 @@ public class CommandParametersTest {
|
||||
assertFalse(parameters.getBool("ungiven"));
|
||||
assertFalse(parameters.getBool("boolFlag"));
|
||||
|
||||
parameters.parseArgs("-boolFlag");
|
||||
try {
|
||||
parameters.parseArgs("-boolFlag");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
assertTrue(parameters.getBool("boolFlag"));
|
||||
assertFalse(parameters.getBool("ungiven"));
|
||||
|
||||
parameters.parseArgs("-ungiven");
|
||||
try {
|
||||
parameters.parseArgs("-ungiven");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
assertFalse(parameters.getBool("ungiven"));
|
||||
assertTrue(parameters.getBool("boolFlag"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.CommandParameters#parseArgs(java.lang.String[])}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.CommandParameters#parseArgs(java.lang.String[])}.
|
||||
*
|
||||
* @throws CommandParsingException if a command parsing failed */
|
||||
@Test
|
||||
public final void testParseArgs(){
|
||||
Set<String> strings = new HashSet<>();
|
||||
Set<String> bools = new HashSet<>();
|
||||
public final void testParseArgs() throws CommandParsingException {
|
||||
final Set<String> strings = new HashSet<>();
|
||||
final Set<String> bools = new HashSet<>();
|
||||
|
||||
bools.add("boolFlag");
|
||||
strings.add("str");
|
||||
|
||||
CommandParameters parameters = new CommandParameters(bools, strings,
|
||||
true);
|
||||
|
||||
assertFalse(parameters.parseArgs("-ungivenFlag"));
|
||||
assertFalse(parameters.parseArgs("-str"));
|
||||
assertTrue(parameters.parseArgs("-boolFlag"));
|
||||
assertTrue(parameters.parseArgs("-str", "-boolFlag"));
|
||||
assertTrue(parameters.parseArgs("-boolFlag", "-str", "val"));
|
||||
final CommandParameters parameters = new CommandParameters(bools,
|
||||
strings, true);
|
||||
|
||||
try {
|
||||
parameters.parseArgs("-ungivenFlag");
|
||||
fail("Strict should fail with flag");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
parameters.parseArgs("-str");
|
||||
fail("String argument without second element should fail");
|
||||
} catch (final CommandParsingException e) {
|
||||
// ok
|
||||
}
|
||||
parameters.parseArgs("-boolFlag");
|
||||
parameters.parseArgs("-str", "-boolFlag");
|
||||
parameters.parseArgs("-boolFlag", "-str", "val");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.CommandParameters#set(java.lang.String, boolean)}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.CommandParameters#set(java.lang.String, boolean)}. */
|
||||
@Test
|
||||
public final void testSetStringBoolean(){
|
||||
Set<String> strings = new HashSet<>();
|
||||
Set<String> bools = new HashSet<>();
|
||||
public final void testSetStringBoolean() {
|
||||
final Set<String> strings = new HashSet<>();
|
||||
final Set<String> bools = new HashSet<>();
|
||||
|
||||
bools.add("boolFlag");
|
||||
strings.add("str");
|
||||
@@ -234,13 +279,12 @@ public class CommandParametersTest {
|
||||
assertTrue(parameters.getBool("boolFlag"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.CommandParameters#set(java.lang.String, java.lang.String)}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.CommandParameters#set(java.lang.String, java.lang.String)}. */
|
||||
@Test
|
||||
public final void testSetStringString(){
|
||||
Set<String> strings = new HashSet<>();
|
||||
Set<String> bools = new HashSet<>();
|
||||
public final void testSetStringString() {
|
||||
final Set<String> strings = new HashSet<>();
|
||||
final Set<String> bools = new HashSet<>();
|
||||
|
||||
bools.add("boolFlag");
|
||||
strings.add("str");
|
||||
|
||||
@@ -38,68 +38,61 @@
|
||||
*/
|
||||
package fr.bigeon.gclc.command;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import fr.bigeon.gclc.exception.InvalidCommandName;
|
||||
|
||||
/** <p>
|
||||
/**
|
||||
* <p>
|
||||
* TODO
|
||||
*
|
||||
* @author Emmanuel Bigeon */
|
||||
public class CommandProviderTest {
|
||||
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.CommandProvider#add(fr.bigeon.gclc.command.ICommand)}. */
|
||||
* {@link fr.bigeon.gclc.command.CommandProvider#add(fr.bigeon.gclc.command.ICommand)}.
|
||||
*
|
||||
* @throws InvalidCommandName if the test failed */
|
||||
@Test
|
||||
public final void testAdd() {
|
||||
CommandProvider provider = new CommandProvider();
|
||||
public final void testAdd() throws InvalidCommandName {
|
||||
final CommandProvider provider = new CommandProvider();
|
||||
try {
|
||||
provider.add(new MockCommand(null));
|
||||
fail("null name for command should be rejected");
|
||||
} catch (InvalidCommandName e) {
|
||||
assertNotNull(e);
|
||||
} catch (final InvalidCommandName e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
provider.add(new MockCommand(""));
|
||||
fail("null name for command should be rejected");
|
||||
} catch (InvalidCommandName e) {
|
||||
assertNotNull(e);
|
||||
} catch (final InvalidCommandName e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
provider.add(new MockCommand("-name"));
|
||||
fail("name with minus as starting character for command should be rejected");
|
||||
} catch (InvalidCommandName e) {
|
||||
assertNotNull(e);
|
||||
} catch (final InvalidCommandName e) {
|
||||
// ok
|
||||
}
|
||||
try {
|
||||
provider.add(new MockCommand("name command"));
|
||||
fail("name with space for command should be rejected");
|
||||
} catch (InvalidCommandName e) {
|
||||
assertNotNull(e);
|
||||
}
|
||||
ICommand mock = new MockCommand("name");
|
||||
try {
|
||||
provider.add(mock);
|
||||
} catch (InvalidCommandName e) {
|
||||
assertNull(e);
|
||||
} catch (final InvalidCommandName e) {
|
||||
// ok
|
||||
}
|
||||
final ICommand mock = new MockCommand("name");
|
||||
provider.add(mock);
|
||||
|
||||
try {
|
||||
provider.add(new MockCommand(mock.getCommandName()));
|
||||
fail("already existing command name should be rejected");
|
||||
} catch (InvalidCommandName e) {
|
||||
assertNotNull(e);
|
||||
} catch (final InvalidCommandName e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
provider.add(mock);
|
||||
} catch (InvalidCommandName e) {
|
||||
assertNotNull(e);
|
||||
}
|
||||
provider.add(mock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,14 +38,18 @@
|
||||
*/
|
||||
package fr.bigeon.gclc.command;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import fr.bigeon.gclc.exception.CommandRunException;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleOutput;
|
||||
|
||||
/** <p>
|
||||
* TODO
|
||||
@@ -54,44 +58,82 @@ import fr.bigeon.gclc.manager.PipedConsoleManager;
|
||||
public class CommandTest {
|
||||
|
||||
@Test
|
||||
public final void test() {
|
||||
try (PipedConsoleManager test = new PipedConsoleManager()) {
|
||||
public final void testCommand() throws IOException {
|
||||
try (PipedConsoleOutput test = new PipedConsoleOutput()) {
|
||||
Command cmd;
|
||||
cmd = new Command("name") {
|
||||
|
||||
@Override
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
//
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
|
||||
// name
|
||||
assertEquals("First line should be command name", "name",
|
||||
test.readNextLine());
|
||||
// tip
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// Usage
|
||||
test.readNextLine();
|
||||
// details
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
assertFalse("Only 6 line should be printed", test.available());
|
||||
|
||||
cmd = new Command("name") {
|
||||
|
||||
@Override
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
//
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
// name
|
||||
assertEquals("First line should be command name", "name",
|
||||
test.readNextLine());
|
||||
// tip
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// Usage
|
||||
test.readNextLine();
|
||||
// details
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
assertFalse("Only 6 line should be printed", test.available());
|
||||
|
||||
cmd = new Command("name") {
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "tip";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#brief() */
|
||||
@Override
|
||||
@@ -100,19 +142,40 @@ public class CommandTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
|
||||
cmd = new Command("name") {
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "tip";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
// name
|
||||
assertEquals("First line should be command name", "name",
|
||||
test.readNextLine());
|
||||
// tip
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// Usage
|
||||
test.readNextLine();
|
||||
// details
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
assertFalse("Only 6 line should be printed", test.available());
|
||||
|
||||
cmd = new Command("name") {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see fr.bigeon.gclc.command.Command#brief() */
|
||||
@Override
|
||||
@@ -121,14 +184,47 @@ public class CommandTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "tip";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
// name
|
||||
assertEquals("First line should be command name", "name",
|
||||
test.readNextLine());
|
||||
// tip
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// Usage
|
||||
test.readNextLine();
|
||||
// details
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
assertFalse("Only 6 line should be printed", test.available());
|
||||
|
||||
cmd = new Command("name") {
|
||||
|
||||
@Override
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "tip";
|
||||
@@ -140,16 +236,32 @@ public class CommandTest {
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
// name
|
||||
assertEquals("First line should be command name", "name",
|
||||
test.readNextLine());
|
||||
// tip
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// Usage
|
||||
test.readNextLine();
|
||||
// details
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
assertFalse("Only 6 line should be printed", test.available());
|
||||
|
||||
cmd = new Command("name") {
|
||||
|
||||
@Override
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "tip";
|
||||
@@ -161,15 +273,35 @@ public class CommandTest {
|
||||
protected String usageDetail() {
|
||||
return "details";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
|
||||
// name
|
||||
assertEquals("First line should be command name", "name",
|
||||
test.readNextLine());
|
||||
// tip
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// Usage
|
||||
test.readNextLine();
|
||||
// pattern
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// details
|
||||
assertEquals("Unexpected detail", "details", test.readNextLine());
|
||||
assertFalse("Only 6 line should be printed", test.available());
|
||||
|
||||
cmd = new Command("name") {
|
||||
|
||||
@Override
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "tip";
|
||||
@@ -181,16 +313,34 @@ public class CommandTest {
|
||||
protected String usageDetail() {
|
||||
return "details" + System.lineSeparator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
// name
|
||||
assertEquals("First line should be command name", "name",
|
||||
test.readNextLine());
|
||||
// tip
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// Usage
|
||||
test.readNextLine();
|
||||
// pattern
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// details
|
||||
assertEquals("Unexpected detail", "details", test.readNextLine());
|
||||
assertFalse("Only 6 line should be printed", test.available());
|
||||
|
||||
cmd = new Command("name") {
|
||||
|
||||
@Override
|
||||
public void execute(final ConsoleOutput out,
|
||||
final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "tip";
|
||||
@@ -202,16 +352,25 @@ public class CommandTest {
|
||||
protected String usageDetail() {
|
||||
return "\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
};
|
||||
cmd.help(test);
|
||||
// name
|
||||
assertEquals("First line should be command name", "name",
|
||||
test.readNextLine());
|
||||
// tip
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
// Usage
|
||||
test.readNextLine();
|
||||
// pattern
|
||||
test.readNextLine();
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
//
|
||||
assertTrue("Section line is empty", test.readNextLine().isEmpty());
|
||||
assertFalse("Only 6 line should be printed", test.available());
|
||||
|
||||
} catch (IOException e) {
|
||||
assertNull(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,98 +39,79 @@
|
||||
package fr.bigeon.gclc.command;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
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.PipedConsoleManager;
|
||||
import fr.bigeon.gclc.manager.ConsoleInput;
|
||||
import fr.bigeon.gclc.manager.ConsoleOutput;
|
||||
import fr.bigeon.gclc.manager.PipedConsoleOutput;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* TODO
|
||||
*
|
||||
* @author Emmanuel Bigeon
|
||||
*
|
||||
*/
|
||||
* @author Emmanuel Bigeon */
|
||||
public class HelpExecutorTest {
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.HelpExecutor#HelpExecutor(java.lang.String, fr.bigeon.gclc.manager.ConsoleManager, fr.bigeon.gclc.command.ICommand)}.
|
||||
*/
|
||||
/** Test method for
|
||||
* {@link fr.bigeon.gclc.command.HelpExecutor#execute(ConsoleOutput, ConsoleInput, String...)}.
|
||||
*
|
||||
* @throws CommandRunException if the test failed
|
||||
* @throws IOException if the test failed */
|
||||
@Test
|
||||
public final void testHelpExecutor(){
|
||||
HelpExecutor help;
|
||||
try {
|
||||
help = new HelpExecutor("?", null, new MockCommand("mock"));
|
||||
fail("help is an interactive command, should need console manager");
|
||||
} catch (Exception e) {
|
||||
assertNotNull(e);
|
||||
}
|
||||
try (PipedConsoleManager test = new PipedConsoleManager()) {
|
||||
help = new HelpExecutor("?", test, new MockCommand("mock"));
|
||||
} catch (Exception e) {
|
||||
assertNull(e);
|
||||
}
|
||||
}
|
||||
public final void testExecute() throws CommandRunException, IOException {
|
||||
final PipedConsoleOutput test = new PipedConsoleOutput();
|
||||
final HelpExecutor help = new HelpExecutor("?", new Command("mock") {
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.HelpExecutor#execute(java.lang.String[])}.
|
||||
*/
|
||||
@Test
|
||||
public final void testExecute(){
|
||||
try {
|
||||
PipedConsoleManager test = new PipedConsoleManager();
|
||||
HelpExecutor help = new HelpExecutor("?", test,
|
||||
new Command("mock") {
|
||||
|
||||
@Override
|
||||
public void execute(String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "";
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
help.execute();
|
||||
test.close();
|
||||
|
||||
try {
|
||||
help.execute();
|
||||
fail("manager closed shall provoke failure of help command execution");
|
||||
} catch (Exception e) {
|
||||
assertNotNull(e);
|
||||
@Override
|
||||
public void execute(final ConsoleOutput out, final ConsoleInput in,
|
||||
final String... args) throws CommandRunException {
|
||||
//
|
||||
}
|
||||
} catch (Exception e) {
|
||||
assertNull(e);
|
||||
|
||||
@Override
|
||||
public String tip() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String usageDetail() {
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
help.execute(test, null);
|
||||
test.close();
|
||||
|
||||
try {
|
||||
help.execute(test, null);
|
||||
fail("manager closed shall provoke failure of help command execution");
|
||||
} catch (final Exception e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link fr.bigeon.gclc.command.HelpExecutor#tip()}.
|
||||
*/
|
||||
/** Test method for {@link fr.bigeon.gclc.command.HelpExecutor#tip()}.
|
||||
*
|
||||
* @throws IOException if the test failed */
|
||||
@Test
|
||||
public final void testTip(){
|
||||
try (PipedConsoleManager test = new PipedConsoleManager()) {
|
||||
HelpExecutor help = new HelpExecutor("?", test,
|
||||
public final void testTip() throws IOException {
|
||||
try (PipedConsoleOutput test = new PipedConsoleOutput()) {
|
||||
final HelpExecutor help = new HelpExecutor("?",
|
||||
new MockCommand("mock"));
|
||||
help.tip();
|
||||
assertNotNull("Tip should be provided", help.tip());
|
||||
help.help(test);
|
||||
} catch (Exception e) {
|
||||
assertNull(e);
|
||||
}
|
||||
try (PipedConsoleManager test = new PipedConsoleManager()) {
|
||||
HelpExecutor help = new HelpExecutor("?", test,
|
||||
try (PipedConsoleOutput test = new PipedConsoleOutput()) {
|
||||
final HelpExecutor help = new HelpExecutor("?",
|
||||
new SubedCommand("sub", new MockCommand("mock")));
|
||||
help.tip();
|
||||
help.help(test);
|
||||
} catch (Exception e) {
|
||||
assertNull(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user