Added interruption flag
Signed-off-by: Emmanuel Bigeon <emmanuel@bigeon.fr>
This commit is contained in:
parent
79ee5394d4
commit
901469792d
@ -49,7 +49,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.bigeon</groupId>
|
<groupId>net.bigeon</groupId>
|
||||||
<artifactId>gclc</artifactId>
|
<artifactId>gclc</artifactId>
|
||||||
<version>2.0.12</version>
|
<version>2.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.bigeon.test</groupId>
|
<groupId>net.bigeon.test</groupId>
|
||||||
|
@ -68,6 +68,7 @@ package net.bigeon.gclc.process.io;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import net.bigeon.gclc.manager.ConsoleInput;
|
import net.bigeon.gclc.manager.ConsoleInput;
|
||||||
@ -82,25 +83,27 @@ public final class ConnectingConsoleInput implements ConsoleInput {
|
|||||||
private static final ConstantString EMPTY_STRING = new ConstantString("");
|
private static final ConstantString EMPTY_STRING = new ConstantString("");
|
||||||
|
|
||||||
/** If the input is closed. */
|
/** If the input is closed. */
|
||||||
private boolean close = false;
|
private boolean close = false;
|
||||||
/** The prompt string. */
|
/** The prompt string. */
|
||||||
private Supplier<String> prompt = EMPTY_STRING;
|
private Supplier<String> prompt = EMPTY_STRING;
|
||||||
/** If the input is currently in prompting state.
|
/** If the input is currently in prompting state.
|
||||||
* <p>
|
* <p>
|
||||||
* To change it you should be in a promptLock. */
|
* To change it you should be in a promptLock. */
|
||||||
private boolean prompting = false;
|
private boolean prompting = false;
|
||||||
/** The synchronization lock for the prompting status. */
|
/** The synchronization lock for the prompting status. */
|
||||||
private final Object promptLock = new Object();
|
private final Object promptLock = new Object();
|
||||||
/** The synchronization lock for the connection status. */
|
/** The synchronization lock for the connection status. */
|
||||||
private final Object connectionLock = new Object();
|
private final Object connectionLock = new Object();
|
||||||
/** The connected console input.
|
/** The connected console input.
|
||||||
* <p>
|
* <p>
|
||||||
* To use it, you should be in a promptLock and connectionLock. */
|
* To use it, you should be in a promptLock and connectionLock. */
|
||||||
private ConsoleInput connected = null;
|
private ConsoleInput connected = null;
|
||||||
/** The connection state.
|
/** The connection state.
|
||||||
* <p>
|
* <p>
|
||||||
* To read or modify it, you should be in a connectionLock synchronize block. */
|
* To read or modify it, you should be in a connectionLock synchronize block. */
|
||||||
private boolean disconnection = false;
|
private boolean disconnection = false;
|
||||||
|
|
||||||
|
private boolean interrupting = false;
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see fr.bigeon.gclc.manager.ConsoleInput#close() */
|
* @see fr.bigeon.gclc.manager.ConsoleInput#close() */
|
||||||
@ -149,6 +152,9 @@ public final class ConnectingConsoleInput implements ConsoleInput {
|
|||||||
public void interruptPrompt() {
|
public void interruptPrompt() {
|
||||||
synchronized (connectionLock) {
|
synchronized (connectionLock) {
|
||||||
synchronized (promptLock) {
|
synchronized (promptLock) {
|
||||||
|
if (prompting) {
|
||||||
|
interrupting = true;
|
||||||
|
}
|
||||||
connectionLock.notifyAll();
|
connectionLock.notifyAll();
|
||||||
if (connected != null) {
|
if (connected != null) {
|
||||||
connected.interruptPrompt();
|
connected.interruptPrompt();
|
||||||
@ -201,14 +207,21 @@ public final class ConnectingConsoleInput implements ConsoleInput {
|
|||||||
actualConnected = connected;
|
actualConnected = connected;
|
||||||
}
|
}
|
||||||
if (connect) {
|
if (connect) {
|
||||||
final String res = actualConnected.prompt(message);
|
try {
|
||||||
synchronized (promptLock) {
|
final String res = actualConnected.prompt(message);
|
||||||
|
synchronized (promptLock) {
|
||||||
|
if (prompting) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final InterruptedIOException e) {
|
||||||
|
// The inner console was interrupted. This can mean we are
|
||||||
|
// disconnecting or actually interrupted.
|
||||||
if (disconnection) {
|
if (disconnection) {
|
||||||
disconnection = false;
|
disconnection = false;
|
||||||
} else if (prompting) {
|
|
||||||
return res;
|
|
||||||
} else {
|
} else {
|
||||||
// prompt interrupted, lose the result.
|
interrupting = false;
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,14 +254,21 @@ public final class ConnectingConsoleInput implements ConsoleInput {
|
|||||||
}
|
}
|
||||||
if (connect) {
|
if (connect) {
|
||||||
synchronized (promptLock) {
|
synchronized (promptLock) {
|
||||||
final String res = actualConnected.prompt(message,
|
try {
|
||||||
end - System.currentTimeMillis());
|
final String res = actualConnected.prompt(message,
|
||||||
if (disconnection) {
|
end - System.currentTimeMillis());
|
||||||
disconnection = false;
|
if (prompting) {
|
||||||
} else if (prompting) {
|
return res;
|
||||||
return res;
|
}
|
||||||
} else {
|
} catch (final InterruptedIOException e) {
|
||||||
// prompt interrupted, lose the result.
|
// The inner console was interrupted. This can mean we are
|
||||||
|
// disconnecting or actually interrupted.
|
||||||
|
if (disconnection) {
|
||||||
|
disconnection = false;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,7 +277,7 @@ public final class ConnectingConsoleInput implements ConsoleInput {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getConnection(final long timeout) {
|
private void getConnection(final long timeout) throws InterruptedIOException {
|
||||||
boolean connect;
|
boolean connect;
|
||||||
synchronized (connectionLock) {
|
synchronized (connectionLock) {
|
||||||
connect = connected != null;
|
connect = connected != null;
|
||||||
@ -268,6 +288,10 @@ public final class ConnectingConsoleInput implements ConsoleInput {
|
|||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (interrupting) {
|
||||||
|
interrupting = false;
|
||||||
|
throw new InterruptedIOException("Prompt ws interrupted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,14 +12,16 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import net.bigeon.gclc.exception.CommandRunException;
|
import net.bigeon.gclc.exception.CommandRunException;
|
||||||
import net.bigeon.gclc.exception.CommandRunExceptionType;
|
import net.bigeon.gclc.exception.CommandRunExceptionType;
|
||||||
import net.bigeon.gclc.utils.PipedConsoleInput;
|
import net.bigeon.gclc.manager.PipedConsoleInput;
|
||||||
import net.bigeon.gclc.utils.PipedConsoleOutput;
|
import net.bigeon.gclc.manager.PipedConsoleOutput;
|
||||||
|
|
||||||
/** @author Emmanuel Bigeon */
|
/** @author Emmanuel Bigeon */
|
||||||
public class ForkTaskTest {
|
public class ForkTaskTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenericForkTask() throws IOException {
|
public void testGenericForkTask() throws IOException {
|
||||||
|
|
||||||
|
// FIXME This test fail to complete on Jenkins.
|
||||||
final ForkTask task = new ForkTask(5) {
|
final ForkTask task = new ForkTask(5) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -36,15 +38,11 @@ public class ForkTaskTest {
|
|||||||
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
|
throw new CommandRunException(CommandRunExceptionType.INTERACTION,
|
||||||
"Unable to prompt user");
|
"Unable to prompt user");
|
||||||
}
|
}
|
||||||
if ("ok".equals(msg)) {
|
out.println(msg);
|
||||||
out.println("Message");
|
|
||||||
} else {
|
|
||||||
out.println("fail");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final Thread execThread = new Thread(task);
|
final Thread execThread = new Thread(task, "Task");
|
||||||
execThread.start();
|
execThread.start();
|
||||||
try {
|
try {
|
||||||
execThread.join(100);
|
execThread.join(100);
|
||||||
@ -55,11 +53,11 @@ public class ForkTaskTest {
|
|||||||
|
|
||||||
try (PipedConsoleOutput pco = new PipedConsoleOutput();
|
try (PipedConsoleOutput pco = new PipedConsoleOutput();
|
||||||
PipedConsoleInput pci = new PipedConsoleInput(null)) {
|
PipedConsoleInput pci = new PipedConsoleInput(null)) {
|
||||||
pci.type("ok");
|
|
||||||
while (!pco.available()) {
|
while (!pco.available()) {
|
||||||
|
pci.type("ok");
|
||||||
task.join(pco, pci, 1000);
|
task.join(pco, pci, 1000);
|
||||||
}
|
}
|
||||||
assertEquals("Execution should work", "Message", pco.readNextLine());
|
assertEquals("Positive execution", "ok", pco.readNextLine());
|
||||||
}
|
}
|
||||||
assertFalse("Running state should be updated by task on its completion",
|
assertFalse("Running state should be updated by task on its completion",
|
||||||
task.isRunning());
|
task.isRunning());
|
||||||
|
@ -14,8 +14,8 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import net.bigeon.gclc.exception.CommandRunException;
|
import net.bigeon.gclc.exception.CommandRunException;
|
||||||
import net.bigeon.gclc.exception.CommandRunExceptionType;
|
import net.bigeon.gclc.exception.CommandRunExceptionType;
|
||||||
|
import net.bigeon.gclc.manager.PipedConsoleOutput;
|
||||||
import net.bigeon.gclc.process.mocks.TaskMock;
|
import net.bigeon.gclc.process.mocks.TaskMock;
|
||||||
import net.bigeon.gclc.utils.PipedConsoleOutput;
|
|
||||||
|
|
||||||
/** @author Emmanuel Bigeon */
|
/** @author Emmanuel Bigeon */
|
||||||
public class ProcessListTest {
|
public class ProcessListTest {
|
||||||
|
@ -4,8 +4,10 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
import java.io.PipedInputStream;
|
import java.io.PipedInputStream;
|
||||||
import java.io.PipedOutputStream;
|
import java.io.PipedOutputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -13,9 +15,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import net.bigeon.gclc.manager.PipedConsoleInput;
|
||||||
|
import net.bigeon.gclc.manager.StreamConsoleInput;
|
||||||
import net.bigeon.gclc.tools.ConstantString;
|
import net.bigeon.gclc.tools.ConstantString;
|
||||||
import net.bigeon.gclc.utils.PipedConsoleInput;
|
|
||||||
import net.bigeon.gclc.utils.StreamConsoleInput;
|
|
||||||
import net.bigeon.test.junitmt.ATestRunnable;
|
import net.bigeon.test.junitmt.ATestRunnable;
|
||||||
import net.bigeon.test.junitmt.FunctionalTestRunnable;
|
import net.bigeon.test.junitmt.FunctionalTestRunnable;
|
||||||
import net.bigeon.test.junitmt.TestFunction;
|
import net.bigeon.test.junitmt.TestFunction;
|
||||||
@ -81,10 +83,26 @@ public class ConnectingConsoleInputTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply() throws Exception {
|
public void apply() throws Exception {
|
||||||
assertNull("Interrupted should return null", in.prompt("m1", -1));
|
try {
|
||||||
assertNull("Interrupted should return null", in.prompt("m2", 5000));
|
in.prompt("m1", -1);
|
||||||
ended.set(true);
|
fail("interruption of infinite waiting prompt should cause error");
|
||||||
assertNull("Overtime should return null", in.prompt("m3", 200));
|
} catch (final InterruptedIOException e) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
in.prompt("m2", 5000);
|
||||||
|
fail("interruption of finite waiting prompt should cause error");
|
||||||
|
} catch (final InterruptedIOException e) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
synchronized (ended) {
|
||||||
|
ended.set(true);
|
||||||
|
try {
|
||||||
|
assertNull("Overtime should return null", in.prompt("m3", 200));
|
||||||
|
} catch (final InterruptedIOException e) {
|
||||||
|
fail("Unexpected interruption error in overtime");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
final ATestRunnable runnable = new FunctionalTestRunnable(one);
|
final ATestRunnable runnable = new FunctionalTestRunnable(one);
|
||||||
@ -95,12 +113,16 @@ public class ConnectingConsoleInputTest {
|
|||||||
public void run() {
|
public void run() {
|
||||||
while (!ended.get()) {
|
while (!ended.get()) {
|
||||||
try {
|
try {
|
||||||
th.join(500);
|
th.join(100);
|
||||||
} catch (final InterruptedException e) {
|
} catch (final InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
in.interruptPrompt();
|
synchronized (ended) {
|
||||||
|
if (!ended.get()) {
|
||||||
|
in.interruptPrompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -112,7 +134,7 @@ public class ConnectingConsoleInputTest {
|
|||||||
|
|
||||||
final PipedOutputStream os = new PipedOutputStream();
|
final PipedOutputStream os = new PipedOutputStream();
|
||||||
final PipedInputStream pis = new PipedInputStream(os);
|
final PipedInputStream pis = new PipedInputStream(os);
|
||||||
in.connect(new StreamConsoleInput(System.out, pis, StandardCharsets.UTF_8));
|
in.connect(new StreamConsoleInput(null, pis, StandardCharsets.UTF_8));
|
||||||
final ATestRunnable runnable2 = new FunctionalTestRunnable(one);
|
final ATestRunnable runnable2 = new FunctionalTestRunnable(one);
|
||||||
final Thread th2 = new Thread(runnable2);
|
final Thread th2 = new Thread(runnable2);
|
||||||
final Thread inter2 = new Thread(new Runnable() {
|
final Thread inter2 = new Thread(new Runnable() {
|
||||||
@ -121,12 +143,16 @@ public class ConnectingConsoleInputTest {
|
|||||||
public void run() {
|
public void run() {
|
||||||
while (!ended.get()) {
|
while (!ended.get()) {
|
||||||
try {
|
try {
|
||||||
th2.join(500);
|
th2.join(100);
|
||||||
} catch (final InterruptedException e) {
|
} catch (final InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
in.interruptPrompt();
|
synchronized (ended) {
|
||||||
|
if (!ended.get()) {
|
||||||
|
in.interruptPrompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import net.bigeon.gclc.utils.PipedConsoleOutput;
|
import net.bigeon.gclc.manager.PipedConsoleOutput;
|
||||||
|
|
||||||
public class ConnectingConsoleOutputTest {
|
public class ConnectingConsoleOutputTest {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user