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
}
Related
This is a strange situation I've run into. I have an abstract base class that defines a protected field. There is also a public method that modifies the field. I then have some subclasses of the base class that make use of the field.
I've noticed that when I invoke the super-class method to modify the field, the change to the field's value does not seem to "carry through" to an instance of a subclass.
The other thing that needs to be mentioned is that the abstract class (and therefore its subclasses) implements Runnable. I wouldn't think that should have an effect on what I'm seeing, but multi-threading is not my forte.
Abstract base class:
public abstract class AbstractWidget implements Runnable {
// Other fields and methods omitted for brevity.
protected boolean running;
public void shutDown() {
running = false;
}
}
Subclass:
public class ConcreteWidget extends AbstractWidget {
// Other fields and methods omitted for brevity.
#Override
public void run() {
running = true;
while (running) {
// ...
}
logger.info("Shutting down");
}
}
So, when I finally invoke the shutDown() method, the subclass instance, running in a thread does not break from it's loop and return.
I've used the technique of modifying a boolean field from "the outside" to stop a "forever" thread many times before. I don't understand what's happening here.
UPDATE:
Below is an example of the code being called.
ConcreteWidget widet = new ConcreteWidget(...);
thread = new Thread(widget);
thread.start();
logger.info("Started");
...
logger.debug("shutting down");
widget.shutDown();
try {
logger.debug("doing join on thread");
thread.join();
logger.debug("returned from join");
} catch (InterruptedException e) {
logger.error("Exception", e);
}
The call to join() never returns.
UPDATE:
As requested I have included what I hope is a complete (enough) example of the code as I currently have it. NOTE: I have taken the suggestion and changed the protected boolean to AtomicBoolean.
public abstract class AbstractWidget implements Runnable {
protected final AtomicBoolean running = new AtomicBoolean(true);
public void shutDown() {
running.set(false);
}
}
public class ConcreteWidget extends AbstractWidget {
#Override
public void run() {
while (running.get()) {
// ... do stuff (including calling process() below)
}
}
private void process() {
try {
// ... do stuff
} catch (IOException e) {
logger.error("Exception", e);
running.set(false);
return;
}
}
}
In the "main" logic:
private void startService() {
widget = new ConcreteWidget(...);
thread = new Thread(widget);
thread.start();
logger.info("Started");
}
public void close() {
logger.debug("shutting down service");
widget.shutDown();
try {
logger.debug("doing join on service thread");
thread.join();
logger.debug("returned from join");
} catch (InterruptedException e) {
logger.error("Exception", e);
}
}
BTW, it still does not work.
Your problem is actually a simple one. By the time you call widget.shutDown();, the thread has not actually started, so when the thread actually starts, it sets running back to true, and never stops. Instead of using running to terminate the loop, use a separate stopped variable.
public abstract class AbstractWidget implements Runnable {
// Other fields and methods omitted for brevity.
private volatile boolean running = false;
private valatile boolean stopped = false;
public boolean isRunning() {
return running;
}
public boolean hasStopped() {
return stopped;
}
public void shutDown() {
stopped = true;
}
}
public class ConcreteWidget extends AbstractWidget {
// Other fields and methods omitted for brevity.
#Override
public void run() {
running = true;
while (!stopped) {
// ...
}
running = false;
logger.info("Shutting down");
}
}
With this setup, you might want to wait a while before stopping, if not the loop will never run at all.
ConcreteWidget widet = new ConcreteWidget(...);
thread = new Thread(widget);
thread.start();
logger.info("Started");
...
try {
Thread.sleep(500); // <--
} catch (Exception e) {}
logger.debug("shutting down");
widget.shutDown();
try {
logger.debug("doing join on thread");
thread.join();
logger.debug("returned from join");
} catch (InterruptedException e) {
logger.error("Exception", e);
}
Do you run widget.shutDown(); immediately after thread.start(); ?
maybe widget.shutDown(); has run before running = true; code inside run() method
[facepalm]
It turns out the problem was that the thread processing was hanging and never able to check the state of the running field. Once I corrected that problem, it worked just fine.
I did change my logic to use the AtomicBoolean instead of boolean, so thank you for that useful advice.
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();
}
}
}
When I tried to figure out how to stop a thread in a program with multiple threads,
I was suggested to call a method which actually sets a flag to tell that thread stop doing real works,like this:
public class ThreadTobeTerminated implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private volatile boolean running = true;
public void terminate() {
running = false;
}
#Override
public void run() {
while (running) {
try {
LOGGER.debug("Doing some real work ,like Counting...");
for(int i=0;i<100;i++){}
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
running = false;
}
}
}
}
when I want to stop this tread ,I'll call threadInstance.terminate();.
Don't I need to literally stop this thread ?
Why I should leave this thread to do some useless work (method run called ,test the flag running==false then return)? I mean :this is a waste of time ,isn't it?
When the execution scope goes beyond the run() method, the thread stops, so the moment that the while loop is broken, the thread will stop.
This would also allow you to do some clean up if the situation requires it:
public void run() {
while (running) {
try {
LOGGER.debug("Doing some real work ,like Counting...");
for(int i=0;i<100;i++){}
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
running = false;
}
}
//Clean up
}
The above approach allows you some control over how is the thread stops and what happens after as opposed to potentially just kill it, which could cause all kinds of problems.
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.
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();
}