3

Below is the compiled program replica of actual problem code,

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class Dummy {

    public static boolean getUserCheck(int size, boolean Check) {
        if (Check) {
            int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
                    "Warning", 0);
            if (ret > 0) {
                System.out.println("User said No: " + ret);
                return false;
            } else if (ret <= 0) {
                System.out.println("user said Yes: " + ret);
                return true;
            }
        }
        return true;
    }

    public static void workerMethod1() {
        System.out.println("am worker method 1");
    }

    public static void workerMethod2() {
        System.out.println("am worker method 2");
    }

    public static void main(String[] args) {
        System.out.println("mainthread code line 1");
        int size = 13;
        boolean thresholdBreach = true;

        if (getUserCheck(size, thresholdBreach)) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    workerMethod1();
                }
            });

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    workerMethod2();
                }
            });
        }
        System.out.println("mainthread code line 2");
        System.out.println("mainthread code line 3");
    }
}

where i would like to run the if{} block in main() on separate thread. Because these 2 lines,

        System.out.println("mainthread code line 2");
        System.out.println("mainthread code line 3");

need not wait for completion of if(){} block

Another problem is, experts recommend to run confirm-dialog methods on event thread.

int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
                    "Warning", 0);

Please help me!!!!

overexchange
  • 15,768
  • 30
  • 152
  • 347
  • 2
    yes you can, but why would you want to? – Jonny Henly Dec 23 '14 at 04:06
  • 2
    This may be an XY Problem type question, but regardless of whether it is or isn't, the question could benefit from a lot more context. Why are you considering doing this? What overall goal are you trying to achieve? – Hovercraft Full Of Eels Dec 23 '14 at 04:06
  • 1
    Why do you think you couldn't nest it to an arbitrary depth? The same points you consider when testing any code. Covering all of the points of testing software is too broad a question. – Elliott Frisch Dec 23 '14 at 04:09
  • @JonnyHenly please check my edit now, i want to do this because i want to generate an extra confirm dialog because the complete application(which runs on n threads) gets blocked due message dialog, if i dont invoke this dialog on thread. – overexchange Dec 23 '14 at 04:18
  • 1
    Do you mean`JOptionPane.showConfirmDialog()`? You're already running on the EDT inside the first `invokeLater`, just call that method directly, you don't need to make a new runnable. – markspace Dec 23 '14 at 04:26
  • 1
    I am now more confused about this question than ever. All dialogs **should** be run on the event thread, and so using `SwingUtilities.invokeLater(...)` makes absolutely no sense in this situation. Please be more concrete in your description of your requirements. – Hovercraft Full Of Eels Dec 23 '14 at 04:27
  • I *think* the OP is thinking that he'll block the (current) EDT if he waits for data. However, the the dialog classes have special code to prevent this from happening. It's tricky but it works. The OP is on the right track, it's just Swing anticipated this and added stuff to make it work anyway. – markspace Dec 23 '14 at 04:30
  • So all this mish-mash is nothing more than an attempt to use a modal dialog? Maybe you're right, but I'd sure like a clear description from the original poster. – Hovercraft Full Of Eels Dec 23 '14 at 04:31
  • I think you're exactly right: it's just one person trying to get their head around how to use a modal dialog window in Swing. Nothing else. (Now watch he OP corrected me ;)). – markspace Dec 23 '14 at 04:32
  • @markspace yes `JOptionPane.showConfirmDialog()` – overexchange Dec 23 '14 at 04:32
  • But i see the problem after modification, because when i placed `line 4` which is like final `ClientxxxModel table = new ClientxxxModel(xxxxList, xxxxConfig, yyyyList.size(), abcd.getRequest().getidnum(), this);` gives an error `The constructor ClientxxxModel(List,Config, int, double,newRunnable(){})` is undefined. I think because of this variable. May be i need to assign `this` to some temorary reference right? – overexchange Dec 23 '14 at 04:39
  • @markspace i agree that, whatever hover says is correct, that dialog should be invoked on eventthread, which actually am not doing in `if()`, i will launch dialog which is in `if()` block on event thread and also launch `line 1` to `line x` on new thread using `Thread` class? i only can't launch only `confirm dialog` on event thread because the output of it will execute `line1` to `linex` – overexchange Dec 23 '14 at 04:46
  • More concrete background information, more specifics about just what you're trying to do please. – Hovercraft Full Of Eels Dec 23 '14 at 04:48
  • @HovercraftFullOfEels i think the query is very clear now – overexchange Dec 23 '14 at 04:59
  • I'm sorry but your question still suffers from crystal ball syndrome, the assumption that we can see code not shown and understand concepts not fully explained. Your question (at least to me) is still very cryptic, still very lacking in concrete information. – Hovercraft Full Of Eels Dec 23 '14 at 05:04
  • You have, `SwingUtilities.invokeLater(.non-gui code..);`, but that should be **gui** code inside of the Runnable that gets passed into `invokeLater(...)`, not **non**-gui code. – Hovercraft Full Of Eels Dec 23 '14 at 05:20
  • I'll one up you for trying to post a runnable version, but could you put some GUI code in there so we can see exactly what goes on in the GUI. Also, sorry, but I'm still confused by your worker methods. They're not being called in background worker threads as one would expect, but instead are being queued onto the Swing event thread. Please clarify. I think that we'll be able to get to the bottom of solving what you're trying to do soon. Much luck! – Hovercraft Full Of Eels Dec 23 '14 at 11:23
  • @HovercraftFullOfEels That GUI is the table of results which we get from http query, Before getting this results from webserver, we have the row count(size = 13) of results, so we want to ask user that, if user is ok with the delay(big number rowcount) caused for querying the results thru dialog(yes/no), On "No", we stop webserver to avoid going very busy ): worker methods are weirdly called in swing event threads, but this is how existing code is): – overexchange Dec 23 '14 at 11:34
  • @HovercraftFullOfEels so the old(existing) code is without `if{}` block. am planning to add new changes with just `if{}` block in main() and `getUserCheck()` method. worker methods are actually placing http queries internally. Hope everything is clear now. – overexchange Dec 23 '14 at 13:05

2 Answers2

3

JOptionPane is a Swing method and should be called on the EDT, the Event Dispatch Thread, and only on this thread, and so it suggests that all your code above should be on the EDT, and that most of your SwingUtilities.invokeLater(new Runnable() calls are completely unnecessary. The only necessary ones will be the main one, where you launch your Swing GUI code, and any areas where Swing calls need to be made from within background threads. Again, if any of the above code is being made within background threads, then the JOptionPane should not be in that thread.

For more specific information in this or any other answer, please provide more specific information in your question. Let's end all confusion. The best way to get us to fully and quickly understand your problem would be if you were to to create and post a minimal example program, a small but complete program that only has necessary code to demonstrate your problem, that we can copy, paste, compile and run without modification.

I have a sneaking suspicion that a decent refactoring along MVC lines could solve most of your problems. Your code is very linear with its lines of code that must follow one another and its if blocks, and it is also tightly coupled with your GUI, two red flags for me. Perhaps better would be less linear code, more event and state-driven code, code where your background code interacts with the GUI via observer notification, and where the background code likewise responds to state changes in the GUI from control notification.


Your control needs two SwingWorkers, one to get the row count and the other to get the rest of the data if the user decides to do so. I'd add a PropertyChangeListener to the first SwingWorker to be notified when the row count data is ready, and then once it is, present it to the view for the user to select whether or not to proceed. If he decides to proceed, I'd then call the 2nd SwingWorker to get the main body of the data.


For example, a rough sketch of what I'm talking about:

import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;

import javax.swing.*;

@SuppressWarnings("serial")
public class SwingWorkerFooView extends JPanel {
   private static final int PREF_W = 400;
   private static final int PREF_H = 300;
   private JProgressBar progressBar;
   private JDialog dialog;

   public SwingWorkerFooView() {
      add(new JButton(new ButtonAction("Foo", this)));
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   public boolean showOptionGetAllData(int numberOfRows) {
      String message = "Number of rows = " + numberOfRows + ". Get all of the data?";
      String title = "Get All Of Data?";
      int optionType = JOptionPane.YES_NO_OPTION;
      int result = JOptionPane.showConfirmDialog(this, message, title, optionType);

      return result == JOptionPane.YES_OPTION;
   }

   public void showProgressBarDialog() {
      progressBar = new JProgressBar();
      progressBar.setIndeterminate(true);
      Window window = SwingUtilities.getWindowAncestor(this);
      dialog = new JDialog(window, "Hang on", ModalityType.APPLICATION_MODAL);
      JPanel panel = new JPanel();
      panel.add(progressBar);
      dialog.add(panel);
      dialog.pack();
      dialog.setLocationRelativeTo(this);
      dialog.setVisible(true);
   }

   public void closeProgressBarDialog() {
      dialog.dispose();
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("SwingWorkerFoo");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new SwingWorkerFooView());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

@SuppressWarnings("serial")
class ButtonAction extends AbstractAction {
   Workers workers = new Workers();
   private SwingWorker<Integer, Void> firstWorker;
   private SwingWorker<List<String>, Void> secondWorker;
   private SwingWorkerFooView mainGui;

   public ButtonAction(String name, SwingWorkerFooView mainGui) {
      super(name);
      this.mainGui = mainGui;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      firstWorker = workers.createFirstWorker();
      firstWorker.addPropertyChangeListener(new FirstPropertyChangeListener());
      firstWorker.execute();
      mainGui.showProgressBarDialog();
   }

   private class FirstPropertyChangeListener implements PropertyChangeListener {

      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
            mainGui.closeProgressBarDialog();
            try {
               int numberOfRows = firstWorker.get();
               boolean getAllData = mainGui.showOptionGetAllData(numberOfRows);
               if (getAllData) {
                  secondWorker = workers.createSecondWorker();
                  secondWorker.addPropertyChangeListener(new SecondPropertyChangeListener());
                  secondWorker.execute();
                  mainGui.showProgressBarDialog();
               } else {
                  // user decided not to get all data
                  workers.cleanUp();
               }
            } catch (InterruptedException e) {
               e.printStackTrace();
            } catch (ExecutionException e) {
               e.printStackTrace();
            }
         }
      }
   }

   private class SecondPropertyChangeListener implements PropertyChangeListener {
      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
            mainGui.closeProgressBarDialog();
            try {
               List<String> finalData = secondWorker.get();

               // display finalData in the GUI
            } catch (InterruptedException e) {
               e.printStackTrace();
            } catch (ExecutionException e) {
               e.printStackTrace();
            }
         }

      }
   }

}

class Workers {
   // database object that may be shared by two SwingWorkers
   private Object someDataBaseVariable;
   private Random random = new Random(); // just for simulation purposes

   private class FirstWorker extends SwingWorker<Integer, Void> {
      @Override
      protected Integer doInBackground() throws Exception {

         // The Thread.sleep(...) is not going to be in final production code
         // it's just to simulate a long running task
         Thread.sleep(4000);

         // here we create our database object and check how many rows there are
         int rows = random.nextInt(10 + 10); // this is just for demonstration purposes only

         // here we create any objects that must be shared by both SwingWorkers
         // and they will be saved in a field of Workers
         someDataBaseVariable = "Fubar";

         return rows;
      }
   }

   private class SecondWorker extends SwingWorker<List<String>, Void> {
      @Override
      protected List<String> doInBackground() throws Exception {

         // The Thread.sleep(...) is not going to be in final production code
         // it's just to simulate a long running task
         Thread.sleep(4000);

         List<String> myList = new ArrayList<>();
         // here we go through the database filling the myList collection

         return myList;
      }
   }

   public SwingWorker<Integer, Void> createFirstWorker() {
      return new FirstWorker();
   }

   public void cleanUp() {
      // TODO clean up any resources and database stuff that will not be used.
   }

   public SwingWorker<List<String>, Void> createSecondWorker() {
      return new SecondWorker();
   }
}

The key to all of this is to not to think in a linear console program way but rather to use observer design pattern, i.e., listeners of some sort to check for change of state of both the GUI and the model.

It's essentially:

  • create worker
  • add observer to worker (property change listener)
  • execute worker
  • show progress bar dialog or notify user in some way that worker is executing.

  • The listener will be notified when the worker is done, and then you can query the worker (here via the get() method call) as to its end result.

  • Then the progress dialog can be closed
  • And the view can display the result or get additional information from the user.
Community
  • 1
  • 1
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • i think i have added more information to the query, please have a look – overexchange Dec 23 '14 at 05:19
  • this is the [compiledprogram](https://github.com/shamhub/CS61B_Fall2006/blob/master/JavaCode/src/dummy.java) where i would like to run the `if{}` block on separate thread, because `mainthread code line 2` and `mainthread code line 3` in main thread need not wait for the `if{}` block. another problem with this code is, am not launching dialog on event thread, as you said above:"All dialogs should be run on the event thread" – overexchange Dec 23 '14 at 08:14
  • @overexchange: Please let me know if you've read my changes and if you have any questions about them. – Hovercraft Full Of Eels Dec 24 '14 at 12:33
  • am not sure, why your created `class Workers` though it is a good design to see, because we need to only analyse `if{}` block and only improve that corresponding code. – overexchange Dec 24 '14 at 14:19
  • @Over, I created the class so that the two SwingWorkers can be held by one object and thus more easily share information. They will likely need to share a database related object. The key to my answer though is the listener structure. Regardless of the specific code you use, you will need this. – Hovercraft Full Of Eels Dec 24 '14 at 16:03
0

Yes; SwingUtilities.invokeLater() simply places your runnable on the AWT event queue to be processed later, and it is safe to do so at any time.

Vitruvie
  • 2,327
  • 18
  • 25