/*
 * Copyright (c) 2002-2018, the original author or authors.
 *
 * This software is distributable under the BSD license. See the terms of the
 * BSD license in the documentation provided with this software.
 *
 * https://opensource.org/licenses/BSD-3-Clause
 */
package jdk.internal.org.jline.terminal.impl;

import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Cursor;
import jdk.internal.org.jline.terminal.Size;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.IntConsumer;

Console implementation with embedded line disciplined. This terminal is well-suited for supporting incoming external connections, such as from the network (through telnet, ssh, or any kind of protocol). The terminal will start consuming the input in a separate thread to generate interruption events.
See Also:
  • LineDisciplineTerminal
/** * Console implementation with embedded line disciplined. * * This terminal is well-suited for supporting incoming external * connections, such as from the network (through telnet, ssh, * or any kind of protocol). * The terminal will start consuming the input in a separate thread * to generate interruption events. * * @see LineDisciplineTerminal */
public class ExternalTerminal extends LineDisciplineTerminal { protected final AtomicBoolean closed = new AtomicBoolean(); protected final InputStream masterInput; protected final Object lock = new Object(); protected boolean paused = true; protected Thread pumpThread; public ExternalTerminal(String name, String type, InputStream masterInput, OutputStream masterOutput, Charset encoding) throws IOException { this(name, type, masterInput, masterOutput, encoding, SignalHandler.SIG_DFL); } public ExternalTerminal(String name, String type, InputStream masterInput, OutputStream masterOutput, Charset encoding, SignalHandler signalHandler) throws IOException { this(name, type, masterInput, masterOutput, encoding, signalHandler, false); } public ExternalTerminal(String name, String type, InputStream masterInput, OutputStream masterOutput, Charset encoding, SignalHandler signalHandler, boolean paused) throws IOException { this(name, type, masterInput, masterOutput, encoding, signalHandler, paused, null, null); } public ExternalTerminal(String name, String type, InputStream masterInput, OutputStream masterOutput, Charset encoding, SignalHandler signalHandler, boolean paused, Attributes attributes, Size size) throws IOException { super(name, type, masterOutput, encoding, signalHandler); this.masterInput = masterInput; if (attributes != null) { setAttributes(attributes); } if (size != null) { setSize(size); } if (!paused) { resume(); } } protected void doClose() throws IOException { if (closed.compareAndSet(false, true)) { pause(); super.doClose(); } } @Override public boolean canPauseResume() { return true; } @Override public void pause() { synchronized (lock) { paused = true; } } @Override public void pause(boolean wait) throws InterruptedException { Thread p; synchronized (lock) { paused = true; p = pumpThread; } if (p != null) { p.interrupt(); p.join(); } } @Override public void resume() { synchronized (lock) { paused = false; if (pumpThread == null) { pumpThread = new Thread(this::pump, toString() + " input pump thread"); pumpThread.setDaemon(true); pumpThread.start(); } } } @Override public boolean paused() { synchronized (lock) { return paused; } } public void pump() { try { byte[] buf = new byte[1024]; while (true) { int c = masterInput.read(buf); if (c >= 0) { processInputBytes(buf, 0, c); } if (c < 0 || closed.get()) { break; } synchronized (lock) { if (paused) { pumpThread = null; return; } } } } catch (IOException e) { processIOException(e); } finally { synchronized (lock) { pumpThread = null; } } try { slaveInput.close(); } catch (IOException e) { // ignore } } @Override public Cursor getCursorPosition(IntConsumer discarded) { return CursorSupport.getCursorPosition(this, discarded); } }