UI does not update when using ProgressMonitorInputStream in Swing to control compression of a compressed file

I am working on a swing app which is based on an embedded H2 database. Since I do not want to link the database to the application (the db is updated frequently and I want new users of the application to start with a recent copy), I have implemented a solution that downloads a compressed copy of the db when the application is first launched and retrieved. Since the checkout process can be slow, I added a ProgressMonitorInputStream to show the progress of the checkout process - unfortunately, when the checkout starts, the progress dialog appears, but it doesn't update at all. Events seem to flow through the event dispatch thread. Here's the way:

public static String extractDbFromArchive(String pathToArchive) {
    if (SwingUtilities.isEventDispatchThread()) {
        System.out.println("Invoking on event dispatch thread");
    }

    // Get the current path, where the database will be extracted
    String currentPath = System.getProperty("user.home") + File.separator + ".spellbook" + File.separator;
    LOGGER.info("Current path: " + currentPath);

    try {
        //Open the archive
        FileInputStream archiveFileStream = new FileInputStream(pathToArchive);
        // Read two bytes from the stream before it used by CBZip2InputStream

        for (int i = 0; i < 2; i++) {
            archiveFileStream.read();
        }

        // Open the gzip file and open the output file
        CBZip2InputStream bz2 = new CBZip2InputStream(new ProgressMonitorInputStream(
                              null,
                              "Decompressing " + pathToArchive,
                              archiveFileStream));
        FileOutputStream out = new FileOutputStream(ARCHIVED_DB_NAME);

        LOGGER.info("Decompressing the tar file...");
        // Transfer bytes from the compressed file to the output file
        byte[] buffer = new byte[1024];
        int len;
        while ((len = bz2.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }

        // Close the file and stream
        bz2.close();
        out.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException ex) {
        ex.printStackTrace();
    }

    try {
        TarInputStream tarInputStream = null;
        TarEntry tarEntry;
        tarInputStream = new TarInputStream(new ProgressMonitorInputStream(
                              null,
                              "Extracting " + ARCHIVED_DB_NAME,
                              new FileInputStream(ARCHIVED_DB_NAME)));

        tarEntry = tarInputStream.getNextEntry();

        byte[] buf1 = new byte[1024];

        LOGGER.info("Extracting tar file");

        while (tarEntry != null) {
            //For each entry to be extracted
            String entryName = currentPath + tarEntry.getName();
            entryName = entryName.replace('/', File.separatorChar);
            entryName = entryName.replace('\\', File.separatorChar);

            LOGGER.info("Extracting entry: " + entryName);
            FileOutputStream fileOutputStream;
            File newFile = new File(entryName);
            if (tarEntry.isDirectory()) {
                if (!newFile.mkdirs()) {
                    break;
                }
                tarEntry = tarInputStream.getNextEntry();
                continue;
            }

            fileOutputStream = new FileOutputStream(entryName);
            int n;
            while ((n = tarInputStream.read(buf1, 0, 1024)) > -1) {
                fileOutputStream.write(buf1, 0, n);
            }

            fileOutputStream.close();
            tarEntry = tarInputStream.getNextEntry();

        }
        tarInputStream.close();
    } catch (Exception e) {
    }

    currentPath += "db" + File.separator + DB_FILE_NAME;

    if (!currentPath.isEmpty()) {
        LOGGER.info("DB placed in : " + currentPath);
    }

    return currentPath;
}

      

This method is called on the event dispatch thread (SwingUtilities.isEventDispatchThread () returns true), so the UI components must be updated. I have not implemented this as a SwingWorker since I need to wait for the fetch anyway before I can continue to initialize the program. This method is called before the main JFrame of the application is visible. I don't want the SwingWorker + based solution to change the listeners - I think ProgressMonitorInputStream is exactly what I need, but I think I am not doing anything right. I am using Sun JDK 1.6.18. Any help would be greatly appreciated.

+2


a source to share


2 answers


As long as you run the extraction process on EDT, it blocks all updates in the GUI, even on the progress monitor. This is exactly the situation in which it SwingWorker

will help.

In fact, you are blocking drawing, forcing the EDT to fetch the database. Any requests to update the GUI (such as calls repaint()

) will be queued, but actually redraw these updates are never triggered because the EDT is already busy.



The only way it can work is to offload the processing to another thread. SwingWorker

makes it easy to do this.

+4


a source


@Ash is right about SwingWorker

. You can use done()

to enable other GUI features when ready. The method done()

will be "executed in the Dispatch Event thread after the method completes doInBackground()

".



+2


a source







All Articles