From 00c0e86d720b359192a73c64613ec65977865d0a Mon Sep 17 00:00:00 2001 From: Emmanuel Bigeon Date: Thu, 10 May 2018 11:29:38 -0400 Subject: [PATCH] Fork process Signed-off-by: Emmanuel Bigeon --- .../gclc/process/CommandForeground.java | 139 ++++++++++++ .../fr/bigeon/gclc/process/CommandFork.java | 99 +++----- .../bigeon/gclc/process/ForkCommandTask.java | 73 ++++++ .../java/fr/bigeon/gclc/process/ForkTask.java | 110 +++++---- .../gclc/process/InterruptionListener.java | 2 +- .../gclc/process/ProcessAttachement.java | 64 ++++++ .../fr/bigeon/gclc/process/ProcessClear.java | 87 ++++++++ .../fr/bigeon/gclc/process/ProcessKill.java | 2 +- .../fr/bigeon/gclc/process/ProcessList.java | 2 +- .../gclc/process/ScreenAttachement.java | 65 ++++++ .../java/fr/bigeon/gclc/process/Task.java | 2 +- .../java/fr/bigeon/gclc/process/TaskPool.java | 66 ++++-- .../fr/bigeon/gclc/process/TaskSpawner.java | 18 +- .../process/io/ConnectingConsoleInput.java | 211 ++++++++++++++++++ .../process/io/ConnectingConsoleOutput.java | 146 ++++++++++++ 15 files changed, 955 insertions(+), 131 deletions(-) create mode 100644 gclc-process/src/main/java/fr/bigeon/gclc/process/CommandForeground.java create mode 100644 gclc-process/src/main/java/fr/bigeon/gclc/process/ForkCommandTask.java create mode 100644 gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessAttachement.java create mode 100644 gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessClear.java create mode 100644 gclc-process/src/main/java/fr/bigeon/gclc/process/ScreenAttachement.java create mode 100644 gclc-process/src/main/java/fr/bigeon/gclc/process/io/ConnectingConsoleInput.java create mode 100644 gclc-process/src/main/java/fr/bigeon/gclc/process/io/ConnectingConsoleOutput.java diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/CommandForeground.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/CommandForeground.java new file mode 100644 index 0000000..2271845 --- /dev/null +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/CommandForeground.java @@ -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. + *

+ * Several things are to be considered before adding this command to an + * application: + *

+ * + * @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 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) ] ([-delai ])"; + } +} diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/CommandFork.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/CommandFork.java index 6c051ad..a2b7c44 100644 --- a/gclc-process/src/main/java/fr/bigeon/gclc/process/CommandFork.java +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/CommandFork.java @@ -1,6 +1,6 @@ /* * process, Distribution repositories and basic setup for Emmanuel Bigeon projects - * Copyright (C) 2014-2017 E. Bigeon + * Copyright (C) 2014-2018 E. Bigeon * mailto:emmanuel@bigeon.fr * * This software is governed by the CeCILL license under French law and @@ -35,17 +35,12 @@ */ package fr.bigeon.gclc.process; -import java.io.IOException; import java.util.Arrays; -import java.util.List; -import fr.bigeon.gclc.command.CommandParameters; +import fr.bigeon.gclc.command.Command; import fr.bigeon.gclc.command.ICommand; import fr.bigeon.gclc.command.ICommandProvider; -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; @@ -56,72 +51,46 @@ import fr.bigeon.gclc.manager.ConsoleOutput; *
    *
  • The commands will be able to run in parallel. *
  • The managing of the commands will be handled through piped systems and - * buffered in so switching to a forled command will actually print again all + * buffered in so switching to a forked command will actually print again all * the command history to the output console. *
* * @author Emmanuel Bigeon */ -public class CommandFork extends ParametrizedCommand { +public class CommandFork extends Command { - private final TaskPool pool = new TaskPool(); - private ICommandProvider provider; + private final TaskPool pool; + private final ICommandProvider provider; + private final int lines; /** Add the fork command. * - * @param name the command name */ - public CommandFork(final String name) { - super(name); - addParameters(); + * @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. * - */ - private void addParameters() { - try { - addStringParameter("join", false); - addBooleanParameter("list"); - } catch (final InvalidParameterException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + * @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; } - /* (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 { - if (parameters.getBool("list")) { - for (final String id : pool.getPIDs()) { - try { - out.println(id + "\t" + pool.get(id).getName()); - } catch (final IOException e) { - throw new CommandRunException( - CommandRunExceptionType.INTERACTION, - "Failed to write to console", e, this); - } - } + 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 String string = parameters.get("join"); - if (string != null) { - // Join the command. - final ForkTask cmd = (ForkTask) pool.get(string); - if (cmd == null) { - throw new CommandRunException("No such fork process", this); - } - cmd.join(out, in); - return; - } - - final List strings = parameters.getAdditionals(); - final ICommand cmd = provider.get(strings.get(0)); - final String[] args = Arrays.copyOfRange(strings.toArray(new String[0]), - 1, strings.size()); - final ForkTask task = new ForkTask(cmd, args); + 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(); @@ -131,18 +100,20 @@ public class CommandFork extends ParametrizedCommand { * @see fr.bigeon.gclc.command.ICommand#tip() */ @Override public String tip() { - // TODO Auto-generated method stub - // return null; - throw new RuntimeException("Not implemented yet"); + return "Command background launch"; } /* (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"); + return ""; } + /* (non-Javadoc) + * @see fr.bigeon.gclc.command.Command#usagePattern() */ + @Override + protected String usagePattern() { + return super.usagePattern() + " "; + } } diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/ForkCommandTask.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/ForkCommandTask.java new file mode 100644 index 0000000..3e1b82b --- /dev/null +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/ForkCommandTask.java @@ -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); + } + +} diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/ForkTask.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/ForkTask.java index f19c504..7792741 100644 --- a/gclc-process/src/main/java/fr/bigeon/gclc/process/ForkTask.java +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/ForkTask.java @@ -1,6 +1,6 @@ /* * process, Distribution repositories and basic setup for Emmanuel Bigeon projects - * Copyright (C) 2014-2017 E. Bigeon + * Copyright (C) 2014-2018 E. Bigeon * mailto:emmanuel@bigeon.fr * * This software is governed by the CeCILL license under French law and @@ -35,63 +35,70 @@ */ package fr.bigeon.gclc.process; -import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import fr.bigeon.gclc.command.ICommand; +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; /** *

* TODO * - * @author Emmanuel Bigeon - * - */ -public class ForkTask implements Task { + * @author Emmanuel Bigeon */ +public abstract class ForkTask implements Task { - private final ICommand command; - private final String[] args; private final Set listeners = new HashSet<>(); - private boolean running = false; + private boolean running = false; - /** @param cmd the command - * @param args the arguements */ - public ForkTask(final ICommand cmd, final String[] args) { - command = cmd; - this.args = Arrays.copyOf(args, args.length); + 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 void addInterruptionListener(final InterruptionListener listener) { + public final void addInterruptionListener(final InterruptionListener listener) { listeners.add(listener); } - /* (non-Javadoc) - * @see fr.bigeon.gclc.process.Task#getName() */ - @Override - public String getName() { - return command.getCommandName(); - } - /* (non-Javadoc) * @see fr.bigeon.gclc.process.Task#isRunning() */ @Override - public boolean isRunning() { - return running; + public final boolean isRunning() { + synchronized (runLock) { + return running; + } } /** @param out the console output - * @param in the console input */ - public void join(final ConsoleOutput out, final ConsoleInput in) { - // TODO Auto-generated method stub - // - throw new RuntimeException("Not implemented yet"); + * @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) @@ -99,29 +106,50 @@ public class ForkTask implements Task { * fr.bigeon.gclc.process.Task#rmInterruptionListener(fr.bigeon.gclc.process * .InterruptionListener) */ @Override - public void rmInterruptionListener(final InterruptionListener listener) { + public final void rmInterruptionListener(final InterruptionListener listener) { listeners.remove(listener); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override - public void run() { - running = true; - try { - // TODO Auto-generated method stub - // - throw new RuntimeException("Not implemented yet"); - } finally { - running = false; + 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 void setRunning(final boolean running) { - this.running = running; + public final void setRunning(final boolean running) { + synchronized (runLock) { + this.running = running; + runLock.notifyAll(); + } } + protected final Object getRunningLock() { + return runLock; + } } diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/InterruptionListener.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/InterruptionListener.java index 0034f0c..4011921 100644 --- a/gclc-process/src/main/java/fr/bigeon/gclc/process/InterruptionListener.java +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/InterruptionListener.java @@ -1,6 +1,6 @@ /* * process, Distribution repositories and basic setup for Emmanuel Bigeon projects - * Copyright (C) 2014-2017 E. Bigeon + * Copyright (C) 2014-2018 E. Bigeon * mailto:emmanuel@bigeon.fr * * This software is governed by the CeCILL license under French law and diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessAttachement.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessAttachement.java new file mode 100644 index 0000000..53c46d7 --- /dev/null +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessAttachement.java @@ -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)); + } + +} diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessClear.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessClear.java new file mode 100644 index 0000000..db7deb7 --- /dev/null +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessClear.java @@ -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; + } + +} diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessKill.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessKill.java index 9364994..56a0d65 100644 --- a/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessKill.java +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessKill.java @@ -1,6 +1,6 @@ /* * process, Distribution repositories and basic setup for Emmanuel Bigeon projects - * Copyright (C) 2014-2017 E. Bigeon + * Copyright (C) 2014-2018 E. Bigeon * mailto:emmanuel@bigeon.fr * * This software is governed by the CeCILL license under French law and diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessList.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessList.java index 61b5508..be7a6d8 100644 --- a/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessList.java +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/ProcessList.java @@ -1,6 +1,6 @@ /* * process, Distribution repositories and basic setup for Emmanuel Bigeon projects - * Copyright (C) 2014-2017 E. Bigeon + * Copyright (C) 2014-2018 E. Bigeon * mailto:emmanuel@bigeon.fr * * This software is governed by the CeCILL license under French law and diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/ScreenAttachement.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/ScreenAttachement.java new file mode 100644 index 0000000..69ec006 --- /dev/null +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/ScreenAttachement.java @@ -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)); + } +} diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/Task.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/Task.java index c7aa3c9..ea9a476 100644 --- a/gclc-process/src/main/java/fr/bigeon/gclc/process/Task.java +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/Task.java @@ -1,6 +1,6 @@ /* * process, Distribution repositories and basic setup for Emmanuel Bigeon projects - * Copyright (C) 2014-2017 E. Bigeon + * Copyright (C) 2014-2018 E. Bigeon * mailto:emmanuel@bigeon.fr * * This software is governed by the CeCILL license under French law and diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/TaskPool.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/TaskPool.java index c4fb770..8d10102 100644 --- a/gclc-process/src/main/java/fr/bigeon/gclc/process/TaskPool.java +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/TaskPool.java @@ -1,6 +1,6 @@ /* * process, Distribution repositories and basic setup for Emmanuel Bigeon projects - * Copyright (C) 2014-2017 E. Bigeon + * Copyright (C) 2014-2018 E. Bigeon * mailto:emmanuel@bigeon.fr * * This software is governed by the CeCILL license under French law and @@ -47,13 +47,29 @@ public final class TaskPool { /** The running processes. */ private final Map running = new HashMap<>(); /** The count for process id. */ - private int count = 0; + private int count = 0; /** The lock for pid attribution synchronization. */ - private final Object lock = new Object(); + 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. @@ -64,22 +80,23 @@ public final class TaskPool { if (cmd == null) { throw new IllegalArgumentException("Task cannot be null"); //$NON-NLS-1$ } - final String pid = getPID(); + final String pid; synchronized (lock) { + pid = getPID(); running.put(pid, cmd); } - cmd.addInterruptionListener(new InterruptionListener() { - - @SuppressWarnings("synthetic-access") - @Override - public void interrupted() { - synchronized (lock) { - running.remove(pid); - count = Math.min(count, Integer.parseInt(pid)); + if (autoClear) { + cmd.addInterruptionListener(new InterruptionListener() { + @SuppressWarnings("synthetic-access") + @Override + public void interrupted() { + synchronized (lock) { + remove(pid); + } + cmd.rmInterruptionListener(this); } - cmd.rmInterruptionListener(this); - } - }); + }); + } return pid; } @@ -112,4 +129,21 @@ public final class TaskPool { public Collection 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. + *

+ * 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); + } + } + } } diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/TaskSpawner.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/TaskSpawner.java index b287f72..7b9cbf1 100644 --- a/gclc-process/src/main/java/fr/bigeon/gclc/process/TaskSpawner.java +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/TaskSpawner.java @@ -1,6 +1,6 @@ /* * process, Distribution repositories and basic setup for Emmanuel Bigeon projects - * Copyright (C) 2014-2017 E. Bigeon + * Copyright (C) 2014-2018 E. Bigeon * mailto:emmanuel@bigeon.fr * * This software is governed by the CeCILL license under French law and @@ -35,6 +35,8 @@ */ 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; @@ -46,31 +48,35 @@ import fr.bigeon.gclc.manager.ConsoleOutput; 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) { + 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 */ + * @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); + 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 void execute(final ConsoleOutput out, final ConsoleInput in, + 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); - th.start(); + threadPool.execute(th); } } diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/io/ConnectingConsoleInput.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/io/ConnectingConsoleInput.java new file mode 100644 index 0000000..6cbbeff --- /dev/null +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/io/ConnectingConsoleInput.java @@ -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; + } + } + } +} diff --git a/gclc-process/src/main/java/fr/bigeon/gclc/process/io/ConnectingConsoleOutput.java b/gclc-process/src/main/java/fr/bigeon/gclc/process/io/ConnectingConsoleOutput.java new file mode 100644 index 0000000..9292c45 --- /dev/null +++ b/gclc-process/src/main/java/fr/bigeon/gclc/process/io/ConnectingConsoleOutput.java @@ -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 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; + } +}