Graceful exception handling in Swing Worker - java

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();
}

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.

java - make if for async task time limit

here is my piece of code:
Thread one = new Thread() {
public void run() {
try {
new LongOperation(finalJson)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
.get(30000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
};
one.start();
i want to say if AsyncTask past 30000 MILLISECONDS and didn't finish the job return a message, how i can code this? thanks
I would prefer doing it using an AsyncTask.
Copy-pasting from the link:
AsyncTask enables proper and easy use of the UI thread. This class
allows you to perform background operations and publish results on the
UI thread without having to manipulate threads and/or handlers.
AsyncTask is designed to be a helper class around Thread and Handler
and does not constitute a generic threading framework. AsyncTasks
should ideally be used for short operations (a few seconds at the
most.) If you need to keep threads running for long periods of time,
it is highly recommended you use the various APIs provided by the
java.util.concurrent package such as Executor, ThreadPoolExecutor and
FutureTask.
Said this, configuring an AsyncTask is pretty simple, just create a class like the following:
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
//this method works on the UI thread.
//this is the first to run before "doInBackground"
mTextView.setText("we start!");
}
#Override
protected String doInBackground(String... params) {
try {
//do whatever your async task needs to do. This method works async
//you can also call an UI callback from here with the publishProgress method. This will call the "onProgressUpdate" method, and it has to respect his type.
publishProgress("we go on!");
} catch (InterruptedException e) {
Thread.interrupted();
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
//this method works on the UI thread
//it get the "doInBackground" return value.
mTextView.setText(result);
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(String... values) {
//this method works on UI thread, so it can access UI components and ctx
mTextView.setText(values[0]);
}
}
This is a basic example on how to create an AsyncTask, you can use it like this (form activity/fragment):
AsyncTaskExample asyncTask = new AsyncTaskExample();
asyncTask.get(30000, TimeUnit.MILLISECONDS);
This will set a timeout on your async operation. Look here for the exact Exception/return
For any further question, ask freely. Hope this helps
Edit:
I just noticed you have an AsyncTask inside your thread. Since AsyncTask is already async, I would avoid doing this and simply call the AsyncTask with the method I gave you before. In that way the AsyncTask will run up to the given TimeSpan :)
See the code below: It may help you.
CountDownTimer timer = new CountDownTimer(3000,1000) {
#Override
public void onTick(long l) {
}
#Override
public void onFinish() {
//return a message here;
}
};
timer.start();
And if you have an async task. Then do like below in doInBackground method:
#Override
protected Void doInBackground(Void... voids) {
//simply do your job
CountDownTimer timer = new CountDownTimer(3000,1000) {
#Override
public void onTick(long l) {
}
#Override
public void onFinish() {
//return a message here;
return message;
}
};
timer.start();
return your_reult;
}

If a SwingWorker task is canceled how do I know when interrupted method completed

If a SwingWorker task is canceled, how do I know when the interrupted method completed.
I have simplified the code. FixSongsController.start() runs in the background. When it completes, a report is created and when that is done the report is then displayed using the done() method.
There is a progress dialog running and if the user elects to cancel ( whilst FixSongsController.start() is running ), this then invokes cancel(true) on the SwingWorker causing an interrupt to be sent to FixSongsController.start(). The trouble is, as soon as it's sent, it then invokes createReport().
I don't want to to start creating the report until FixSongsController.start() has actually finished. I can't see how to identify when that has occurred.
If I remove the createReport code from FixSongsDialog and the isCancelled() checks on Fix Songs the problem is worse because then FixSongsDialog trys to display the report before it has been created.
I thought I could make use of SwingWorker.getState() but once cancelTask is invoked doInBackground() task is interrupted and completes shortly after even though FixSongsController method is still tidying up.
Then I thought I could use SwingWorker.setProgress() within FixSongsController.start() method in its finally block and add a listener that would only invoke showReport once the progress value had changed but setProgress is protected so I cannot access it outside of FixSongs class itself.
SwingWorker Class
public class FixSongs extends SwingWorker<String, Object>
{
#Override
public String doInBackground() throws Exception
{
try
{
new FixSongsController().start();
if (!isCancelled())
{
new FixSongsReportCreator().createReport();
}
return "";
}
catch (Throwable ex)
{
MainWindow.logger.log(Level.SEVERE, ex.getMessage(), ex);
throw ex;
}
}
#Override
protected void done()
{
SongKong.mainWindow.dialog.close();
if (!isCancelled())
{
ShowCounters showCounters = new ShowReport();
}
}
}
Progress Dialog Class
public class FixSongsDialog extends RecordBasedProgressDialog
{
....
#Override
public void cancelTask()
{
try
{
swingWorker.cancel(true);
CreateReport createReport = new CreateReport();
createReport.execute();
this.dispose();
}
catch(Exception ex)
{
MainWindow.logger.log(Level.SEVERE,ex.getMessage(),ex);
throw ex;
}
}
class CreateReport extends SwingWorker<String, Object>
{
#Override
public String doInBackground() throws Exception
{
try
{
new FixSongsReportCreator().createReport();
return "";
}
catch(Exception ex)
{
MainWindow.logger.log(Level.SEVERE,ex.getMessage(),ex);
throw ex;
}
}
#Override
protected void done()
{
ShowCounters showCounters = new ShowReport();
}
}
}

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
}

Swing Worker Delay

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.

Categories