Search code examples
javaswingconcurrencyswingworker

Why is doAction() getting to done() early?


I have somewhat of a strange problem. I have a doAction() Swingworker with a for loop to remove rows from a JTable and a data base. If the for loop is large the done() method will fire off before the loop is completed, effectively stopping execution midway.

My code:

 public void doAction() throws Exception {
    int selectedRows[];
    int modelRows[];
    java.util.ArrayList<customRecord> condemned;
    customTableModel dataSource;
    boolean databaseDelete = false;
    Object uniqueID;
    int rowCount = 0;

    dataSource = (customTableModel)table.getModel();
    selectedRows = table.getSelectedRows();

    condemned = new java.util.ArrayList<customRecord>();
    modelRows = new int[selectedRows.length];

    for(int i=0;i<selectedRows.length;i++)
        modelRows[i] = table.convertRowIndexToModel(selectedRows[i]);

    selectedRows = null;

    for(int i= 0; i < modelRows.length; i++){
        uniqueID = dataSource.getUniqueID(modelRows[i]);

        System.out.println("Debug: spot 1, rowCount = " + rowCount + ", i = " + i + ", length = " + modelRows.length);

        if(uniqueID == null)
            dataSource.removeRow(modelRows[i]);
        else if(adminPrivileges){
            condemned.add(//Record being looked at);
            databaseDelete = true;

            System.out.println("Debug: spot 2, rowCount = " + rowCount + ", i = " + i);

            dataSource.removeRow(modelRows[i]);
            if(condemned.size() >= 100){
                database.clearData(condemned);
                condemned.clear();
            }

            System.out.println("Debug: spot 3, rowCount = " + rowCount + ", i = " + i);

        }

        System.out.println("Debug: spot 4, rowCount = " + rowCount + ", i = " + i + ", uniqueID = " + uniqueID.toString() + "\n");

        if(++rowCount >= 1000){
            System.out.println("rowCount = " + rowCount + ", in break");
            break;
        }
    }

    if(databaseDelete){
        if(condemned.size() > 0)
            database.clearData(condemned);
        loadData(table,database,filterParams);
    }
}

public void done() {

    System.out.println("Debug: in done()");

    table.setVisible(true);
    //Display warning message if the number of rows reached the limit
    if(rowCount >= 1000){
        // Display Limit Reached Warning;
    }
}

My output here looks like:

Debug: spot 1, rowCount = 0, i = 0, length 1006
Debug: spot 2, rowCount = 0, i = 0
Debug: spot 3, rowCount = 0, i = 0
Debug: spot 4, rowCount = 0, i = 0, uniqueID = 2608
.
.
.
Debug: spot 1, rowCount = 505, i = 505, length = 1006
Debug: spot 2, rowCount = 505, i = 505
Debug: spot 3, rowCount = 505, i = 505
Debug: spot 4, rowCount = 505, i = 505, uniqueID = 3073

Debug: spot 1, rowCount = 506, i = 506, length = 1006
Debug: in done()

If the big for loop is set to go from large to small like for(i = modelRows.length-1; i >= 0; i--) it will get a little further:

Debug: spot 1, rowCount = 0, i = 1005, length 1006
Debug: spot 2, rowCount = 0, i = 1005
Debug: spot 3, rowCount = 0, i = 1005
Debug: spot 4, rowCount = 0, i = 1005, uniqueID = 3073
.
.
.
Debug: spot 1, rowCount = 899, i = 106, length = 1006
Debug: spot 2, rowCount = 899, i = 106
Debug: spot 3, rowCount = 899, i = 106
Debug: spot 4, rowCount = 899, i = 106, uniqueID = 2174

Debug: in done()

How can I make/allow this doAction() method complete properly? Is there a maximum time a Swingworker will execute before the done() method is called?

EDIT

I believe I have confused myself(and probably everyone else) in saying that doAction() is a SwingWorker. doAction() is a method that is called from doInBackground() in a class that extends SwingWorker.

Code:

protected interface LengthyAction{

    public customActions getAction();

    public java.awt.Component getComponent();

    public void doAction() throws Exception;

    public void done();

}

private class DataSwingWorker extends javax.swing.SwingWorker{

    private LengthyAction action;

    public DataSwingWorker(LengthyAction targetAction){
        action = targetAction;
        setCursor(action.getComponent(),java.awt.Cursor.WAIT_CURSOR);
        if(listener != null)
            listener.actionPerformed(new java.awt.event.ActionEvent(**stuff**));
    }
    .
    .
    .
    @Override
    protected Object doInBackground() throws Exception {
        action.doAction();
        return null;            
    }

    @Override
    protected void done() {
        if(listener != null)
            listener.actionPerformed(new java.awt.event.ActionEvent(**stuff**));                
        action.done();
    }

}

I found the answer to my problem, and it really had nothing to do with my initial thoughts. Taking out the:

if(condemned.size() >= 100){
    database.clearData(condemned);
    condemned.clear();
}

In the big doAction() for loop makes everything work. It seems that this was causing the uniqueID's to skip forward 100 places, and I was hitting the end of the array before the end of the loop, giving a NullPointerException.

Thanks for all the help, it did end up putting me on the correct path.


Solution

  • I'll try to answer the questions that so far seem answerable based on the data you've posted:

    I have a doAction() Swingworker with a for loop to remove rows from a JTable and a data base ...... No, it is a doAction() method on an object that is called from a doInBackground() method.

    This is not good. If you are calling doAction() from within your SwingWorker's doInBackground() method that means that your code is flagrantly violating Swing threading rules by making Swing calls, especially calls that mutate the state of Swing components, from within a background thread. This may in fact be the cause or at least a major contributor to your intermittent problems with the running of this background thread.

    Is there a maximum time a Swingworker will execute before the done() method is called?

    No. It is called when the doInBackground() method has completed its actions and no exceptions have been found. A key issue is: are you checking for exceptions thrown during the SwingWorker's run? This would be tested by calling get() on your SwingWorker within a try/catch block and after it has completed its actions.

    How can I make/allow this doAction() method complete properly?

    Unfortunately it's hard to tell based on your current code since we can't compile or run it. If still stuck, create and post your minimal example program or SSCCE.

    My main recommendations:

    1. First and foremost, re-write all this code so that it strictly follows Swing threading rules. This has the greatest chance for solving your problem. Understand that SwingWorkers automatically allow PropertyChangeListener support, and so this may be a good way to communicate between the background thread and the Swing event thread.
    2. Next be sure to refactor the code so that your classes are small independently testable units, and then test each of them to the max by trying to make them fail in any way you can think of.