I have a BufferedWriter to which I use to write characters into a PipedInputStream. Based on keyboard events. But I am implementing a backspace handler, but I can see no way of doing so without unputc.
I am almost falling back into using a string to buffer the current line.
Perhaps, I could do a better job using Canvas instead. (God how I hate java!)
public class Console extends JTextArea {
/**
*
*/
private static final long serialVersionUID = 6315506875930497268L;
private PipedInputStream stdin;
private PipedOutputStream stdout;
private PipedOutputStream stderr;
private boolean bounceKey = true;
private class Dispatcher implements Runnable {
InputStream in;
public Dispatcher(InputStream in) {
this.in = in;
}
#Override
public void run() {
Reader input = new BufferedReader(new InputStreamReader(in));
int c;
try {
try {
while ((c = input.read()) >= 0) {
append(String.valueOf((char) c));
}
} finally {
input.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public Console() throws IOException {
stdin = new PipedInputStream();
stdout = new PipedOutputStream();
stderr = new PipedOutputStream();
final Writer kbd = new BufferedWriter(new OutputStreamWriter(
new PipedOutputStream(stdin)));
new Thread(new Dispatcher(new PipedInputStream(stdout))).start();
new Thread(new Dispatcher(new PipedInputStream(stderr))).start();
addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
try {
char ch = e.getKeyChar();
kbd.write(ch);
append(String.valueOf(ch));
if(ch == '\n') kbd.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
}
#Override
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
switch(keycode) {
case KeyEvent.VK_BACK_SPACE:
// Erase the last char from buffer and the screen
}
}
#Override
public void keyReleased(KeyEvent e) {
} // DONOTHING
});
setEditable(false);
setFocusable(true);
setBackground(Color.black);
setForeground(Color.lightGray);
setFont(new Font("Monospace", Font.BOLD, 12));
setLineWrap(true);
}
public OutputStream getStderr() {
return stderr;
}
public OutputStream getStdout() {
return stdout;
}
public InputStream getStdin() {
return stdin;
}
public boolean isBounceKey() {
return bounceKey;
}
public void setBounceKey(boolean bounceKey) {
this.bounceKey = bounceKey;
}
}
Yes, see PushbackInputStream.
I am almost falling back into using a string to buffer the current line.
That is the correct way to do it: save up the whole line and don't do anything with it it until the user presses "enter". They might delete the whole thing - what if the process you are communicating with has already taken action?
I.e. what you should be doing is the thing you are for some reason trying to avoid doing! :-)
Related
i'm trying to create a chat application using multhitreading functionalities and here's the code of the session class that handles connections and of the server class that accept connections:
Session class:
public class Session extends Thread{
Socket Sock;
BufferedReader din;
PrintWriter dout;
Thread receive;
Server serv;
boolean connected = false;
String lineSep = System.getProperty("line.separator");
public Session(Socket s, Server n){
super("ThreadSessions");
this.Sock = s;
this.serv = n;
}
public void run(){
try{
din = new BufferedReader(new InputStreamReader(Sock.getInputStream()));
dout = new PrintWriter(Sock.getOutputStream());
connected = true;
Receive();
}
catch(IOException ioe){
ioe.printStackTrace();
}
receive.start();
}
public void sendTo(String text){
dout.write(text);
dout.flush();
}
public void sendToAll(String text){
for(int ind = 0; ind < serv.sessions.size(); ind++){
Session s = serv.sessions.get(ind);
s.sendToAll(text);
}
}
public void Receive(){
receive = new Thread(new Runnable(){
#Override
public void run() {
receive = new Thread(new Runnable(){
String msgIn;
public void run() {
while(connected){
try{
msgIn = din.readLine();
if(msgIn != "" || msgIn != null){
System.out.println(msgIn);
msgIn = "";
}else{
}
}
catch(SocketException exc){
exc.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
});
}
}
Server class:
public class Server {
private JFrame frame;
private JTextField txtPort;
JTextArea textArea, textSessions;
String lineSep = System.getProperty("line.separator");
ServerSocket ServSock;
Socket Sock;
String port;
public JTextField textField;
int numbSess = 0, actSess = 0;
ArrayList<Session> sessions = new ArrayList<Session>();
boolean shouldRun = true;
public static void main(String[] args)
{
Server window = new Server();
window.frame.setVisible(true);
}
public Server() {
initializeComponents(); //This void initializes the graphic components
}
private void Connect(){
port = txtPort.getText();
int portN = 0;
try{
portN = Integer.parseInt(port);
}
catch(NumberFormatException exc)
{
exc.printStackTrace();
}
try{
ServSock = new ServerSocket(9081);
while(shouldRun){
Sock = ServSock.accept();
String ip = Sock.getInetAddress().getHostAddress();
Session s = new Session(Sock, this);
s.start();
sessions.add(s);
numbSess++;
}
}
catch(Exception exc){
exc.printStackTrace();
System.exit(3);
}
}
private void initializeComponents() {
[...]
Button btnConn = new JButton("Open Connection");
btnConn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Connect();
}
});
btnConn.setBackground(new Color(0, 0, 0));
btnConn.setForeground(new Color(0, 128, 0));
btnConn.setBounds(160, 13, 137, 25);
frame.getContentPane().add(btnConn);
[...]
}
What i want to do is creating a chat application that can handle more connection at the same time but instead of entering the first connection(session in my app.) it continues waiting for other connections and adding those in the arrayList.
Probably the code is full of mistakes so forgive me.
If somebody knows a better way to create a server that can handle more client's connections those are welcome.
Hope someone can help me, thanks in advance.
instead of entering the first connection(session in my app.) it continues waiting for other connections and adding those in the arrayList
This is due to how your threads are set up
Each time you make and start a session, its run method is called...
public void run()
{
Receive();
[...]
receive.start();
}
...which in turn sets up receive in Receive();
public void Receive()
{
receive = new Thread(new Runnable()
{
public void run()
{
receive = new Thread(new Runnable()
{
public void run()
{
//your actual code that you wanted to run
}
});
}
});
}
The thread created when ran, will do one thing, set up receive yet again, with the code you wanted the first time
receive = new Thread(new Runnable()
{
public void run()
{
//your actual code that you wanted to run
}
});
But after you call Receive();, you only called receive.start(); once
You'll either need to call it twice, and somehow ensure that it updated in time, or just remove the excess thread
I have a simple task of capturing all System.out output; I am however failing:
public class Main {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
ArrayList<String> history = new ArrayList<>();
StringBuilder output = new StringBuilder();
StringBuilder input = new StringBuilder();
JPanel panel = new JPanel();
JTextArea txt = new JTextArea();
String PROMPT = "> ";
Runnable show = new Runnable() {
public void run() {
txt.setText("");
for (String line: history) txt.append(line + "\n");
txt.append(PROMPT);
txt.append(output.toString());
txt.setCaretPosition(txt.getText().length());
}
};
PrintStream os = new PrintStream(new OutputStream() {
#Override
public void write(int b) throws IOException {
if (b == 13) {
history.add(input.toString());
input = new StringBuilder();
SwingUtilities.invokeLater(show);
} else input.append((char)b);
}
});
void init(String title) {
panel.setLayout(new BorderLayout());
txt.setLineWrap(true);
txt.setFont(new Font("Courier", Font.PLAIN, 16));
txt.setBackground(Color.BLACK);
txt.setForeground(Color.GREEN);
txt.setCaretColor(Color.GREEN);
txt.setEditable(false);
txt.setText(PROMPT);
txt.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == '\n') {
System.setOut(os);
System.setErr(os);
String result = output.toString();
history.add(PROMPT + result);
try {
engine.eval(result);
} catch (ScriptException ex) {
history.add(ex.toString());
}
output = new StringBuilder();
} else {
output.append(e.getKeyChar());
}
SwingUtilities.invokeLater(show);
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
});
panel.add(txt, BorderLayout.CENTER);
JFrame frame = new JFrame(title);
frame.setSize(650, 425);
frame.setContentPane(panel);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String args[]) {
new Main().init("javascript");
}
}
I should note the output I am looking for comes from something like:
new ScriptEngineManager().getEngineByName("js").eval("print(\"test\");");
The output is not going anywhere other than normal STDOUT a.k.a. System.out. Why?
ScriptEngine output redirection:
To redirect the output of a script you first have to get its ScriptContext instance, that has a method called setWriter(Writer).
That sets the output of the ScriptEngine.
For that you first need a Writer. This can be simple like this one:
public class CaptureWriter extends Writer
{
private StringBuilder m_build;
public CaptureWriter(StringBuilder build)
{
m_build = build;
}
#Override
public void write(char[] cbuf, int off, int len) throws IOException
{
m_build.insert(m_build.length(), cbuf, off, len);
}
#Override
public void flush() throws IOException
{
}
#Override
public void close() throws IOException
{
}
}
This writes all input into a StringBuilder.
Then you register it into a ScriptContext
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
engine.getContext().setWriter(new CaptureWriter(m_mess));
When running this simple program:
StringBuilder build = new StringBuilder();
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
engine.getContext().setWriter(new CaptureWriter(build));
try
{
engine.eval("print(\"Hello\")");
engine.eval("print(\"World\")");
engine.eval("print(\"You\")");
engine.eval("print(\"There\")");
} catch(ScriptException e)
{
e.printStackTrace();
}
System.out.println(build);
The output is the buffered output of the script:
Hello
World
You
There
You can of course hook anything into the write method of the Writer implementation, this is just an example.
System.out redirection:
To capture the System.out output you have to make your own PrintStream implementation, it can be very simple like:
public class Capture extends PrintStream
{
private StringBuilder m_build;
public Capture(OutputStream out, StringBuilder build)
{
super(out);
m_build = build;
}
public void println(String s)
{
super.println(s);
m_build.append(s + "\n");
}
}
Then you need to register it in the runtime:
StringBuilder build = new StringBuilder();
System.setOut(new Capture(System.out, build));
Then every time System.out.println is called it works like the overriding PrintStream should and the message gets written into the provided StringBuilder.
To capture other messages you'll need to override other methods.
You can capture System.out be providing a PrintStream that you control ... try something like this
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
PrintStream old = System.out;
System.setOut(ps);
System.out.println("This will be captured :P");
System.out.flush();
System.setOut(old);
System.out.println("He is what was captured : " + baos.toString());
Hi i am using Jsch to run my shell command. I want to make it interactive .I am using java textarea to display the output of a script.This script requires some user inputs also. How i can i make this textarea to accept user input and run the commands.
public void init(){
try{
JSch jsch=new JSch();
Panel panel = new Panel();
TextArea log = new TextArea();
panel.add(log);
add(panel);
PrintStream printStream = new PrintStream(new CustomOutputStream(log));
System.setOut(printStream);
System.setErr(printStream);
Session session=jsch.getSession("akumar", "banas", 22);
String passwd = "*****";
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setPassword(passwd);
session.connect(30000); // making a connection with timeout.
Channel channel=session.openChannel("shell");
channel.setInputStream(System.in);
channel.setOutputStream(System.out);
channel.connect(3*1000);
printStream.println("sftp akumar#banas");
printStream.flush();
}
catch(Exception e){
System.out.println(e);
}
}
// This is the code for making textarea as output stream
public class CustomOutputStream extends OutputStream {
private TextArea textArea;
public CustomOutputStream(TextArea textArea) {
this.textArea = textArea;
}
#Override
public void write(int b) throws IOException {
// redirects data to the text area
textArea.append(String.valueOf((char)b));
// scrolls the text area to the end of data
textArea.setCaretPosition(textArea.getText().length());
}
}
Here is an approach using a BlockingQueue to pass data from a JTextField's actionListener to the CustomInputStream.
public class Main extends JFrame {
public Main() {
init();
}
public void init() {
try {
JSch jsch = new JSch();
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(200, 200));
JTextArea log = new JTextArea();
JTextField cmd = new JTextField();
panel.setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane(log);
panel.add(scrollPane, BorderLayout.CENTER);
panel.add(cmd, BorderLayout.SOUTH);
add(panel);
Session session = jsch.getSession("akumar", "banas", 22);
String passwd = "******";
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setPassword(passwd);
session.connect(30000); // making a connection with timeout.
Channel channel = session.openChannel("shell");
channel.setInputStream(new CustomInputStream(cmd));
channel.setOutputStream(new CustomOutputStream(log));
channel.connect(3 * 1000);
pack();
} catch (Exception e) {
System.out.println(e);
}
}
public class CustomInputStream extends InputStream implements ActionListener {
final JTextField field;
final BlockingQueue<String> q;
public CustomInputStream(JTextField field) {
this.field = field;
q = new LinkedBlockingQueue<>();
field.addActionListener(this);
}
private String s;
int pos;
#Override
public int read() throws IOException {
while (null == s || s.length() <= pos) {
try {
s = q.take();
pos = 0;
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
int ret = (int) s.charAt(pos);
pos++;
return ret;
}
#Override
public boolean markSupported() {
return false;
}
#Override
public int read(byte[] b, int off, int len) throws IOException {
int bytes_copied = 0;
while (bytes_copied < 1) {
while (null == s || s.length() <= pos) {
try {
s = q.take();
System.out.println("s = " + s);
pos = 0;
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
int bytes_to_copy = len < s.length()-pos ? len : s.length()-pos;
System.arraycopy(s.getBytes(), pos, b, off, bytes_to_copy);
pos += bytes_to_copy;
bytes_copied += bytes_to_copy;
}
return bytes_copied;
}
#Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void actionPerformed(ActionEvent e) {
q.add(field.getText() + "\r\n");
field.setText("");
}
}
// This is the code for making textarea as output stream
public class CustomOutputStream extends OutputStream {
private JTextArea textArea;
public CustomOutputStream(JTextArea textArea) {
this.textArea = textArea;
}
#Override
public void write(int b) throws IOException {
// redirects data to the text area
textArea.append(String.valueOf((char) b));
// scrolls the text area to the end of data
textArea.setCaretPosition(textArea.getText().length());
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
String s = new String(b,off,len);
textArea.append(s);
textArea.setCaretPosition(textArea.getText().length());
}
#Override
public void write(byte[] b) throws IOException {
this.write(b, 0, b.length);
}
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(() -> {
new Main().setVisible(true);
});
}
}
I'm writing a program that constantly pings a server. I wrote the code to check it once and put the ping in a JLabel and put it in a method called setPing().
Here is my code
private void formWindowOpened(java.awt.event.WindowEvent evt) {
setPing();
}
That worked but only did it once, so I did:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
for(;;){
setPing();
}
}
But this doesn't even work for the first time.
I didnt put the setPing method because it was too long so here it is:
public String setPing(){
Runtime runtime = Runtime.getRuntime();
try{
Process process = runtime.exec("ping lol.garena.com");
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
int i = 0;
i = line.indexOf("Average");
if(i > 0){
String finalPing = "";
line.toCharArray();
try
{
finalPing = "";
for(int x = i; x < i + 17; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException e)
{
try
{
finalPing = "";
for(int x = i; x < i + 16; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException f)
{
try
{
finalPing = "";
for(int x = i; x < i + 15; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException g){}
}
}
String final1Ping = finalPing.replaceAll("[^0-9]", "");
return final1Ping;
}
}
}catch(IOException e){
}
return "";
}
UPDATE
Just in case this is important, Im using netbeans. I created a form and put this code in the formWindowOpened evt instead of calling it in main:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PingWorker().execute();
}
};
Timer timer = new Timer(1000, timerListener);
timer.start();
jLabel1.setText(label.getText());
timer.stop();
// TODO add your handling code here:
}
class PingWorker extends SwingWorker {
int time;
#Override
protected Object doInBackground() throws Exception {
time = pingTime("lol.garena.com");
return new Integer(time);
}
#Override
protected void done() {
label.setText("" + time);
}
};
public JComponent getUI() {
return label;
}
public static int pingTime(String hostnameOrIP) {
Socket socket = null;
long start = System.currentTimeMillis();
try {
socket = new Socket(hostnameOrIP, 80);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
long end = System.currentTimeMillis();
return (int) (end - start);
}
Use a Swing Timer for repeating tasks & a SwingWorker for long running tasks. E.G. of both below - it uses a Timer to repeatedly perform a 'long running' task (a ping) in a SwingWorker.
See Concurrency in Swing for more details on the Event Dispatch Thread and doing long running or repeating tasks in a GUI.
This code combines a long running task ('pinging' a server) using SwingWorker invoked from a repeating task (updating the JLabel repeatedly with the times) using a Swing based Timer.
import java.awt.event.*;
import javax.swing.*;
import java.net.Socket;
public class LabelUpdateUsingTimer {
static String hostnameOrIP = "stackoverflow.com";
int delay = 5000;
JLabel label = new JLabel("0000");
LabelUpdateUsingTimer() {
label.setFont(label.getFont().deriveFont(120f));
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PingWorker().execute();
}
};
Timer timer = new Timer(delay, timerListener);
timer.start();
JOptionPane.showMessageDialog(
null, label, hostnameOrIP, JOptionPane.INFORMATION_MESSAGE);
timer.stop();
}
class PingWorker extends SwingWorker {
int time;
#Override
protected Object doInBackground() throws Exception {
time = pingTime();
return new Integer(time);
}
#Override
protected void done() {
label.setText("" + time);
}
};
public static int pingTime() {
Socket socket = null;
long start = System.currentTimeMillis();
try {
socket = new Socket(hostnameOrIP, 80);
} catch (Exception weTried) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception weTried) {}
}
}
long end = System.currentTimeMillis();
return (int) (end - start);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new LabelUpdateUsingTimer();
}
};
SwingUtilities.invokeLater(r);
}
}
You could use a Thread. The problem is you are blocking the main thread, thereby blocking your program. To get around this, start a background Thread to update components repeatedly.
(Note: you need to update GUI components on the EDT, so use SwingUtilities.invokeLater)
(new Thread((new Runnable(){
#Override
public void run(){
while(true){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
refToJLabel.setText(Math.random());
}
});
}
}
}))).start();
I want to print in the text area named arena. I am calling the append method from the same class with no luck so far. How to print a string in a text area??
public class Chat_window extends javax.swing.JFrame implements Runnable {
public static DataInputStream in = null;
public static PrintStream out = null;
private static Socket cs = null;
private static BufferedReader zz;
private static boolean alive = true;
public Chat_window() {
initComponents();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Chat_window w=new Chat_window();
w.setVisible(true);
}
});
try {
cs = new Socket("localhost", 3333);
out = new PrintStream(cs.getOutputStream());
zz = new BufferedReader(new InputStreamReader(System.in));
in = new DataInputStream(cs.getInputStream());
} catch (Exception ex) {
System.out.println(ex);
}
if (cs != null && out != null && in != null) {
try {
new Thread(new Chat_window()).start();
while (alive) {
out.println(zz.readLine().trim());
}
out.close();
in.close();
cs.close();
} catch (Exception error) {
System.out.println(error);
}
}
}
#Override
public void run() {
try {
while ((m = in.readLine()) != null) {
**arena.append(m);** //This line doesnt work
System.out.println(m);
}
} catch (Exception e) {
System.out.println(e);
}
}
// Variables declaration
private java.awt.TextArea arena;
private java.awt.TextArea textArea2;
private java.awt.TextArea usrchat;
// End of variables declaration
}
Try to replace it with arena.setText(arena.getText()+"\n"+m);
Or
arena.setText(arena.getText()+m);
If you don't want to add a new line in between :)