1

I'm developing a java application where I save big files using bufferedInputStream. I have put a progressbar inside a JDialog which indicates the percentage of uploaded files and which increases every n seconds. The problem is that the application waits indefinitely for the dialog to close and so it never exits. Anyone can help?

here is the main application snippet:

JDialog dialog = new JDialog(Main.getMainFrame(), true);
                    ProgressBarJPanel progressBarJPanel = new ProgressBarJPanel();
                    dialog.setContentPane(progressBarJPanel);
                    dialog.pack();
                    dialog.setVisible(true);
                    while ((val = bufferedInputStream.read()) != -1)
                    {
                        fileOutputStream.write(val);
                    }
                    progressBarJPanel.end();
                    dialog.setVisible(false);

and the incriminated class

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

public class ProgressBarJPanel extends JPanel
        implements ActionListener
{
    private JProgressBar progressBar;
    private Timer timer;
    public Thread updateThread;
    public final static int ONE_SECOND = 1000;
    private JTextArea taskOutput;
    private String newline = "\n";
    int timeNow= 0;
    int progress = 0;

    public ProgressBarJPanel()
    {
        super(new BorderLayout());

        progressBar = new JProgressBar(0, 100);
        progressBar.setValue(0);
        progressBar.setStringPainted(true);
        taskOutput = new JTextArea(5, 20);
        taskOutput.setMargin(new Insets(5,5,5,5));
        taskOutput.setEditable(false);
        taskOutput.setCursor(null);

        JPanel panel = new JPanel();
        panel.add(progressBar);

        add(panel, BorderLayout.PAGE_START);
        add(new JScrollPane(taskOutput), BorderLayout.CENTER);
        setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

        //Create a timer.
        timer = new Timer(ONE_SECOND, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                progressBar.setValue(progress);
                progress+=10;
                String s = "now at "+progress+"%";
                if (s != null) {
                    taskOutput.append(s + newline);
                    taskOutput.setCaretPosition(
                            taskOutput.getDocument().getLength());
                }
            }
        });

    }

    public void end()
    {
        timer.stop();
    }

    public void startProgress()
    {
        timer.start();
    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI()
    {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        JFrame frame = new JFrame("ProgressBarDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        JComponent newContentPane = new ProgressBarJPanel();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
                createAndShowGUI();
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}

-- EDIT

Here is the solution based on ben75 advice:

JDialog dialog = new JDialog(Main.getMainFrame(), true);
ProgressBarJPanel progressBarJPanel = new ProgressBarJPanel();
dialog.setContentPane(progressBarJPanel);
Runnable r = new Runnable(){
  public void run(){
             SwingUtilities.invokeLater(new Runnable()
             {
                    @Override
                    public void run()
                    {
                       progressBarJPanel.startProgress();
                       dialog.pack();
                       dialog.setVisible(true);
                    }
             });

            //this is the long running job
            while ((val = bufferedInputStream.read()) != -1)
            {
                fileOutputStream.write(val);
            }

            //here is the callback to UI thread
            SwingUtilities.invokeLater(new Runnable(){
                public void run(){
                    progressBarJPanel.end();
                    dialog.setVisible(false);
                }
           }
  };
Thread t = new Thread(r);
t.start();
mKorbel
  • 109,525
  • 20
  • 134
  • 319
dendini
  • 3,842
  • 9
  • 37
  • 74
  • 1
    An advice: Use a different thread for time consuming jobs than the one dealing with the UI... – ppeterka Mar 05 '13 at 08:59
  • How do the two communicate? Also in the above Netbeans warns me that updateThread.start(); shouldn't be inside the constructor, how do I put it outside?? – dendini Mar 05 '13 at 09:02
  • Check out [this example](http://stackoverflow.com/questions/15199091/progress-bar-java/15199220#15199220) – MadProgrammer Mar 05 '13 at 09:56

3 Answers3

3

You must do time consumming jobs in another thread (i.e. non UI thread) and at the end of the job callback the UI thread to close the dialog.

More or less how it can be coded:

JDialog dialog = new JDialog(Main.getMainFrame(), true);
ProgressBarJPanel progressBarJPanel = new ProgressBarJPanel();
dialog.setContentPane(progressBarJPanel);
dialog.pack();
dialog.setVisible(true);
Runnable r = new Runnable(){
      public void run(){
                //this is the long running job
                while ((val = bufferedInputStream.read()) != -1)
                {
                    fileOutputStream.write(val);
                }
                //here is the callback to UI thread
                SwingUtilities.invokeLater(new Runnable(){
                    public void run(){
                        progressBarJPanel.end();
                        dialog.setVisible(false);
                    }
               }
      };
Thread t = new Thread(r);
t.start();
ben75
  • 29,217
  • 10
  • 88
  • 134
  • the process still hangs because the progressJBar is created outside the Runnable and on creation it starts a thread of its own to update its progress every second. – dendini Mar 05 '13 at 09:47
  • in your updateThread : instead of updating the progressBar directly : do it in a callback for the UI thread (use `SwingUtilities.invokeLater(...)` as shown in my post). – ben75 Mar 05 '13 at 09:52
  • I updated my question based on your solution, it works but i'm still not sure whether the Thread t will actually always stop... – dendini Mar 05 '13 at 10:17
3

This would be an ideal job for a SwingWorker - do the upload in doInBackground, and have it call setProgress with an updated progress number every so often. Update the progress bar using a PropertyChangeListener and close the dialog in done, both of which are guaranteed to run on the event handling thread.

There is an example very close to what you need in the javadoc I linked to above.

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
0

Throw it all away and use a javax.swing.ProgressMonitorInputStream.

user207421
  • 305,947
  • 44
  • 307
  • 483