How to chain multiple SwingWorkers

79 Views Asked by At

I've got a big background task with some intermediate points where a user input is required.

E.g. buttonLoad_actionPerformed --> load, verify validity --> if outdated, ask user whether to update. If yes --> update, if no --> stop.

For now, I've coded this like this:

// load worker
SwingWorker<Status,Object> swLoad=new SwingWorker<> () {
    Validity doInBackGround() {
        return load(file);
    }

    void done() {
        Validity validity=get();

        switch (validity) {
            case UPTODATE:
                return;
            case OUTDATED:
                int res=JOptionPane.showConfirmDialog("File obsolete. Do you want to update it ?");
                if (res != JOptionPane.YES_OPTION) 
                    return;
                    
                // update worker
                SwingWorker<Boolean,Object> swUpdate = new SwingWorker<>() {
                    Boolean doInBackGround() {
                        return update();
                    }

                    void done() {
                        Boolean success=get();
                        if (!success) ....
                    }
                };
                
                swUpdate.execute();
        }    
    }
};

swLoad.execute();

However this can be become pretty unreadable if multiple steps are required.

What's the best approach for chaining conditional/optional SwingWorkers ?

2

There are 2 best solutions below

4
matt On BEST ANSWER

I don't understand why you're doing anything in your done method.

Validity doInBackGround() {
    Validity validity = load(file);
    switch (validity) {
        case UPTODATE:
            return validity;
        case OUTDATED:
            int res= confirmOnEdt();
            if (res == JOptionPane.YES_OPTION) {
                Boolean success = update();
                //check the update and respond.
            }
    }
    return validity;
}

public int confirmOnEDT() throws InterruptedException{
    int[] container = {0};
    SwingUtilities.invokeAndWait( ()->{
            container[0] = JOptionPane.showConfirmDialog("File obsolete. Do you want to update it ?");
        }
    );
    return container[0];
}

One SwingWorker for the full set of tasks. The JOptionPane will block your background thread and wait for input. Also, you probably want to different return value than Validity.

1
TomDK On

Given that this is a bit of an obscure question, I'll give it a shot.

There are 2 items that stand out for me:

  1. The shown function/method is rather lengthy
  2. There is a lot of nesting going on

A simple solution to improve those is to split out code into separate methods. Example, move the actionable code from the "OUTDATED" case into a separate method. Sure it's longer, but easier to follow.

public loadInWorker() {
    SwingWorker<Status,Object> swLoad=new SwingWorker<> () {
        Validity doInBackGround() {
            return load(file);
        }

        void done() {
            onLoad(get());
            }
        }
    };

    swLoad.execute();
}



private onLoad(Validity validity) {
    switch (validity) {
        case UPTODATE:
            return;
        case OUTDATED:
            int res=JOptionPane.showConfirmDialog("File obsolete. Do you want to update it ?");
            if (res == JOptionPane.YES_OPTION) {
                updateInWorker(); 
            }
    }
}



private updateInWorker() {
    SwingWorker<Boolean,Object> swUpdate = new SwingWorker<>() {
        Boolean doInBackGround() {
            return update();
        }

        void done() {
            Boolean success=get();
            if (!success) {
                onUpdateFailure();
            }
        }
    };

    swUpdate.execute();
}


private onUpdateFailure() {
    ....
}

Edit + Opinion: I'll add that I've never used that worker API. Looking at it, it seems quite convoluted. In my last days of Java development, I relied on ThreadPools for tasks with a short execution time and dedicated Threads for tasks with long execution time. You could also write a wrapper for the SwingWorker call as you don't care about the internal types outside of it. Example using bog standard Threads:

public loadInThread() {
    new Thread(this::loadInThread, "Load Thread").start();
}


private loadInThread() {
    switch (load(file)) {
        case UPTODATE:
            return;
        case OUTDATED:
            int res=JOptionPane.showConfirmDialog("File obsolete. Do you want to update it ?");
            if (res == JOptionPane.YES_OPTION) {
                new Thread(this::updateInThread, "Update Thread").start();
            }
    }
}



private updateInThread() {
    if (!update()) {
        // Update Failure code goes here
    }
}