4

just learnd the SwingWorker and have a question
( i have search for a answer to this but non specifically address this setup)

Im creating a small server that will have like max simultaneous 2-3 connections only.
Im using a Jframe that have the inner class SwingWorker

In the SwingWorker doInBackground() i have the:

while(true) {
  Socket client_socket = listen_socket.accept();
  Connection con = new Connection(client_socket, "name");
  Future<Connection> future = es.submit(con , con ); 
  tasks.add(future);
 }

The Connection is a runnable and declared as a subclass in SwingWorker.

Before the runnable has completed it write an entry in the SQL.
How can then this runnable before it dies send a heads-up to Jframe event dispatch thread.
and the Jframewill check the SQL for the new entry and display it to user.

what is best to do:

1 - create an interface witch all runnable can send messages to Jframe event dispatch thread.

2 - use SwingWorker insted of runnables for all new connections and have Done() call a method in server SwingWorker that call method in Jframe using EventQueue.invokeLater..

3 - or use a PropertyChangeListener (somehow no sure)

4 - Let each runnables have s ref to Jframe and do EventQueue.invokeLater..

Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417
Erik
  • 5,039
  • 10
  • 63
  • 119

3 Answers3

1

I would go for the following: have a thread safe blocking queue or list in the parent thread that will be passed to worker threads. Once a task is done, a worker thread will post a message that contains the result entry ID into this blocking queue. The parent thread will block on the queue waiting for results from child threads. Whenever there is an element in the queue, the parent thread will take it and go get that data from DB and display it to user.

GETah
  • 20,922
  • 7
  • 61
  • 103
  • could work. parent in my case is a 'SwingWorker' and it's already blocking on 'doInBackground() listen_socket.accept()'. One could start a listening thread in parent holding the blocking queue volatile list and it will post messages to the 'Jframe' using EventQueue.invokeLater. Im no expert and this feels overkill to – Erik Nov 24 '11 at 14:21
  • Yep, I personally use this producer/consumer in a large distributed system and have not seen any problems with it so far – GETah Nov 24 '11 at 15:09
1

The SwingWorker docs are pretty clear. You should subclass SwingWorker and perform the long task in the doInBackground() method. You should update the UI in the done() method.

It really is as easy as that.

Edit:
To make it clearer. Assuming your Connection class extends SwingWorker it does not need to implement Runnable and you do not need to explicitly provide a thread pool to run the workers in. Just put the contents of the run() method in doInBackground().

Now your main loop looks something like this;

while (true) {
  Socket client_socket = listen_socket.accept();
  Connection con = new Connection(client_socket, "name");
  con.execute();
}

You seem to be submitting to an ExecutorService in your main loop. Is there a specific reason for this (note that a SwingWorker manages its own internal ThreadPoolExecutor for worker threads). Is it to limit the number of concurrent clients? If so there are other ways to accomplish this.

Qwerky
  • 18,217
  • 6
  • 44
  • 80
  • My server SwingWorker is using its 'done()' when server closes down. What about all 'runnables' i launch from 'doInBackground()' they dont have the 'done()' mechanisms – Erik Nov 24 '11 at 14:14
  • 1
    I think that he already knows about using a SwingWorker. His problem is one step beyond the basics of SwingWorker use. – Hovercraft Full Of Eels Nov 24 '11 at 14:18
  • 1
    @Erik: Are they Runnables or are the Futures? If the latter, then they do have a done method of sorts, especially if a FutureTask or something similar. – Hovercraft Full Of Eels Nov 24 '11 at 14:20
  • Right now they are Futures just for testing. i have no way of testing the Futures for completion result since server SwingWorker is blocking on the listen_socket.accept(); (maybe im wrong) – Erik Nov 24 '11 at 14:25
  • 1
    @Erik I see there two potential issues, `1)` opening Connection sould be separated thread or SwingWorker, if with succeed then start put/get `2)` not sure if is correct to define one Future inside another SwingWorker, but mabe is possible chaining two Futures, but required to implementing Propert&CHangeListener please read http://stackoverflow.com/q/7054627/714968 – mKorbel Nov 24 '11 at 14:38
  • See my update. Give your server class a synchronised method that updates the GUI with results, pass a ref to the server instance when you create your client swing workers, have the client swing workers call the server update GUI method from `done()`. – Qwerky Nov 24 '11 at 14:41
  • @Qwerky sounds promising will try it now. One thought is about the server listen_socket.accept();. will that not block the ref server synchronised method? – Erik Nov 24 '11 at 14:50
  • @Erik - No, synchronised server method is being called by the swing worker threads, not the server's main thread (which will spend 99.9% of its time blocking on `listen_socket.accept()`) – Qwerky Nov 24 '11 at 14:57
1

I tried to create an example of a SwingWorker that generates background worker threads, the results of which get published via a fixed pool ExecutorService and a CompletionService. I still have some questions regarding the thread safety of creating the worker threads inside of one thread, and then calling get on their futures inside of another thread (the SwingWorker background thread).

for example:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.swing.*;

@SuppressWarnings("serial")
public class TestSwingWorker extends JPanel {
   public static final int POOL_SIZE = 4;
   private JTextArea tArea = new JTextArea(10, 30);
   private JButton doItBtn;

   public TestSwingWorker() {
      doItBtn = new JButton(new AbstractAction("Do It!") {
         public void actionPerformed(ActionEvent ae) {
            swingWorkerRunning(true);
            MySwingWorker mySW = new MySwingWorker();
            mySW.execute();
            tArea.append("SwingWorker started\n");
         }
      });
      JPanel btnPanel = new JPanel();
      btnPanel.add(doItBtn);
      tArea.setEditable(false);
      tArea.setFocusable(false);

      setLayout(new BorderLayout());
      add(new JScrollPane(tArea), BorderLayout.CENTER);
      add(btnPanel, BorderLayout.SOUTH);
   }

   private class MySwingWorker extends SwingWorker<String, String> {
      @Override
      protected String doInBackground() throws Exception {
         ExecutorService execService = Executors.newFixedThreadPool(POOL_SIZE);
         final CompletionService<String> completionService = new ExecutorCompletionService<String>(
               execService);
         new Thread(new Runnable() {

            @Override
            public void run() {
               for (int i = 0; i < POOL_SIZE; i++) {
                  final int index = i;
                  completionService.submit(new Callable<String>() {
                     public String call() throws Exception {
                        Thread.sleep(2000 * index + 500);
                        return "Callable " + index + " complete";
                     }
                  });
                  try {
                     Thread.sleep(1000);
                  } catch (InterruptedException e) {
                  }
               }
            }
         }).start();

         for (int i = 0; i < POOL_SIZE; i++) {
            Future<String> f = completionService.take();
            publish(f.get());
         }

         return "Do in background done";
      }

      @Override
      protected void process(List<String> chunks) {
         for (String chunk : chunks) {
            tArea.append(chunk + "\n");
         }
      }

      @Override
      protected void done() {
         try {
            tArea.append(get() + "\n");
         } catch (InterruptedException e) {
            e.printStackTrace();
         } catch (ExecutionException e) {
            e.printStackTrace();
         } finally {
            swingWorkerRunning(false);
         }
      }
   }

   public void swingWorkerRunning(boolean running) {
      doItBtn.setEnabled(!running);
   }

   private static void createAndShowGui() {
      TestSwingWorker mainPanel = new TestSwingWorker();

      JFrame frame = new JFrame("TestSwingWorker");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

Corrections most welcome!

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373