Fork process

Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
This commit is contained in:
Emmanuel Bigeon 2018-05-10 11:29:38 -04:00
parent eae7e0d69f
commit 00c0e86d72
15 changed files with 955 additions and 131 deletions

View File

@ -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>])";
}
}

View File

@ -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;
* <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 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.
* </ul>
*
* @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<String> 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() + " <command>";
}
}

View File

@ -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);
}
}

View File

@ -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;
/**
* <p>
* 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<InterruptionListener> 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;
}
}

View File

@ -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

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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));
}
}

View File

@ -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

View File

@ -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<String, Task> 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<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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}