2

Using SwingWorker with FileVisitor not publishing processed info quickly; GUI hangs. I'd prefer it didn't and would like help with that problem.

Here's a brief outline of how I use SwingWorker with FileVisitor interface to search a Windows directory node for files matching user-specified criteria. :

  public class Main 
  {
    public static void main(String args[]) 
    {
      EventQueue.invokeLater( new Runnable() {
          @Override public void run() {
            gui = new GUI();
          }});
    }
  }
  //==============================================================
  public class GUI extends JFrame
  {
    public     GUI()
    {
      init();
      createAndShowGUI();
    }

     private void init()
     {
       dftTableModel = new DefaultTableModel(0 , 4);
         tblOutput     = new JTable(dftTableModel);
         tblOutput.setAutoResizeMode(AUTO_RESIZE_OFF);
         scrTblOutput     = new JScrollPane(tblOutput);
       dftTableModel.setColumnIdentifiers(new Object[]{"Date", "Size", "Filename", "Path"});

EDIT HAD I ONLY INCLUDED THESE TWO LINES, PROBLEM MAY HAVE BEEN SOLVED IMMEDIATELY

       tca = new tablecolumnadjuster.TableColumnAdjuster(tblOutput);
       tca.setDynamicAdjustment(true);

     }

      private static void btnSearchActionPerformed(ActionEvent evt) 
      {
        TASK task = new TASK();
        task.execute();      
      }
    }
  }
  //==============================================================

  public class TASK extends SwingWorker<Void,String>
  {

    private class rowRec{
      String date;
      int size;
      String filename;
      String pathname;

      private rowRec(String d, int s, String f, String p) 
      {
        date = d; 
        size = s; 
        filename = f; 
        pathname = p;
      }
    }

    FV fv;

    TASK() {        fv = new FV();        }

    //-------------- inner class   

      class FV implements FileVisitor<Path>
      {
        // When walk begins, internal FileVisitor code makes this routine
        // loop until no more files are found OR disposition = TERMINATE.

        public FileVisitResult visitFile(Path f, BasicFileAttributes a) throws IOException 
        {
          if(f.getFileName().toString().toLowerCase().matches(fPatt.toLowerCase().trim()))
          {

             publish(s); 

             if(++k > parseInt(GUI.txtMaxMatches.getText()))

                disposition = TERMINATE;
                publish("Stopped at max. records specified");
          }
          return disposition;
        }
      }
    //----------------

    private  void report(String s)
    {
      rowData = new rowRec(date, isize, filename, path);
      dftTableModel.addRow(new Object[]{rowData.date, rowData.size, rowData.filename, rowData.pathname});
    }

    @Override
    protected void process(List<String> chunks)
    {
      chunks.stream().
        forEach
        (
           (chunk) -> 
           {
             report(chunk);
           }
      );
      kc += chunks.size();
      System.out.println(kc); // *********************************
    }

    @Override
    public Void doInBackground() throws Exception 
    {
        disposition = FileVisitResult.CONTINUE;
        Files.walkFileTree(GUI.p ,fv);
    }
  }

GUI starts a new instance of SwingWorker, whose job is to display (in a JTable) file info found by the instance of TASK that it starts. TASK instantiates FileVisitor, and walkFileTree begins. Every matching file found in the visitFile method is published for SwingWorker to process.

It works great most of the time, but if there is a ton of matching files, the GUI becomes unresponsive for several seconds; meanwhile, plenty of reading and displaying has occurred, and the UI is updated every few thousand file reads.

Despite using SwingWorker to fill the JTable in the background, apparently (I'm guessing) too much matching files info comes too fast to keep up.

Here's why I say that:

Even though visitFile has a counter which signals to TERMINATE, process apparently is far behind in adding records to the JTable. The println inside it shows that, as time passes, the number of chunks passed varies considerably, depending on rate of FileVisitor finding matches:

41
81
138
250
604
1146
...
1417
1497
1590
1670
1672
1676
1680
1682
1692
1730
1788
1794
1797
1801
1807
1820
1826
1829
1847
1933
2168
10001

After visitFile terminated, process had to send (10001-2168), or 7833 records to the JTable, and it took a long time, and the GUI was unresponsive much of the time. In fact, if the max. matches is (the ridiculous) 10,000, the program hangs for many MINUTES, but 10,000 records are in the JTable.

I don't know what to do about the unresponsiveness. I'd like to be able to hit the STOP button and have the program stop. Or be able to X (close) the window. No way.

Am I not using SwingWorker correctly? I can't make the tree walk be based on SwingWorker since there's no loop that I have available to me (it's internal).

P.S. While apparently hung, javaw is taking a solid 25% of CPU time and incrementing its memory allocation by about 16K per second, as process plods along, until it finally publishes the last chunk.

enter image description here

EDIT

I may have found help here.

But, gee, close??

I've highlighted my questions.

Community
  • 1
  • 1
DSlomer64
  • 4,234
  • 4
  • 53
  • 88

2 Answers2

2

I don't know the solution to your problem, but I do know that this should not be done within the SwingWorker or its inner classes:

GUI.txtMaxMatches.getText())

You should get this information on the EDT and pass it into your SwingWorker via its constructor.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • @Hover--This program began LONG ago, when I had HORRIBLE habits. I gave up on it until a week or so ago when Windows 7 search couldn't find a .doc that I KNEW existed. Old version of this found it. So I decided to revise it. I've broken most bad habits but haven't tried to fix them in this project because it seemed so close to working. But no doubt not working at least partly because of the bad habits. I may have a major rewrite facing me. Not what I want to say/hear. The link I edited into my Question may get me the Answer. Thanks for advice. – DSlomer64 Aug 20 '15 at 16:39
  • As it stands, inside the `FileVisitor` method `visitFile`, I'm writing to a `JTable` defined in the GUI. Is that bad and causing the unresponsiveness? If so, consider this: The `TASK` class has `FileVisitor` as a subclass. Could/should I remove the `JTable` from the GUI and put it inside a `JFrame` in the `TASK` (or `FileVisitor`) class to improve responsiveness? This would put the `JTable` inside a `JFrame` in the `SwingWorker` and I don't know if that's just wrong. – DSlomer64 Aug 20 '15 at 18:19
  • I'll answer my last question: " Could/should I remove the JTable from the GUI and put it inside a JFrame in the TASK (or FileVisitor) class to improve responsiveness?" NO. No change. – DSlomer64 Aug 20 '15 at 18:56
0

The responsiveness problem is solved in ONE LINE OF CODE.

// tca.setDynamicAdjustment(true);

Refer to edit in original question.

DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • 3
    Which is why you should ALWAYS post a [SSCCE](http://sscce.org/) when you ask a question. Until a problem is solved you don't know what the relevant code to post is. If we don't have all the information we are just wasting our time guessing. – camickr Aug 20 '15 at 19:46
  • Given how much time I spent making the outline and all the text in the question, It may have taken less effort to make a SSCCE. Owing to the complexity (main creates GUI creates TASK which extends SwingWorker and creates FV which implements FileVisitor which publishes...), I never considered doing so. I do apologize for wasting anyone's time. I was hoping that the outline would show some obvious bad practice and that no one would try to go through much other than that. Now I feel a lot more moronic. A little common sense on my part would have spared us all the time spent. – DSlomer64 Aug 20 '15 at 20:09
  • 2
    The point of a SSCCE is to simplify the problem to see if you understand the concept. All you needed to do was create a Swing worker that contained a loop. Inside the loop you could sleep for a fixed number of ms.. Then inside that loop you "publish" a batch of data and see how the GUI responds. So your first test might be with a batch size of 10. If the GUI is responsive then up the batch size to 100 and so on until it becomes unresponsive. Again the point of a SSCCE is simplify the problem to see if can be reproduced. – camickr Aug 20 '15 at 20:15
  • I know the point. You make it sound so easy, but the project has five packages and a total of twelve classes and an external library, to say nothing of two major concepts: FileVisitor and SwingWorker. SSCCE seemed far off. But I stand corrected. Never ask a question without a SSCCE. Thanks for all your previous help. I hope for more in the future.\ – DSlomer64 Aug 20 '15 at 20:54
  • 3
    `the project has five packages and a total of twelve classes` - foreget about those packages and classes. The point of a SSCCE is to understand the question, not your current implementation. Your basic question is `how fast can a JTable be updated from a SwingWorker while keeping the GUI responsive`. Work on one concept at a timer. Understand the SwingWorker and its publishing abilities in a tight loop. Once you prove the concept works, then you add the FileVisitor. – camickr Aug 20 '15 at 21:12
  • I surrender. Mea culpa. – DSlomer64 Aug 21 '15 at 00:09