Fixed readline mixing with previous readchar executions

This commit is contained in:
nathan 2014-10-19 01:10:11 +02:00
parent 227a0d9378
commit c6e4d813a7

View File

@ -8,7 +8,9 @@ import java.io.PrintStream;
import java.net.URL; import java.net.URL;
import java.security.CodeSource; import java.security.CodeSource;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;
import org.fusesource.jansi.Ansi; import org.fusesource.jansi.Ansi;
import org.fusesource.jansi.AnsiConsole; import org.fusesource.jansi.AnsiConsole;
@ -104,13 +106,13 @@ public class InputConsole {
return consoleProxyReader.echo0(); return consoleProxyReader.echo0();
} }
private final ReentrantLock inputLock = new ReentrantLock();
private final Object inputLockSingle = new Object(); private final Object inputLockSingle = new Object();
private final Object inputLockLine = new Object(); private final Object inputLockLine = new Object();
private final ObjectPool<ByteBuffer2> pool = ObjectPoolFactory.create(new ByteBuffer2Poolable()); private final ObjectPool<ByteBuffer2> pool = ObjectPoolFactory.create(new ByteBuffer2Poolable());
private ThreadLocal<ObjectPoolHolder<ByteBuffer2>> threadBufferForRead = new ThreadLocal<ObjectPoolHolder<ByteBuffer2>>(); private ThreadLocal<ObjectPoolHolder<ByteBuffer2>> threadBuffer = new ThreadLocal<ObjectPoolHolder<ByteBuffer2>>();
private CopyOnWriteArrayList<ObjectPoolHolder<ByteBuffer2>> threadBuffersForRead = new CopyOnWriteArrayList<ObjectPoolHolder<ByteBuffer2>>(); private List<ObjectPoolHolder<ByteBuffer2>> threadBuffersForRead = new CopyOnWriteArrayList<ObjectPoolHolder<ByteBuffer2>>();
private volatile int readChar = -1; private volatile int readChar = -1;
private final Terminal terminal; private final Terminal terminal;
@ -120,7 +122,7 @@ public class InputConsole {
String type = System.getProperty(TerminalType.TYPE, TerminalType.AUTO).toLowerCase(); String type = System.getProperty(TerminalType.TYPE, TerminalType.AUTO).toLowerCase();
if ("dumb".equals(System.getenv("TERM"))) { if ("dumb".equals(System.getenv("TERM"))) {
type = "none"; type = TerminalType.NONE;
logger.debug("$TERM=dumb; setting type={}", type); logger.debug("$TERM=dumb; setting type={}", type);
} }
@ -205,13 +207,44 @@ public class InputConsole {
return this.terminal.isEchoEnabled(); return this.terminal.isEchoEnabled();
} }
/** return -1 if no data or bunged-up */
private final int read0() {
synchronized (this.inputLockSingle) {
try {
this.inputLockSingle.wait();
} catch (InterruptedException e) {
return -1;
}
}
/** return null if no data */ return this.readChar;
}
/** return empty char[] if no data */
private final char[] readLinePassword0() {
// don't bother in an IDE. it won't work.
boolean echoEnabled = this.terminal.isEchoEnabled();
this.terminal.setEchoEnabled(false);
char[] readLine0 = readLine0();
this.terminal.setEchoEnabled(echoEnabled);
return readLine0;
}
/** return empty char[] if no data */
private final char[] readLine0() { private final char[] readLine0() {
if (this.threadBufferForRead.get() == null) { synchronized (this.inputLock) {
// empty here, because we don't want to register a readLine WHILE we are still processing
// the current line info.
// the threadBufferForRead getting added is the part that is important
if (this.threadBuffer.get() == null) {
ObjectPoolHolder<ByteBuffer2> holder = this.pool.take(); ObjectPoolHolder<ByteBuffer2> holder = this.pool.take();
this.threadBufferForRead.set(holder); this.threadBuffer.set(holder);
this.threadBuffersForRead.add(holder); this.threadBuffersForRead.add(holder);
} else {
this.threadBuffer.get().getValue().clear();
}
} }
synchronized (this.inputLockLine) { synchronized (this.inputLockLine) {
@ -222,7 +255,7 @@ public class InputConsole {
} }
} }
ObjectPoolHolder<ByteBuffer2> objectPoolHolder = this.threadBufferForRead.get(); ObjectPoolHolder<ByteBuffer2> objectPoolHolder = this.threadBuffer.get();
ByteBuffer2 buffer = objectPoolHolder.getValue(); ByteBuffer2 buffer = objectPoolHolder.getValue();
int len = buffer.position(); int len = buffer.position();
if (len == 0) { if (len == 0) {
@ -237,35 +270,11 @@ public class InputConsole {
this.threadBuffersForRead.remove(objectPoolHolder); this.threadBuffersForRead.remove(objectPoolHolder);
this.pool.release(objectPoolHolder); this.pool.release(objectPoolHolder);
this.threadBufferForRead.set(null); this.threadBuffer.set(null);
return readChars; return readChars;
} }
/** return null if no data */
private final char[] readLinePassword0() {
// don't bother in an IDE. it won't work.
boolean echoEnabled = this.terminal.isEchoEnabled();
this.terminal.setEchoEnabled(false);
char[] readLine0 = readLine0();
this.terminal.setEchoEnabled(echoEnabled);
return readLine0;
}
/** return -1 if no data */
private final int read0() {
synchronized (this.inputLockSingle) {
try {
this.inputLockSingle.wait();
} catch (InterruptedException e) {
return -1;
}
}
return this.readChar;
}
/** /**
* releases any thread still waiting. * releases any thread still waiting.
*/ */
@ -292,6 +301,9 @@ public class InputConsole {
// don't type ; in a bash shell, it quits everything // don't type ; in a bash shell, it quits everything
// \n is replaced by \r in unix terminal? // \n is replaced by \r in unix terminal?
while ((typedChar = this.terminal.read()) != -1) { while ((typedChar = this.terminal.read()) != -1) {
synchronized (this.inputLock) {
// don't let anyone add a new reader while we are still processing the current actions
asChar = (char) typedChar; asChar = (char) typedChar;
if (logger2.isTraceEnabled()) { if (logger2.isTraceEnabled()) {
@ -363,9 +375,9 @@ public class InputConsole {
this.inputLockLine.notifyAll(); this.inputLockLine.notifyAll();
} }
} else { } else {
// only append if we are not a new line.
// our windows console PREVENTS us from returning '\r' (it truncates '\r\n', and returns just '\n') // our windows console PREVENTS us from returning '\r' (it truncates '\r\n', and returns just '\n')
// only append if we are not a new line.
for (ObjectPoolHolder<ByteBuffer2> objectPoolHolder : this.threadBuffersForRead) { for (ObjectPoolHolder<ByteBuffer2> objectPoolHolder : this.threadBuffersForRead) {
ByteBuffer2 buffer = objectPoolHolder.getValue(); ByteBuffer2 buffer = objectPoolHolder.getValue();
buffer.writeChar(asChar); buffer.writeChar(asChar);
@ -373,6 +385,7 @@ public class InputConsole {
} }
} }
} }
}
/** /**
* try to guess if we are running inside an IDE * try to guess if we are running inside an IDE