Hi,
I'm trying to have basic terminal emulator with swing GUI based on one of the examples in the respository. the following code is first attemp with some limitations and issues.
i hope others to improve it so that we can a basic terminal that can help to integrate it directly in any java app and also identifying issues quickly in the library.
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Shell;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import net.schmizz.sshj.connection.channel.direct.PTYMode;
import java.io.*;
import java.security.PublicKey;
import java.util.*;
import net.schmizz.sshj.common.LoggerFactory;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class Console extends JTextPane
{
DocOutputStream out;
PrintStream pout;
DocInputStream in;
JFrame frame;
StyledDocument doc;
Console() throws Exception, Throwable
{
setPreferredSize(new Dimension(600, 500));
doc = this.getStyledDocument();
out = new DocOutputStream(doc, this);
pout = new PrintStream(out);
in = new DocInputStream();
this.addKeyListener(in);
setFont(new Font("Courier New", Font.PLAIN, 12));
//setEditorKit(new HTMLEditorKit());
frame = new JFrame("Console");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(this));
frame.pack();
frame.setVisible(true);
final SSHClient ssh = new SSHClient();
ssh.addHostKeyVerifier(
new HostKeyVerifier()
{
@Override
public boolean verify(String s, int i, PublicKey publicKey)
{
return true;
}
});
ssh.connect("sdf.org");
try
{
ssh.authPassword("naseh", "naseh");
final Session session = ssh.startSession();
try
{
java.util.Map<PTYMode, Integer> ptyModeMap = new HashMap<>();
// disables echoing of input to output
ptyModeMap.put(PTYMode.ECHO, 0);
session.allocatePTY("vt100", 120, 120, 0, 0, ptyModeMap);
final Shell shell = session.startShell();
new StreamCopier(shell.getInputStream(), getOut(), LoggerFactory.DEFAULT)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stdout");
new StreamCopier(shell.getErrorStream(), getOut(), LoggerFactory.DEFAULT)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stderr");
new StreamCopier(getIn(), shell.getOutputStream(), LoggerFactory.DEFAULT)
.bufSize(shell.getRemoteMaxPacketSize())
.copy();
} finally
{
session.close();
}
} finally
{
ssh.disconnect();
}
}
public InputStream getIn()
{
return in;
}
public PrintStream getOut()
{
return pout;
}
private static class DocOutputStream extends OutputStream
{
StyledDocument doc;
MutableAttributeSet cur;
JTextPane pane;
public DocOutputStream(StyledDocument doc, JTextPane pane)
{
this.doc = doc;
this.pane = pane;
cur = new SimpleAttributeSet();
}
@Override
public void write(int b) throws IOException
{
try
{
doc.insertString(doc.getLength(), (char) b + "", cur);
pane.setCaretPosition(doc.getLength());
}
catch (BadLocationException ex)
{
ex.printStackTrace();
}
}
}
private static class DocInputStream extends InputStream implements KeyListener
{
ArrayBlockingQueue<Integer> queue;
public DocInputStream()
{
queue = new ArrayBlockingQueue<Integer>(1024);
}
@Override
public int read() throws IOException
{
Integer i = null;
try
{
i = queue.take();
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
if (i != null)
return i;
return -1;
}
@Override
public int read(byte[] b, int off, int len) throws IOException
{
if (b == null)
{
throw new NullPointerException();
}
else
if (off < 0 || len < 0 || len > b.length - off)
{
throw new IndexOutOfBoundsException();
}
else
if (len == 0)
{
return 0;
}
int c = read();
if (c == -1)
{
return -1;
}
b[off] = (byte) c;
int i = 1;
try
{
for (; i < len && available() > 0; i++)
{
c = read();
if (c == -1)
{
break;
}
b[off + i] = (byte) c;
}
}
catch (IOException ee)
{
}
return i;
}
@Override
public int available()
{
return queue.size();
}
@Override
public void keyTyped(KeyEvent e)
{
int c = e.getKeyChar();
try
{
queue.put(c);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
@Override
public void keyPressed(KeyEvent e)
{
}
@Override
public void keyReleased(KeyEvent e)
{
}
}
public static void main(String... args) throws Exception, Throwable
{
new Console();
}
}
some limitations to work with:
- interpret ANSI Escape code since you will see many codes in the output command (e.g. color)
- disable editing the previous output and the command prompt
- enable history (arrow up/down)
- interactive shell
- Disable echo (working in some shells and others are not working. in the example it is not working with
sdf.org shells
- hide password ..... and many more