I have a JList, the contents of which are updated asynchronously by a different thread. The way I update the JList is by updating the model:
private final DefaultListModelstreamedDataModel; ... streamedDataModel.addElement(element);
The only problem is, if this is not done in the EventDispatchThread, the UI becomes unresponsive, and starts behaving weird.
The way to solve this to use the SwingWorker.
I have a Jini server, which streams data to a Jini client, in this case, a Swing JFrame. I have a JList to which I publish the data as and when it becomes available.
Its a simple streaming service:
public interface BankDetailStreamingService extends Remote { void streamAllBankDetails(RemoteDataListenerbankDetailRemoteListener) throws RemoteException; }
At its heart is the interface RemoteDataListener, on to which the server publishes data as it becomes available.
public interface RemoteDataListenerextends Remote { void newData(T data) throws RemoteException; void endOfData() throws RemoteException; }
The key is to implement the interface RemoteDataListener along with the SwingWorker as shown below as an inner class in the JFrame:
class StreamingTask extends SwingWorkerimplements RemoteDataListener { private final DefaultListModel streamedDataModel; public StreamingTask(DefaultListModel streamedDataModel) { this.streamedDataModel = streamedDataModel; } @Override protected Void doInBackground() { try { Exporter exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0), new BasicILFactory()); @SuppressWarnings("unchecked") RemoteDataListener exportedRemoteDataListener = (RemoteDataListener ) exporter.export(this); bankDetailStreamingService.streamAllBankDetails(exportedRemoteDataListener); } catch (RemoteException e) { throw new RuntimeException(e); } return null; } @Override protected void process(List chunks) { for (BankDetail element : chunks) { streamedDataModel.addElement(element); } // scroll to end listStreamedData.ensureIndexIsVisible(streamedDataModel.size() - 1); } @Override public void newData(BankDetail data) throws RemoteException { publish(data); } @Override public void endOfData() throws RemoteException { } @Override protected void done() { prgStreamingData.setIndeterminate(false); JOptionPane.showMessageDialog(RmiStreamingDemoFrame.this, "All data streamed successfully", "End of data", JOptionPane.INFORMATION_MESSAGE); btnStartStreaming.setEnabled(true); setCursor(null); } }
We invoke the long running service in the doInBackground() method and add itself as a listener. When new data is received in the newData() method, we immediately call the publish() method, which delegates it to the process() method. The process() method is invoked within the eventDispatcherThread. This ensures that any updates made to the model of the JList is reflected on the UI. So, the code to update Swing components reside here. Also note that the done() method is called by the SwingWorker after the processing thread is finished.
Run the SpringNonSecureRmiServer to start the Reggie and the Jini Server. After the Jini Server starts up, run the RmiStreamingDemoFrame.
The sources can be found here: https://github.com/paawak/blog/tree/master/code/jini/unsecure/streaming-with-jini
There are 3 Maven projects under that: