0

I have a code that compiles a java source file and runs it . the console output is displayed in a jtextarea in the application. the problem is although , the source file is being compiled and the .class files are being created , the output jtextarea does not displays anything . i tried running the lengthy tasks on a different thread , but that doesnt helps either. what am i doing wrong here ?

UPDATE: i updated the write method of my custom output stream class to include the append method in a swing worker class.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

class test extends JFrame
{
    JTextArea content,compiler;
    JSplitPane pane;
    JMenuBar jmb = new JMenuBar();
    JMenu menu = new JMenu("Options");
    JMenuItem item = new JMenuItem("Compile") , item1 = new JMenuItem("Run") , item2 = new JMenuItem("Save");

    test()
    {
        setTitle("Testing Window");
        setSize(700,700);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        content = new JTextArea();
        compiler = new JTextArea();
        PrintStream stream = new PrintStream(new cos(compiler));
        System.setOut(stream);
        System.setErr(stream);
        pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,true,new JScrollPane(content),new JScrollPane(compiler));
        pane.setResizeWeight(0.8);
        add(pane);
        menu.add(item);
        menu.add(item1);
        menu.add(item2);
        jmb.add(menu);
        setJMenuBar(jmb);

        ActionListener listener = (ActionEvent ae) -> {
            try(FileWriter file = new FileWriter("hello.java");
                BufferedWriter bw = new BufferedWriter(file))
            {
                Scanner sc = new Scanner(content.getText());
                while( sc.hasNext())
                {
                    bw.write(sc.nextLine());
                    bw.newLine();
                }

            }catch(Exception e){e.printStackTrace();}
        };
        item2.addActionListener(listener);


        ActionListener listener1 = (ActionEvent ae)->{
            Runnable runnable = ()->{
                try
                {
                    Process p = Runtime.getRuntime().exec("javac hello.java");
                    p.waitFor();
                    System.out.print("Compiled Successfully \n");

                }catch(Exception e){e.printStackTrace();}
            };
            Thread newThread = new Thread(runnable);
            newThread.start();
        };
        item.addActionListener(listener1);

        ActionListener listener2 = (ActionEvent ae)->{
            Runnable runnable = ()->{
                try
                {
                    Process p = Runtime.getRuntime().exec("java hello");
                    p.waitFor();

                }catch(Exception e){e.printStackTrace();}
            };

            Thread newThread = new Thread(runnable);
            newThread.start();
        };
        item1.addActionListener(listener2);

        setVisible(true);
    }

    public static void main(String args[])
    {
        SwingUtilities.invokeLater( ()->{new test();} );
    }
}

class cos extends OutputStream
{
    JTextArea textarea;
    SwingWorker worker;

    cos(JTextArea textarea)
    {
        this.textarea = textarea;
    }

    public void write(int b)throws IOException
    {
        worker = new SwingWorker()
        {
           protected Object doInBackground()
           {
              publish(String.valueOf( (char)b ));
              return null;
           }

           protected void process(ArrayList<String> list)
           {
              textarea.append(list.get(list.size()-1))//to get latest string
           }

       };
        worker.execute();

    }
}
  • 3
    You've got your Swing threading all wrong, including trying to write directly to the JTextArea from within a background thread. Use a SwingWorker, send the String data obtained in the worker's background thread to the Swing GUI using the SwingWorker's publish/process method pair and your problem should be solved. – Hovercraft Full Of Eels Jan 04 '17 at 17:51
  • okay , i will try that . thanks brother :) –  Jan 04 '17 at 17:53
  • is there any other issue in it? –  Jan 04 '17 at 17:57
  • You might review this related [example](http://stackoverflow.com/a/3245805/230513) before you edit your question to show your revised approach. – trashgod Jan 04 '17 at 17:59
  • can someone guide me on how to add a swing worker in my code. cant seem to figure a way out. –  Jan 04 '17 at 18:52
  • That's not how this site works. If you need help with your SwingWorker, you'll want to show your attempt and tell what specific problems you may be having. – Hovercraft Full Of Eels Jan 04 '17 at 18:55
  • i updated my question –  Jan 04 '17 at 19:08

1 Answers1

0

Say you had a simple Java program that just printed some Strings to the console output with a delay, something like:

package foo1;

import java.util.concurrent.TimeUnit;

public class StringProducer {
    private static final int MAX = 10;
    private static final long SLEEP_TIME = 400;

    public static void main(String[] args) {
        System.out.println("Start");
        for (int i = 0; i < MAX; i++) {
            try {
                TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("At index: " + i);
            try {
                TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("End");
    }
}

Say you wanted to run this separate class, and capture the output from the OS as the above class is being run in its own process, and display it into a JTextArea, you could create a SwingWorker, inside of it, create a Process that runs the OS's command shell program, for Windows, "cmd.exe", capture the process's InputStream and "gobble" it in its own Thread using a Scanner, and then publish/process it into the JTextArea like the code below. The code assumes that both Java programs are in the same package/classpath region:

package foo1;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;

import javax.swing.*;

@SuppressWarnings("serial")
public class WorkerTester extends JPanel {
    private static final int ROWS = 30;
    private static final int COLS = 60;

    // name of the Java program to run in its own process
    public static final String CLASS_TO_RUN = "StringProducer";

    // our JTextArea
    private JTextArea textArea = new JTextArea(ROWS, COLS);

    public WorkerTester() {
        // make jtextarea non-editable/focusable and put into jscrollpane
        textArea.setFocusable(false);
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        // this button will start the process and the worker
        JPanel btnPanel = new JPanel();
        btnPanel.add(new JButton(new WorkerAction("Start Action")));

        setLayout(new BorderLayout());
        add(scrollPane, BorderLayout.CENTER);
        add(btnPanel, BorderLayout.PAGE_END);
    }

    // Our JButton's Action
    private class WorkerAction extends AbstractAction {
        public WorkerAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            final MyWorker myWorker = new MyWorker();
            myWorker.addPropertyChangeListener(new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                        // you always want to check for exceptions by calling
                        // get on the worker when done
                        try {
                            myWorker.get();
                        } catch (InterruptedException | ExecutionException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            myWorker.execute(); // run the worker
        }
    }

    private class MyWorker extends SwingWorker<Void, String> {
        private PrintStream out;
        private Scanner scanner;

        @Override
        protected Void doInBackground() throws Exception {
            @SuppressWarnings("unused")
            String separator = System.getProperty("file.separator"); // in case needed
            String classpath = "\"" + System.getProperty("java.class.path") + "\"";

            Package packageValue = WorkerTester.this.getClass().getPackage();
            String packageName = packageValue.getName() + ".";

            ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe");
            processBuilder.redirectErrorStream(true); // combine error and input streams
            Process process = processBuilder.start();

            InputStream is = process.getInputStream();
            OutputStream os = process.getOutputStream();
            out = new PrintStream(os);
            scanner = new Scanner(is);

            new Thread(new ScannerGobbler(scanner)).start();

            out.println("dir"); // just to test out and see where we are
            out.println("java -cp .;" + classpath + " " + packageName + CLASS_TO_RUN);
            out.println("exit");
            out.close();
            int exitValue = process.waitFor();
            publish("Process Exited with exitValue of: " + exitValue);
            return null;
        }

        @Override
        protected void process(List<String> chunks) {
            for (String chunk : chunks) {
                textArea.append(chunk + "\n");
            }
        }

        private class ScannerGobbler implements Runnable {
            private Scanner scanner;

            public ScannerGobbler(Scanner scanner) {
                this.scanner = scanner;
            }

            @Override
            public void run() {
                while (scanner.hasNextLine()) {
                    publish(scanner.nextLine());
                }
                if (scanner != null) {
                    scanner.close();
                }
            }
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("WorkerTester");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new WorkerTester());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • thanks for the answer . i had a question though . in my original code , do the compile and run actions happen in different processes ? is this the reason why the textarea is not updating? –  Jan 05 '17 at 06:11