Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I redirect System.in to JavaFX TextField?

I am working on new project. I am trying to send commands to external computer (Linux) through ethernet from Java. I am using Jsch to create shell connection. I set shell input and output to System.out and System.in.

((ChannelShell)channel).setInputStream(System.in);
((ChannelShell)channel).setOutputStream(System.out);

It works in console! But I need to remote it from javafx GUI app. I have already solved redirecting of System.out to TextArea:

    public void redirectOutputStream() {
        OutputStream out = new OutputStream() {

            private final StringBuilder sb = new StringBuilder();

            @Override
            public void write(int b) throws IOException {
                if (b == '\r') {
                    return;
                }

                if (b == '\n') {
                    final String tmp = sb.toString() + "\n";
                    text.add(tmp);
                    updateText(text);
                    sb.setLength(0);
                } else {
                    sb.append((char) b);
                }
            }
        };

        System.setOut(new PrintStream(out, true));
        // System.setErr(new PrintStream(out, true));
    }

But now I need also to redirect System.in to TextField, so that I can write something into TextField, press enter and send it through shell to the external computer.

Any help would be appreciate. Thanks!

EDIT: Sorry, still doesn't work for me :( ... Now I have this piece of code (I am using javafx):

/** Tmp queue for standard input redirecting */
BlockingQueue<Integer> stdInQueue = new LinkedBlockingQueue<>();



 @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        redirectOutputStream();
        redirectInputStream();
}


/** redirects standard System.out to GUI command_window */
public void redirectOutputStream() {
    OutputStream out = new OutputStream() {

        private final StringBuilder sb = new StringBuilder();

        @Override
        public void write(int b) throws IOException {
            if (b == '\r') {
                return;
            }
            if (b == '\n') {
                final String tmp = sb.toString() + "\n";
                text.add(tmp);
                updateText(text);
                sb.setLength(0);
            } else {
                sb.append((char) b);
            }
        }
    };
    System.setOut(new PrintStream(out, true));
    System.setErr(new PrintStream(out, true));
}

/** redirects standard System.in to GUI command_line */
public void redirectInputStream() {
    InputStream in = new InputStream() {

        @Override
        public int read() throws IOException {
            try {
                int c = stdInQueue.take().intValue();
                return c;
            } catch (InterruptedException exc) {
                Thread.currentThread().interrupt();
                return -1;
            }
        }
    };
    System.setIn(in);
}


@FXML
    void sendButtonPressed(ActionEvent event) {
        if (!command_line.getText().isEmpty()) {
            for (char c : command_line.getText().toCharArray()) {
                System.out.write(new Integer(c)); //display in ListView (output)
                stdInQueue.add(new Integer(c)); 
            }
            System.out.write(new Integer('\n')); //display in ListView (output)     
            stdInQueue.add(new Integer('\n'));
            command_line.clear();
        }
    }

The redirecting of System.out and System.err works perfectly. It is being displayed in javafx ListView.

The problem is still that under the ListView I have a "command line" javafx TextField and I need to write some ssh command into this TextField and redirect it to System.in when pressing "enter" or clicking on "send" button.

The reason why I need to do this is that I am using SSH communication, which is set for System.in and System.out. It fully works in console (tested), but not in my GUI app.

Thanks for any further advice!

like image 978
Zuzana Avatar asked Oct 31 '25 02:10

Zuzana


1 Answers

You can set up a BlockingQueue<Integer> to send the individual characters to, and then have the input stream take characters from it:

BlockingQueue<Integer> stdInQueue = new LinkedBlockingQueue<>();

System.setIn(new InputStream() {

    @Override
    public int read() throws IOException {
        try {
            int c = stdInQueue.take().intValue();
            return c;
        } catch (InterruptedException exc) {
            Thread.currentThread().interrupt();
            return -1 ;
        }
    }
});

textField.setOnAction(e -> {
    for (char c : textField.getText().toCharArray()) {
        stdInQueue.add(new Integer(c));
    }
    stdInQueue.add(new Integer('\n'));
    textField.clear();
});

Here's a quick demo: for testing I just set up a background thread that reads from System.in:

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class StdInFromTextField extends Application {

    @Override
    public void start(Stage primaryStage) {

        TextField textField = new TextField();

        BlockingQueue<Integer> stdInQueue = new LinkedBlockingQueue<>();

        System.setIn(new InputStream() {

            @Override
            public int read() throws IOException {
                try {
                    int c = stdInQueue.take().intValue();
                    return c;
                } catch (InterruptedException exc) {
                    Thread.currentThread().interrupt();
                    return -1 ;
                }
            }
        });

        textField.setOnAction(e -> {
            for (char c : textField.getText().toCharArray()) {
                stdInQueue.add(new Integer(c));
            }
            stdInQueue.add(new Integer('\n'));
            textField.clear();
        });

        // for testing:
        Thread readThread = new Thread(() -> {
            try {
                int i ;
                while ((i = System.in.read()) != -1) {
                    System.out.print((char)i);
                }
            } catch (IOException exc) {
                exc.printStackTrace();
            }
        });
        readThread.setDaemon(true);
        readThread.start();

        primaryStage.setScene(new Scene(new StackPane(textField), 300, 120));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
like image 143
James_D Avatar answered Nov 02 '25 16:11

James_D



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!