Swing Worker Delay - java

I have a button click event that will fire a swing worker thread which in return fire another thread to do a long calculation including writing a file. Then this file is read to draw some graphics. However drawing part never happens if i don't add a delay in between.. (It says file not found although the file is there..What is the better way to fix this without adding a delay..
private void buttonFragmentActionPerformed(java.awt.event.ActionEvent evt) {
try
{
ESIPlusFragmenterWorker epfw = new ESIPlusFragmenterWorker(10, sdfFile, cidSpectrum);
epfw.execute();
Thread.sleep(1000);
holder.molTable1.drawMolViewPanel(currDir+sep+"esiFragments"+sep+"esiFrag.sdf");
}
catch (Exception e)
{
e.printStackTrace();
}
}
Swing Worker
public class ESIPlusFragmenterWorker extends SwingWorker<Void, Void>{
int mzppm_;
String SDF_;
String spectrum_;
Double mion_;
MolTable holder_;
ESIPlusFragmenterWorker(int mzppm, String SDF, String spectrum)
{
mzppm_ = mzppm;
SDF_ = SDF;
spectrum_ = spectrum;
}
#Override
protected Void doInBackground() {
try
{
Molecule mol;
MolImporter importer = new MolImporter(SDF_);
ExecutorService te = Executors.newFixedThreadPool(1);
while ((mol = importer.read()) != null)
{
Runnable epf = new ESIPlusFragmenter(mol, spectrum_, mzppm_);
Thread t = new Thread(epf);
te.execute(epf);
}
importer.close();
te.awaitTermination(10, TimeUnit.MINUTES);
}
catch (Exception e)
{
//
}
finally
{
return null;
}
}
#Override
protected void done() {
try {
//
} catch (Exception e) {
//e.printStackTrace();
}
}
}

Never, never, never call Thread.sleep(...) on the EDT as this will put your entire GUI to sleep. And besides, what if you estimate wrong, and the background process takes longer than your sleep delay time?
One possible solution is to add a PropertyChangeListener to the SwingWorker and listen on the "state" property for the SwingWorker.StateValue to be SwingWorker.StateValue.DONE, then do your drawing.
e.g.
private void buttonFragmentActionPerformed(java.awt.event.ActionEvent evt) {
try {
ESIPlusFragmenterWorker epfw = new ESIPlusFragmenterWorker(10,
sdfFile, cidSpectrum);
epfw.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if ("state".equals(pcEvt.getPropertyName())) {
if (pcEvt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
holder.molTable1.drawMolViewPanel(currDir + sep
+ "esiFragments" + sep + "esiFrag.sdf");
}
}
}
});
epfw.execute();
So what this does is waits until the SwingWorker has completed its business before calling the code inside of the listener.
Another option is to call your holder.molTable1.drawMolViewPanel inside of the SwingWorker's done() method, and this will work too, but by doing it as noted above with a PropertyChangeListener, the SwingWorker doesn't have to have any knowledge about the code called in the listener (as opposed to using SwingWorker's done() method), and this may allow for looser coupling.

Related

How can I implement a method that returns a result to Event Dispatch Thread?

I have the following method:
public Object someMethod(Object param) {
return performLongCalculations();
}
Some time consuming calculations I placed in a separate method:
private Object performLongCalculations() {
...
}
The problem is that it returns some calculation result. These calculations are performed in the EDT and lead to freezing the UI.
I tried to solve it with the following way:
public Object someMethod(final Object param) {
Object resultObject = new Object();
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<Object> future = executorService.submit(new Callable<Object>() {
#Override
public Object call() {
return performLongCalculations(param);
}
});
executorService.shutdown();
try {
resultObject = future.get();
} catch (InterruptedException | ExecutionException e) {
// ...
}
return resultObject;
}
But the thread is blocked on the call to future.get(); until the calculations are completed. And I think it also runs in EDT.
Next I tried to use SwingWorker:
public Object someMethod(final Object param) {
SwingWorker<Object, Void> worker = new SwingWorker<Object, Void>() {
#Override
protected Object doInBackground() {
return performLongCalculations(param);
}
#Override
protected void done() {
try {
get();
} catch (InterruptedException e) {
}
catch (ExecutionException e) {
}
}
};
worker.execute();
// what should I return here?
}
Here I need to return the result, but it returns before the end of the thread that runs in parallel with EDT.
Your question essentially is:
How can I return a value directly into my Swing GUI from a method where the solution is obtained from long-running code called within a background thread?
And the answer, succinctly, is: you don't.
Trying to do this in any way, shape or fashion would mean forcing the background thread to block the GUI event thread until the background thread has completed its task, and if the task takes any appreciable time, then this will always cause the GUI to freeze. Instead, you must extract the information when the background thread has completed, and not get the result from the method itself. This is usually done using a call-back mechanism of some sort.
For example, in this code:
public void someMethod(final Object param) {
SwingWorker<Object, Void> worker = new SwingWorker<Object, Void>() {
#Override
protected Object doInBackground() {
return performLongCalculations(param);
}
#Override
protected void done() {
try {
Object something = get();
// (A)
} catch (InterruptedException e) {
// do handle these exceptions!
}
catch (ExecutionException e) {
// do handle these exceptions!
}
}
};
worker.execute();
// (B)
}
You would give the result to the GUI at location (A) not as a return value from the method, location (B)
Alternatively, you could attach a PropertyChangeListener to the SwingWorker, listen for when Worker's state property changes to SwingWorker.StateValue.DONE, and then call .get() on the worker and push the value returned onto the GUI. This is my preferred way of doing this because it usually allows for lower code coupling.

Populate JList with threads

I want JList to be populated with multiple threads.
I tried this way but jlist is empty.
It would be good if jlist was updated on the fly
There are two threads, the other one loads in anouther direction
new Thread(new Runnable() {
#Override
public void run() {
for(i=0; i<cells.size()/2; i++){
System.out.println("thread");
try{
HtmlPage p = client.getPage("https://tbilisi.embassytools.com/en/slotsReserve?slot="+cells.get(i).getAttribute("data-slotid"));
pages.add(p);
if(!p.getUrl().toString().contains("slotsReserve"))
model.add(i,p.getUrl().toString());
}
catch (Exception e){
e.printStackTrace();
}
}
}
});
list1.setModel(model)
Thanks in advance
UPDATE*
So I fixed by using SwingWorker
Swing is a single threaded framework, that is, it is expected that all updates and modifications to the UI are done from within the context of the Event Dispatching Thread.
Equally, you should do nothing in the EDT that might block or otherwise prevent it from processing the Event Queue (like downloading content from the web).
This raise a conundrum. Can't update the UI outside the EDT, need to use some kind of background process to execute time consuming/blocking tasks...
So long as the order of items is unimportant, you would use multiple SwingWorkers in place o of the Threads, for example...
DefaultListModel model = new DefaultListModel();
/*...*/
LoadWorker worker = new LoadWorker(model);
worker.execute();
/*...*/
public class LoaderWorker extends SwingWorker<List<URL>, String> {
private DefaultListModel model;
public LoaderWorker(DefaultListModel model) {
this.model = model;
}
protected void process(List<String> pages) {
for (String page : pages) {
model.add(page);
}
}
protected List<URL> doInBackground() throws Exception {
List<URL> urls = new ArrayList<URL>(25);
for(i=0; i<cells.size()/2; i++){
try{
HtmlPage p = client.getPage("https://tbilisi.embassytools.com/en/slotsReserve?slot="+cells.get(i).getAttribute("data-slotid"));
pages.add(p);
if(!p.getUrl().toString().contains("slotsReserve")) {
publish(p.getUrl().toString());
urls.add(p.getUrl());
}
}
catch (Exception e){
e.printStackTrace();
}
}
return urls;
}
}
This allows you execute your blocking/long running in the backround (doInBackground) and publish the results of this method which are then processed within the context of the EDT...
See Concurrency in Swing for more details
Swing is not thread safe you should use SwingUtilities to run multiple threads updating swing.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
doWhateverYouWant();
}
});
read more

Kill a process created in a different class

Say I run a program in my Process class, something like:
Process p1 = Runtime.getRuntime().exec("setup.exe");
try {
p1.waitFor();
} catch (InterruptedException e) {
//Whatever
}
And then I also have a cancel button in my GUI class, something along the lines of:
private void cancelButtonActionPerformed(ActionEvent e) {
//Need to interrupt the thread from here!
System.exit(0);
}
Simply exiting the program out as is leaves my setup.exe process I created over in Process.java running, and that's a problem. Normally I'd call p1.destroy(), but I'm having issues making the connection in between the two classes.
Any suggestions would be much appreciated!
EDIT:
private void beginThread() {
SwingWorker<String, Void> myWorker = new SwingWorker<String, Void>() {
#Override
protected String doInBackground() throws Exception {
//Do some stuff
}
return null;
}
};
myWorker.execute();
}
Assuming your run the process in a separate thread, you can call processThread.interrupt() from your GUI class.
You then simply need to modify the try/catch to:
try {
p1.waitFor();
} catch (InterruptedException e) {
Sys.err(e.toString());
//don't ignore the exception:
p1.destroy();
Thread.currentThread.interrupt(); //reset interrupted flag
}

JTable not updating from a SwingWorker thread

I currently have a JTable that is populated with a series of data that forms the basis of a import screen. When I have finished selecting which updates I want or do not want, I press on the Apply button and the updates are applied successfully but the JTable does not fully update.
This is the code for the method that deals with applying the changes:
private void doProcessChanges() {
ChangeProcessor cp = new ChangeProcessor();
final List<Integer> rowsToRemove = new ArrayList<Integer>();
BeanTableModel<UpdateModel> model = (BeanTableModel<UpdateModel>) table.getModel();
for (int i=0; i<model.getRowCount(); i++) {
UpdateRow ur = mode.getObject(i);
if (ur.isAccepted() <> ChangeAcceptance.NO_ACTION) {
cp.processChange(ur);
rowsToRemove.add(i);
}
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
for (int row : rowsToRemove) {
model.removeObject(row);
model.fireTableDataChanged();
}
}
);
}
The method is called from within a SwingWorker thread as below:
SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {
#Override
protected Object doInBackground() throws Exception {
doProcessChanges();
return null;
}
#Override
protected void done() {
try {
get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
I do not get any exceptions from executing this so I am I doing anything wrong?
Thanks in advance.
Your fragment shows incorrect synchronization. In particular, you access BeanTableModel, a subclass of AbstractTableModel, from the background thread. Instead, pass the List<Integer> rowsToRemove to your worker in its constructor.
Addendum: Instead of invokeLater(), you can update the TableModel in your implementation of process(), which executes on the EDT. Also, you shouldn't have to fireTableDataChanged(), which "Notifies all listeners that all cell values in the table's rows may have changed." The removeObject() implementation should fire the least pervasive event required to effect the change.

Graceful exception handling in Swing Worker

I am using threading in application through Swing Worker class. It works fine, yet I have a bad feeling about showing an error message dialog in try-catch block. Can it potentially block the application? This is what it looks right now:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
// Executed in background thread
public Void doInBackground() {
try {
DoFancyStuff();
} catch (Exception e) {
e.printStackTrace();
String msg = String.format("Unexpected problem: %s", e
.toString());
//TODO: executed in background thread and should be executed in EDT?
JOptionPane.showMessageDialog(Utils.getActiveFrame(),
msg, "Error", JOptionPane.ERROR_MESSAGE,
errorIcon);
}//END: try-catch
return null;
}
// Executed in event dispatch thread
public void done() {
System.out.println("Done");
}
};
Can it be done in a safe way using Swing Worker framework? Is overriding publish() method a good lead here?
EDIT:
Did it like this:
} catch (final Exception e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
e.printStackTrace();
String msg = String.format(
"Unexpected problem: %s", e.toString());
JOptionPane.showMessageDialog(Utils
.getActiveFrame(), msg, "Error",
JOptionPane.ERROR_MESSAGE, errorIcon);
}
});
}
Calling get in done method would result in two try-catch blocks, as the computational part throws exceptions, so I think this is cleaner in the end.
The right way to do it is as follows:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
// Executed in background thread
protected Void doInBackground() throws Exception {
DoFancyStuff();
return null;
}
// Executed in EDT
protected void done() {
try {
System.out.println("Done");
get();
} catch (ExecutionException e) {
e.getCause().printStackTrace();
String msg = String.format("Unexpected problem: %s",
e.getCause().toString());
JOptionPane.showMessageDialog(Utils.getActiveFrame(),
msg, "Error", JOptionPane.ERROR_MESSAGE, errorIcon);
} catch (InterruptedException e) {
// Process e here
}
}
}
You should NOT try to catch exceptions in the background thread but rather let them pass through to the SwingWorker itself, and then you can get them in the done() method by calling get()which normally returns the result of doInBackground() (Voidin your situation). If an exceptionwas thrown in the background thread then get() will throw it, wrapped inside an ExecutionException.
Please also note that overidden SwingWorker methods are protected and you don't need to make them public.
One option is to use SwingUtilities.invokeLater(...) to post the action on the EDT
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
JOptionPane.showMessageDialog(
Utils.getActiveFrame(),
msg,
"Error",
JOptionPane.ERROR_MESSAGE,
errorIcon);
}
});
And as you noted, SwingWorker is capable of reporting intermediate results, but you'll need to override process(...), which is called when you invoke publish(...).
Regardless, why not just set a flag if an exception occurs, and if that flag is set, show the dialog in done() since it's executed safely in the EDT?
You are right, you are violating the cardinal rule of Swing, which is don't modify the GUI anywhere except for on the event-dispatch-thread.
If it was me, I would throw an event that the GUI listens for to show the error message. Or, you can just wrap the invocation of the SwingWorker in a try catch and show the dialogue there.
First of all: sorry for the short answer, don't have too much time to spare.
I had the same problem: wanting to publish to System.out from within the worker.
Short answer: It won't block your app if you use the execute() method
The thing is that there is no blocking if you execute the worker as it should be: a background task.
class MyWorker extend SwingWorker<Void, Void>{
#Override
protected Void doInBackground() throws ... {
// your logic here and a message to a stream
System.out.println("from my worker, with love");
// ...
try {
throw new Exception("Whoops, this is an exception from within the worker");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Now you will invoke this worker creating a new instance, and after that calling the execute() method. But to save you some time: you will probably want to know when your worker is done, so you'll need to register an property change listener, which is fairly simple:
class MyListener implements PropertyChangeListener{
#Override
public void propertyChange(PropertyChangeEvent evt){
if(evt.getPropertyName().equals("state") && evt.getNewValue().equals(SwingWorker.StateValue.DONE)){
System.out.println("The worker is done");
}
}
}
And to put everything together at your main():
public void main(...){
MyWorker w = new MyWorker();
MyListener l = new MyListener();
w.addPropertyChangeListener(l);
w.execute();
}

Categories