SWT, multiple threads, and exceptions - java

I'm using SWT in a main GUI program. Within it, I create another thread to run some programs. However, if some errors are encountered in those processes, I want to report this to the user by making a message box appear. Because in SWT, only a single thread can perform GUI operations, I was having the program runner throw exceptions, so the GUI thread could deal with them. However, I am having problems because I create a new thread for the program runner (in order not to hold up the GUI thread, which will be continuously updating and refreshing some graphics), but as a result, the exceptions that take place are stuck as part of that thread, which can not create the error message box. Any suggestions on how to deal with this?
private void goButtonActionPerformed()
{
// create the program runner object
ProgramRunner PR = new ProgramRunner(); // real code passes in data to be used
try{
// check all necessary parameters are entered
boolean paramsOK = PR.checkParams();
if (paramsOK)
{
// all necessary information is available. Start Processing.
Thread t = new Thread() {
public void run()
{
try{
PR.runPrograms();
}
catch (IOException iox)
{
// This does not work to catch & display the exceptions
// which took place in PR.runPrograms(), because this
// thread is not allowed to perform GUI operations.
// However, I don't know how to pass this
// exception / error notification out of this thread.
MessageBox mb = new MessageBox(m_Shell, SWT.ICON_ERROR);
mb.setMessage(iox.getMessage());
mb.open();
}
}
};
t.start();
}
}
catch (IOException iox)
{
// this works to catch & display the exceptions which took place
// in PR.checkParams() because that is not a separate thread
MessageBox mb = new MessageBox(m_Shell, SWT.ICON_ERROR);
mb.setMessage(iox.getMessage());
mb.open();
}

Wrap catch logic inside a Display.getDefault().asyncExec to display error messages on UI thread:
Thread t = new Thread()
{
public void run()
{
try
{
PR.runProgram();
}
catch ( final IOException iox )
{
Display.getDefault().asyncExec( new Runnable()
{
public void run()
{
MessageBox mb = new MessageBox(m_Shell, SWT.ICON_ERROR);
mb.setMessage(iox.getMessage());
mb.open();
}
});
}
}
});
t.start();
the exceptions can be displayed in UI thread then.

You need to arrange that the UI code runs in the UI thread. You can do this using the asyncExec or syncExec methods of Display.
syncExec suspends the current thread until the UI code has been run. asyncExec does not suspend the thread and runs the UI code as soon as possible.
You can get the current display in any thread using Display.getDefault() so you might do something like:
Display.getDefault().asyncExec(() ->
{
if (m_Shell != null && !m_Shell.isDisposed()) {
MessageBox mb = new MessageBox(m_Shell, SWT.ICON_ERROR);
mb.setMessage(iox.getMessage());
mb.open();
}
});
I have used a Java 8 lambda expression for the Runnable here as it is shorter than the traditional method.
Since this code is being run asynchronously it is good practice to check that the shell is not null and has not been disposed.

Related

JFrame freezes while running the code continuously

I have problem while working with JFrame, which get freezes while
running the code continuously. Below is my code:
On clicking on btnRun, I called the function MainLoop():
ActionListener btnRun_Click = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
MainLoop();
}
};
Implementation of MainLoop():
void MainLoop()
{
Hopper = new CHopper(this);
System.out.println(Hopper);
btnRun.setEnabled(false);
textBox1.setText("");
Hopper.getM_cmd().ComPort = helpers.Global.ComPort;
Hopper.getM_cmd().SSPAddress = helpers.Global.SSPAddress;
Hopper.getM_cmd().Timeout = 2000;
Hopper.getM_cmd().RetryLevel = 3;
System.out.println("In MainLoop: " + Hopper);
// First connect to the validator
if (ConnectToValidator(10, 3))
{
btnHalt.setEnabled(true);
Running = true;
textBox1.append("\r\nPoll Loop\r\n"
+ "*********************************\r\n");
}
// This loop won't run until the validator is connected
while (Running)
{
// poll the validator
if (!Hopper.DoPoll(textBox1))
{
// If the poll fails, try to reconnect
textBox1.append("Attempting to reconnect...\r\n");
if (!ConnectToValidator(10, 3))
{
// If it fails after 5 attempts, exit the loop
Running = false;
}
}
// tick the timer
// timer1.start();
// update form
UpdateUI();
// setup dynamic elements of win form once
if (!bFormSetup)
{
SetupFormLayout();
bFormSetup = true;
}
}
//close com port
Hopper.getM_eSSP().CloseComPort();
btnRun.setEnabled(true);
btnHalt.setEnabled(false);
}
In the MainLoop() function, the while loop is running continuesly until the Running is true problem is that if i want to stop that while loop i have to set Running to false which is done at another button btnHalt:
ActionListener btnHalt_Click = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
textBox1.append("Poll loop stopped\r\n");
System.out.println("Hoper Stopped");
Running = false;
}
};
but btnHalt is not responding, whole frame is get freeze, also not
showing any log in the textarea.
Swing is a single thread framework. That is, there is a single thread responsible for dispatching all the events to all the components, including repaint requests.
Any action which stops/blocks this thread will cause your UI to "hang".
The first rule of Swing, NEVER run any blocking or time consuming tasks on the Event Dispatching Thread, instead, you should use a background thread.
This runs you smack into the second rule of Swing. Never create, modify or interact with any UI component outside of the EDT.
There are a number of ways you can fix this. You could use SwingUtilities.invokeLater or a SwingWorker.
SwingWorker is generally easier, as it provides a number of simple to use methods that automatically re-sync there calls to the EDT.
Take a read through Concurrency in Swing
Updated
Just so you understand ;)
Your MainLoop method should not be executed within the context of the EDT, this is very bad.
Also, you should not be interacting with any UI component from any thread other the then the EDT.

Nothing happens when I call my print method on a thread off the EDT?

I have a method that does some printing and I want the task to run on another thread (not on the EDT) because it might be creating a large file and I dont want the long process to freeze the GUI. The execution works perfectly on the EDT (with GUI freezing of course - which isn't desired), but when invoked on a different thread, it just doesn't execute. Here goes the method;
buildReceipt(itemList, method);
Where;
itemList is an ArrayList used to populate the receipt, and
method is an enum Type that determines whether to make the output a .pdf File or Send it directly to a Printer
The code above produces the document nicely when executed on the EDT but when I tried making it a background task using doInBackground() method of SwingWorker , it just didn't do anything at all; Then I got curious and tried the following;
Thread thread = new Thread(new Runnable(){
#Override
public void run()
{
buildReceipt(itemList, method);
}
});
thread.start();
and still, nothing happened......... More funny and confusing is the fact that I have even tried SwingUtilities.InvokeLater & SwingUtilities.InvokeAndWait (which by documentation run on the EDT) but still to no avail.
I have searched as many Stack Overflow related questions as I could, but none addresses my strange problem. I really need help on this one. Been stuck since yesteray?!?!?!
EDIT:
In Respose to Jean Waghetti; here's briefly what happens inside buildReceipt
private boolean buildReceipt(ArrayList<Sales> itemList, PrintMethod method)
{
boolean built = false;
if(!itemList.isEmpty())
{
InvoiceDesign design = new InvoiceDesign(itemList);
try
{
JasperReportBuilder report = design.build();
if(method.equals(PrintMethod.PDF))
{
appManager.connectToDB();
File fileDir = appManager.getReceiptsDir();
appManager.disconnectDB();
FileOutputStream fos = new FileOutputStream(fileDir);
report.toPdf(fos);
fos.close();
built = true;
}
else if(method.equals(PrintMethod.PRINTER))
{
report.print(true);
built = true;
}
}
catch(IOException e)
{
e.printStackTrace();
}
catch (DRException e)
{
e.printStackTrace();
}
}
return built;
}
So basically your item list is empty hence it never executes the code in the IF condition in that method.

How do I read a SwingWorker's result *without* busy wait?

I'm writing an application that executes its file menu actions using SwingWorker. Every called method returns a boolean value that tells, whether the operation was successfully executed or not.
At the moment I'm using busy waiting for the result, like this:
public boolean executeOperation() {
final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
#Override
protected Boolean doInBackground() throws Exception {
// ..
if (aborted) {
return false;
}
// ..
return true;
}
};
worker.execute();
// busy wait
while (!worker.isDone())
;
try {
return worker.get().booleanValue();
} catch (Exception e) {
// handle exceptions ..
return false;
}
}
Is there a less polling-intense way of solving this?
Using worker.get() directly wouldn't work, as it blocks the EDT, waiting for the task to finish - meaning even the dialogs I open from within the SwingWorker wouldn't get painted.
EDIT: If possible, I would like to avoid that the method (or the worker) to communicate their result asynchronously. I'm implementing several short methods (file -> open, new, close, save, save as, exit) that rely on each other (i. e. when the trying to exit, exit calls close, close might call save, save might call save as). Solving this asynchronously seems to make the code much more complicated.
The point of the SwingWorker is precisely to launch some task in the background and don't block the EDT. Either you want something synchronous, and the EDT will be blocked whatever you try, or you want something asynchronous, and the background task should update its status using the publish method of the SwingWorker.
You could display a blocking modal dialog with a progress bar while the task is running, and hide it once the task completes.
The alternative is to block for some time, hoping the task will be quick to finish, and then backup to an asynchronous way of doing. This can be done using the get method taking a timeout as argument.
You could use an asynchronous paradigm. Look at Observer / Observable and use the job to transfer the result back to the object which is currently doing the polling.
Using worker.get() directly wouldn't work, as it blocks the EDT, waiting for the task to finish - meaning even the dialogs I open from within the SwingWorker wouldn't get painted.
They don't with the current code either. Your busy wait blocks the EDT as much as calling worker.get() does - there is only one event dispatch thread, and the dialogs in the SwingWorker are just as blocked if that thread is spinning in a loop or awaiting a lock. The problem here is that if a method runs on the EDT, it simply can't return a value from an asynchronous operation (without hogging the EDT) to its caller.
The correct way to react to completed async processing is overriding the done() method in SwingWorker.
Also check out http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html for more info.
One way as mentioned by several folks above is to override the SwingWorker's done method. However if for some reason you want the post SwingWorker code outside of the SwingWorker and in the calling code, you can take advantage of SwingWorker's property change support. Simply add a PropertyChangeListener to the SwingWorker and listen for the state property which has a property name of "state". You can then extract the SwingWorker's state with its getState() method. When it is done it will return the DONE value of the SwingWorker.StateValue enum. For example (from an answer I've given in another thread here on SO):
if (turn == white) {
try {
final SwingWorker<Move, Void> mySwingWorker = new SwingWorker<Move, Void>() {
#Override
protected Move doInBackground() throws Exception {
Engine e = new Engine(); // Engine is implemented by runnable
e.start();
Move m = e.getBestMove(board);
return m;
}
};
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (StateValue.DONE == mySwingWorker.getState()) {
try {
Move m = mySwingWorker.get();
// TODO: insert code to run on the EDT after move determined
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
mySwingWorker.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
I ran into a similar problem when I wanted a function to return a value that would be calculated in a swing worker. I didn't want to simply get that thread to block the EDT. I also didn't want it to block. So I used a semaphore like this:
public boolean executeOperation() {
final Semaphore semaphore = new Semaphore(1);
semaphore.acquire(1); // surround by try catch...
final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
#Override
protected Boolean doInBackground() throws Exception {
// ..
if (aborted) {
semaphore.release();
return false;
}
// ..
semaphore.release();
return true;
}
};
worker.execute();
try {
semaphore.tryAcquire(1, 600, TimeUnit.SECONDS); // awakes when released or when 10 minutes are up.
return worker.get().booleanValue(); // blocks here if the task doesn't finish in 10 minutes.
} catch (Exception e) {
// handle exceptions ..
return false;
}
}
I guess this is not ideal for all situations. But I thought it was an alternative approach that was very useful for me.

Get and send messages with Java Threads

I want to make a thread, which runs, computes something with the data i give it, and returns a few values, or an object. The thread is a part of a Swing GUI.
My question: How can I make a method that runs when I make the thread, and returns an object (or whatever I want it to return)?
My code:
private void nextTurn () {
// do something
if (turn == white) {
try {
Engine e = new Engine(); // Engine is implemented by runnable
e.start();
Move m = e.getBestMove (board);
// thread should work, next code should be excecuted immediately
}
catch (Exception e) {}
}
// end of Main class
}
This is the first time I am working with Threads, and I know you should avoid them if possible, but I need it this time for my GUI.
The info on the Oracle site on Threads did not help me out. I am able to make a program with multiple Threads that runs indefinately, but I can't make it work with functions.
Since this is with a Swing GUI, consider using a SwingWorker object which creates a background thread (all the code run in the doInBackground method), and then can return a final result and/or interim results. Information on how to use this is well documented in the tutorials here:
Concurrency in Swing
SwingWorkers have property change support and thus will allow listeners to observe its state (as a SwingWorker.StateValue) via a PropertyChangeListener. This is one way your program can determine that the thread has completed its processing, get the returned result and go from there.
On an unrelated note, this isn't in your production code is it?:
catch (Exception e) {}
If so, you will likely want to fix this as ignored exceptions can bite you in the tail big time.
e.g.,
if (turn == white) {
try {
final SwingWorker<Move, Void> mySwingWorker = new SwingWorker<Move, Void>() {
#Override
protected Move doInBackground() throws Exception {
Engine e = new Engine(); // Engine is implemented by runnable
e.start();
Move m = e.getBestMove(board);
return m;
}
};
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (StateValue.DONE == mySwingWorker.getState()) {
try {
Move m = mySwingWorker.get();
// TODO: insert code to run on the EDT after move determined
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
mySwingWorker.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
I suggest you use an ExecutorService. It allows you to create a thread pool, you can pass tasks to it and get the results later.
http://download.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html

Java Swing Threading with Updatable JProgressBar

First off I've been working with Java's Concurrency package quite a bit lately but I have found an issue that I am stuck on. I want to have and Application and the Application can have a SplashScreen with a status bar and the loading of other data. So I decided to use SwingUtilities.invokeAndWait( call the splash component here ). The SplashScreen then appears with a JProgressBar and runs a group of threads. But I can't seem to get a good handle on things. I've looked over SwingWorker and tried using it for this purpose but the thread just returns. Here is a bit of pseudo code. and the points I'm trying to achieve.
Have an Application that has a SplashScreen that pauses while loading info
Be able to run multiple threads under the SplashScreen
Have the progress bar of the SplashScreen Update-able yet not exit until all threads are done.
Launching splash screen
try {
SwingUtilities.invokeAndWait( SplashScreen );
} catch (InterruptedException e) {
} catch (InvocationTargetException e) { }
Splash screen construction
SplashScreen extends JFrame implements Runnable{
public void run() {
//run threads
//while updating status bar
}
}
I have tried many things including SwingWorkers, Threads using CountDownLatch's, and others. The CountDownLatch's actually worked in the manner I wanted to do the processing but I was unable to update the GUI. When using the SwingWorkers either the invokeAndWait was basically nullified (which is their purpose) or it wouldn't update the GUI still even when using a PropertyChangedListener. If someone else has a couple ideas it would be great to hear them. Thanks in advance.
I actually got ready to post better code to help out and found my solution. I thank you for all who helped.
For running a series of operations in the background and reporting progress, use SwingWorker.
The background method does the background processing.
Use the publish method to post periodic status updates.
Override the process method to handle the updates (process always executes on the EDT).
progressBar = new JProgressBar();
sw = new SwingWorker<Boolean,Integer>() {
protected Boolean doInBackground() throws Exception {
// If any of the operations fail, return false to notify done()
// Do thing 1
publish(25); // 25% done
// Do thing 2
publish(50); // 50% done
// Do thing 3
publish(75); // 75% done
// Do thing 4
return true;
}
protected void process(List<Integer> chunks) {
for (Integer i : chunks)
progressBar.setValue(i);
}
protected void done() {
try {
boolean b = get();
if (b)
progressBar.setValue(100); // 100% done
else
// Notify the user processing failed
}
catch (InterruptedException ex) {
// Notify the user processing was interrupted
}
catch (ExecutionException ex) {
// Notify the user processing raised an exception
}
}
};
Addendum:
This can be extended to multiple tasks, it just requires changing how you approach setting the progress bar. Here's what comes to mind:
Have an array of completion counter, one per task.
int[] completions = new int[numTasks];
Arrays.fill(completions,0);
Start the SwingWorkers, each passed an index number. The process or done methods then call something like this to update the overall progress bar.
void update(int index, int percComplete) {
completions[index] = percComplete;
int total = 0;
for(int comp: completions)
total += comp/numTasks;
overallPB.setValue(total);
}
Optionally, display a JProgressBar per task.
Addendum 2:
If the tasks vary in completion time (eg, cache hit vs cache miss), you may want to investigate ProgressMonitor. It's a progress dialog that only appears if the task takes more than some (configurable, default 500ms) amount of time.
No need to call the frame inside invokeAndWait but you should update progress bar state like this.
try {
SwingUtilities.invokeAndWait( new Runnable() {
public void run() {
//update state of the progress bar here
}
});
} catch (InterruptedException e) {
} catch (InvocationTargetException e) { }

Categories