How can this SwingWorker code be made testable - java

Consider this code:
public void actionPerformed(ActionEvent e) {
setEnabled(false);
new SwingWorker<File, Void>() {
private String location = url.getText();
#Override
protected File doInBackground() throws Exception {
File file = new File("out.txt");
Writer writer = null;
try {
writer = new FileWriter(file);
creator.write(location, writer);
} finally {
if (writer != null) {
writer.close();
}
}
return file;
}
#Override
protected void done() {
setEnabled(true);
try {
File file = get();
JOptionPane.showMessageDialog(FileInputFrame.this,
"File has been retrieved and saved to:\n"
+ file.getAbsolutePath());
Desktop.getDesktop().open(file);
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Thread interupted, process aborting.", ex);
Thread.currentThread().interrupt();
} catch (ExecutionException ex) {
Throwable cause = ex.getCause() == null ? ex : ex.getCause();
logger.log(Level.SEVERE, "An exception occurred that was "
+ "not supposed to happen.", cause);
JOptionPane.showMessageDialog(FileInputFrame.this, "Error: "
+ cause.getClass().getSimpleName() + " "
+ cause.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
} catch (IOException ex) {
logger.log(Level.INFO, "Unable to open file for viewing.", ex);
}
}
}.execute();
url is a JTextField and 'creator' is an injected interface for writing the file (so that part is under test). The location where the file is written is hard coded on purpose because this is intended as an example. And java.util.logging is used simply to avoid an external dependency.
How would you chunk this up to make it unit-testable (including abandoning SwingWorker if needed, but then replacing its functionality, at least as used here).
The way I look at it, the doInBackground is basically alright. The fundamental mechanics are creating a writer and closing it, which is almost too simple to test and the real work is under test. However, the done method is quote problematic, including its coupling with the actionPerformed method the parent class and coordinating the enabling and disabling of the button.
However, pulling that apart is not obvious. Injecting some kind of SwingWorkerFactory makes capturing the GUI fields a lot harder to maintain (it is hard to see how it would be a design improvement). The JOpitonPane and the Desktop have all the "goodness" of Singletons, and exception handling makes it impossible to wrap the get easily.
So what would be a good solution to bring this code under test?

IMHO, that's complicated for an anonymous class. My approach would be to refactor the anonymous class to something like this:
public class FileWriterWorker extends SwingWorker<File, Void> {
private final String location;
private final Response target;
private final Object creator;
public FileWriterWorker(Object creator, String location, Response target) {
this.creator = creator;
this.location = location;
this.target = target;
}
#Override
protected File doInBackground() throws Exception {
File file = new File("out.txt");
Writer writer = null;
try {
writer = new FileWriter(file);
creator.write(location, writer);
}
finally {
if (writer != null) {
writer.close();
}
}
return file;
}
#Override
protected void done() {
try {
File file = get();
target.success(file);
}
catch (InterruptedException ex) {
target.failure(new BackgroundException(ex));
}
catch (ExecutionException ex) {
target.failure(new BackgroundException(ex));
}
}
public interface Response {
void success(File f);
void failure(BackgroundException ex);
}
public class BackgroundException extends Exception {
public BackgroundException(Throwable cause) {
super(cause);
}
}
}
That allows the file writing functionality to be tested independent of a GUI
Then, the actionPerformed becomes something like this:
public void actionPerformed(ActionEvent e) {
setEnabled(false);
Object creator;
new FileWriterWorker(creator, url.getText(), new FileWriterWorker.Response() {
#Override
public void failure(FileWriterWorker.BackgroundException ex) {
setEnabled(true);
Throwable bgCause = ex.getCause();
if (bgCause instanceof InterruptedException) {
logger.log(Level.INFO, "Thread interupted, process aborting.", bgCause);
Thread.currentThread().interrupt();
}
else if (cause instanceof ExecutionException) {
Throwable cause = bgCause.getCause() == null ? bgCause : bgCause.getCause();
logger.log(Level.SEVERE, "An exception occurred that was "
+ "not supposed to happen.", cause);
JOptionPane.showMessageDialog(FileInputFrame.this, "Error: "
+ cause.getClass().getSimpleName() + " "
+ cause.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
#Override
public void success(File f) {
setEnabled(true);
JOptionPane.showMessageDialog(FileInputFrame.this,
"File has been retrieved and saved to:\n"
+ file.getAbsolutePath());
try {
Desktop.getDesktop().open(file);
}
catch (IOException iOException) {
logger.log(Level.INFO, "Unable to open file for viewing.", ex);
}
}
}).execute();
}
Additionally, the instance of FileWriterWorker.Response can be assigned to a variable and tested independent of FileWriterWorker.

The current implementation couples together threading concerns, UI and file writing - and as you've discovered that coupling makes it hard to test the individual components in isolation.
This is quite a long response, but it boils down to pulling out these three concerns from the current implementation into separate classes with a defined interface.
Factor out Application Logic
To start with, focus on the core application logic and move that into a separate class/interface. An interface allows easier mocking, and use of other swing-threading frameworks. The separation means you can test your application logic entirely independently from the other concerns.
interface FileWriter
{
void writeFile(File outputFile, String location, Creator creator)
throws IOException;
// you could also create your own exception type to avoid the checked exception.
// a request object allows all the params to be encapsulated in one object.
// this makes chaining services easier. See later.
void writeFile(FileWriteRequest writeRequest);
}
class FileWriteRequest
{
File outputFile;
String location;
Creator creator;
// constructor, getters etc..
}
class DefualtFileWriter implements FileWriter
{
// this is basically the code from doInBackground()
public File writeFile(File outputFile, String location, Creator creator)
throws IOException
{
Writer writer = null;
try {
writer = new FileWriter(outputFile);
creator.write(location, writer);
} finally {
if (writer != null) {
writer.close();
}
}
return file;
}
public void writeFile(FileWriterRequest request) {
writeFile(request.outputFile, request.location, request.creator);
}
}
Separate out UI
With the application logic now separate, we then factor out the success and error handling. This means that the UI can be tested without actually doing the file writing. In particular, error handling can be tested without actually need to provoke those errors. Here, the errors are quite simple, but often some errors can be very difficult to provoke. By separating out the error handling, there is also chance for reuse, or replacing how the errors are handled. E.g. using a JXErrorPane later.
interface FileWriterHandler {
void done();
void handleFileWritten(File file);
void handleFileWriteError(Throwable t);
}
class FileWriterJOptionPaneOpenDesktopHandler implements FileWriterHandler
{
private JFrame owner;
private JComponent enableMe;
public void done() { enableMe.setEnabled(true); }
public void handleFileWritten(File file) {
try {
JOptionPane.showMessageDialog(owner,
"File has been retrieved and saved to:\n"
+ file.getAbsolutePath());
Desktop.getDesktop().open(file);
}
catch (IOException ex) {
handleDesktopOpenError(ex);
}
}
public void handleDesktopOpenError(IOException ex) {
logger.log(Level.INFO, "Unable to open file for viewing.", ex);
}
public void handleFileWriteError(Throwable t) {
if (t instanceof InterruptedException) {
logger.log(Level.INFO, "Thread interupted, process aborting.", ex);
// no point interrupting the EDT thread
}
else if (t instanceof ExecutionException) {
Throwable cause = ex.getCause() == null ? ex : ex.getCause();
handleGeneralError(cause);
}
else
handleGeneralError(t);
}
public void handleGeneralError(Throwable cause) {
logger.log(Level.SEVERE, "An exception occurred that was "
+ "not supposed to happen.", cause);
JOptionPane.showMessageDialog(owner, "Error: "
+ cause.getClass().getSimpleName() + " "
+ cause.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
Separate out Threading
Finally, we can also separate out the threading concerns with a FileWriterService. Using a FileWriteRequest above makes coding this simpler.
interface FileWriterService
{
// rather than have separate parms for file writing, it is
void handleWriteRequest(FileWriteRequest request, FileWriter writer, FileWriterHandler handler);
}
class SwingWorkerFileWriterService
implements FileWriterService
{
void handleWriteRequest(FileWriteRequest request, FileWriter writer, FileWriterHandler handler) {
Worker worker = new Worker(request, fileWriter, fileWriterHandler);
worker.execute();
}
static class Worker extends SwingWorker<File,Void> {
// set in constructor
private FileWriter fileWriter;
private FileWriterHandler fileWriterHandler;
private FileWriterRequest fileWriterRequest;
protected File doInBackground() {
return fileWriter.writeFile(fileWriterRequest);
}
protected void done() {
fileWriterHandler.done();
try
{
File f = get();
fileWriterHandler.handleFileWritten(f);
}
catch (Exception ex)
{
// you could also specifically unwrap the ExecutorException here, since that
// is specific to the service implementation using SwingWorker/Executors.
fileWriterHandler.handleFileError(ex);
}
}
}
}
Each part of the system is separately testable - the application logic, the presentation (success and error handling) and the threading implementation is also a separate concern.
This may seem like a lot of interfaces, but the implementation is mostly cut-and-paste from your original code. The interfaces provide the separation that is needed to make these classes testable.
I'm not much of a fan of SwingWorker's so keeping them behind an interface helps keep the clutter they produce out of the code. It also allows you to use a different implementation for implementing the separate UI/background threads. For example, to use Spin, you only need to provide a new implementation of FileWriterService.

Easy solution : a simple timer is best ; you lanch your timer, you launch your actionPerformed, and at the timeout the bouton must be enabled and so on.
Here is an very littel exemple with a java.util.Timer :
package goodies;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JButton;
public class SWTest
{
static class WithButton
{
JButton button = new JButton();
class Worker extends javax.swing.SwingWorker<Void, Void>
{
#Override
protected Void doInBackground() throws Exception
{
synchronized (this)
{
wait(4000);
}
return null;
}
#Override
protected void done()
{
button.setEnabled(true);
}
}
void startWorker()
{
Worker work = new Worker();
work.execute();
}
}
public static void main(String[] args)
{
final WithButton with;
TimerTask verif;
with = new WithButton();
with.button.setEnabled(false);
Timer tim = new Timer();
verif = new java.util.TimerTask()
{
#Override
public void run()
{
if (!with.button.isEnabled())
System.out.println("BAD");
else
System.out.println("GOOD");
System.exit(0);
}};
tim.schedule(verif, 5000);
with.startWorker();
}
}
Supposed Expert solution : a Swing Worker is a RunnableFuture, inside it a FutureTask imbeded in a callable, so you can use your own executor to launch it (the RunableFuture). To do that, you need a SwingWorker with a name class, not an anonymous. With your own executor and a name class, you can test all you want, the supposed expert says.

Related

How do I pause my main thread so my JFrame can operate properly

I read a couple questions related to pausing main and both gave answers I didn't understand, and frankly I don't think are applicable.
I have a JFrame that makes use of a database I'm setting up in my driver class.
The JFrame will launch and the window opens; however when I try to make use of the database it fails; because back in main the program just keeps running and shuts down the connection, and closes it.
I tried just removing the connection.close() code just to see if my database methods work in the JFrame, and they do, so I just need to learn how to halt main while my JFrame is running.
public static void main(String[] args) {
File dbPropertiesFile = new File(DbConstants.DB_PROPERTIES_FILENAME);
if (!dbPropertiesFile.exists()) {
showUsage();
System.exit(-1);
}
try {
new Lab9(dbPropertiesFile).run(args);
} catch (Exception e) {
LOG.error(e.getMessage());
e.printStackTrace();
} finally {
shutdown();
}
}
private static void configureLogging() {
ConfigurationSource source;
try {
source = new ConfigurationSource(new FileInputStream(LOG4J_CONFIG_FILENAME));
Configurator.initialize(null, source);
} catch (IOException e) {
System.out.println(
String.format("Can't find the log4j logging configuration file %s.", LOG4J_CONFIG_FILENAME));
}
}
private static void shutdown() {
LOG.info("Shutting down");
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
LOG.error(e.getMessage());
e.printStackTrace();
}
}
}
private static void showUsage() {
System.err.println(
String.format("Program cannot start because %s cannot be found.", DbConstants.DB_PROPERTIES_FILENAME));
}
private Lab9(File file) throws IOException {
properties = new Properties();
properties.load(new FileInputStream(file));
database = new Database(properties);
}
/**
* Where the computer start making a lot of noise.
*
* #param args
* #throws Exception
*/
private void run(String[] args) throws Exception {
LOG.info("Running");
LOG.info("Loading database properties from: " + DbConstants.DB_PROPERTIES_FILENAME + ".");
LOG.info(properties.getProperty("db.driver"));
LOG.info("Driver loaded");
LOG.info("DB URL = " + properties.getProperty("db.url"));
LOG.info("DB USER = " + properties.getProperty("db.user"));
LOG.info("DB PASSWORD = " + properties.getProperty("db.password"));
connect();
Statement statement = connection.createStatement();
try {
// If the user enters the -drop switch
if (args[0].equalsIgnoreCase(DROP_COMMAND)) {
LOG.info("Table " + CustomerDao.TABLE_NAME + "is being DROPPED!");
customerDao.drop();
LOG.info("Table has been DROPPED!");
}
// Check to see if the table is already made; if its not then make it, and fill
// it.
if (Database.tableExists(CustomerDao.TABLE_NAME) == false) {
createTables(statement);
LOG.info("Created the table: " + CustomerDao.TABLE_NAME + ".");
LOG.info("Inserting Customer objects into table: " + CustomerDao.TABLE_NAME + ".");
insertCustomers();
LOG.info("Inserted customer info into table from file: [" + CUSTOMER_DATA + "].");
}
createUI();
// I NEED MAIN
// TO STOP
// AROUND HERE!
}catch(SQLException e){
e.printStackTrace();
LOG.error(e.getMessage());
}finally{
connection.close();
}
}
public static void createUI() {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
DatabaseControlFrame frame = new DatabaseControlFrame(customerDao);
frame.setVisible(true);
// OR MAYBE I NEED MAIN
// TO STOP
// AROUND HERE!
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private void connect() throws SQLException {
connection = database.getConnection();
customerDao = new CustomerDao(database);
}
}
Any ideas? I tried using a while(frame.isVisilbe()){ wait(600) }; But the compiler had a spas when I tried to use wait().
You'll note I'm passing a customerDAO object to my JFrame constructor; but I'm beginning to wonder could I make a connection inside the JFrame so that when main's connection closes; my JFrame's doesn't? Is that a good idea? Is that even possible I'm not super SQL savvy I'm going to need to study up on it more.
You could use Thread.sleep() - I've found that useful with JFrame before, though I'm not 100% sure it would fit what you're looking for. If you want it to wait indefinitely, put it in a while loop:
while(//condition)
{
Thread.sleep(500); //pauses for .5 sec, then loops back to check condition
}
JFrame event handler runs on a different thread than main thread, so you need to shutdown on that thread.
Here is a example, Using JDBC with GUI API.
This example call connection.close() on received window-closing-event.
public class MyFrame extends JFrame {
public MyFrame() {
// ...
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent e) {
shutdown();
System.exit(0);
}
});
}
// ...
}

SWTException: Invalid thread access

I work on a wizard for creation of a java project and get a invalid thread access exception if I run it in the empty workspace for the first time. I try to implement my wizard similar to JavaProjectWizard, but I don't need the second page, so I try to perform finish from the first page and to initialize the second page in advance:
import org.eclipse.jdt.ui.wizards.NewJavaProjectWizardPageTwo;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.INewWizard;
public class SomeNewWizard
extends Wizard
implements INewWizard {
private SomeWizardPageTwo javaWizardPageTwo;
#Override
public void addPages() {
if (javaWizardPageTwo == null)
someWizardPageTwo = new SomeWizardPageTwo(newSeeAppWizardPageOne);
}
#Override
public boolean performFinish() {
/*line 109*/someWizardPageTwo .createProvisonalProject();
final IWorkspaceRunnable op = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor)
throws CoreException, OperationCanceledException {
try {
someWizardPageTwo.performFinish(new SubProgressMonitor(monitor, 1));
}
catch (InterruptedException e) {
throw new OperationCanceledException(e.getMessage());
}
finally {
monitor.done();
}
}
};
try {
rule = null;
Job job = Job.getJobManager().currentJob();
if (job != null)
rule = job.getRule();
IRunnableWithProgress runnable = new IRunnableWithProgress() {
#Override
public void run(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
try {
JavaCore.run(op, rule, monitor);
}
catch (OperationCanceledException e) {
throw new InterruptedException(e.getMessage());
}
catch (CoreException e) {
throw new InvocationTargetException(e);
}
}
};
getContainer().run(true, true, runnable);
}
catch (InvocationTargetException e) {
handleFinishException(getShell(), e);
return false;
}
catch (InterruptedException e) {
return false;
}
return true;
}
public class SomeWizardPageTwo
extends NewJavaProjectWizardPageTwo
{
private NewJavaProjectWizardPageOne mainPage;
public SomeWizardPageTwo(NewJavaProjectWizardPageOne mainPage)
{
super(mainPage);
this.mainPage = mainPage;
}
#Override
public IProject createProvisonalProject()
{
return super.createProvisonalProject();
}
#Override
protected IWizardContainer getContainer()
{
if (mainPage == null)
return null;
return mainPage.getWizard().getContainer();
}
}
The stacktrace can be found here.
The root of the issue seems that ImageDescriptorRegistry is created from the wrong thread because the corresponding IRunnableWithProgress runs forked. But I wonder why does it work for the normal JavaProjectWizard then? And the main question is: how to make it work for my wizard?
getContainer().run(true, true, runnable); causes the runnable to be executed in a separate thread. The first parameter fork is responsible therefore.
The call to javaWizardPageTwo.performFinish() atempts to access the UI thread and causes the invalid thread access exception.
If you set the fork parameter to false, the code will be executed on the current thread.
Why don't you call javaWizardPageTwo.performFinish() directly?
There is not a bug in Display.checkDevice.
Your call to NewJavaProjectWizardPageTwo.performFinish is running in a background thread, but the code is using ImageDescriptorRegistry which needs to be initialized on the User Interface thread.
This is intermittent because sometimes something else that you do will have already initialized the registry.

unreported exception Exception; must be caught or declared to be thrown

There's two classes:
The main one (Corina.java) and the one that I am having issues with (Functions.java). Without complicating things too much, Corina.java calls a method in Functions.java, the method checks if a boolean is true or false and asks for authentication based on that, the code is very impartial at the moment, though I am using a phidgets RFID reader and copied a portion of one of their examples. but I get the following error in JCreator:
--------------------Configuration: Corina - JDK version 1.7.0_45 <Default> - <Default>--------------------
C:\Users\alexis.JKLSEMICOLON\Documents\JCreator LE\MyProjects\Corina\src\Functions.java:23: error: unreported exception Exception; must be caught or declared to be thrown
authenticateContinue();
^
First class code:
public class Corina {
public static void main(String[] args) {
Functions funtion = new Functions();
funtion.authenticateStart();
}
}
Second class code:
import com.phidgets.*;
import com.phidgets.event.*;
public class Functions {
public void authenticateStart() {
boolean authStatus = false;
System.out.println("The authentication status is currently: " + authStatus + ".");
if (authStatus) {
System.out.println("The applications is unlocked. Please wait.");
// applicationStart();
} else {
System.out.println("Please authenticate now by swiping one of the RFID tags allowed to unlock the program.");
authenticateContinue();
}
}
public void authenticateContinue() throws Exception {
RFIDPhidget rfid;
System.out.println(Phidget.getLibraryVersion());
rfid = new RFIDPhidget();
rfid.addAttachListener(new AttachListener() {
public void attached(AttachEvent ae) {
try {
((RFIDPhidget) ae.getSource()).setAntennaOn(true);
((RFIDPhidget) ae.getSource()).setLEDOn(true);
} catch (PhidgetException ex) {
}
System.out.println("attachment of " + ae);
}
});
rfid.addDetachListener(new DetachListener() {
public void detached(DetachEvent ae) {
System.out.println("detachment of " + ae);
}
});
rfid.addErrorListener(new ErrorListener() {
public void error(ErrorEvent ee) {
System.out.println("error event for " + ee);
}
});
rfid.addTagGainListener(new TagGainListener() {
public void tagGained(TagGainEvent oe) {
System.out.println(oe);
}
});
rfid.addTagLossListener(new TagLossListener() {
public void tagLost(TagLossEvent oe) {
System.out.println(oe);
}
});
rfid.addOutputChangeListener(new OutputChangeListener() {
public void outputChanged(OutputChangeEvent oe) {
System.out.println(oe);
}
});
rfid.openAny();
System.out.println("waiting for RFID attachment...");
rfid.waitForAttachment(1000);
System.out.println("Serial: " + rfid.getSerialNumber());
System.out.println("Outputs: " + rfid.getOutputCount());
System.out.println("Outputting events. Input to stop.");
System.in.read();
System.out.print("closing...");
rfid.close();
rfid = null;
System.out.println(" ok");
if (false) {
System.out.println("wait for finalization...");
System.gc();
}
}
}
any help would be appreciated. Ideally, I would like to just have the tag saved to a string, so if you would be knowledgeable on that, by all means.
Your problem is that authenticateStart calls authenticateContinue(), but you've flagged authenticateContinue as able to throw an Exception. That means that authenticateStart needs to be able to deal with that exception when it's thrown. You have a couple of options.
Put the call to authenticateContinue inside a try block, and deal with the exception in a catch block beneath it.
Change authenticateContinue so that it doesn't throw a checked exception.
Flag authenticateStart as able to throw an Exception. This will push the problem up into main, where you're calling authenticateStart.
No matter what you do, you'll have to deal with that exception somehow. The whole point of Java exception handling is that you can't just leave checked exceptions unhandled - you have to deal with them somehow.

Telling a ThreadPoolExecutor when it should go ahead or not

I have to send a set of files to several computers through a certain port. The fact is that, each time that the method that sends the files is called, the destination data (address and port) is calculated. Therefore, using a loop that creates a thread for each method call, and surround the method call with a try-catch statement for a BindException to process the situation of the program trying to use a port which is already in use (different destination addresses may receive the message through the same port) telling the thread to wait some seconds and then restart to retry, and keep trying until the exception is not thrown (the shipping is successfully performed).
I didn't know why (although I could guess it when I first saw it), Netbeans warned me about that sleeping a Thread object inside a loop is not the best choice. Then I googled a bit for further information and found this link to another stackoverflow post, which looked so interesting (I had never heard of the ThreadPoolExecutor class). I've been reading both that link and the API in order to try to improve my program, but I'm not yet pretty sure about how am I supposed to apply that in my program. Could anybody give a helping hand on this please?
EDIT: The important code:
for (Iterator<String> it = ConnectionsPanel.list.getSelectedValuesList().iterator(); it.hasNext();) {
final String x = it.next();
new Thread() {
#Override
public void run() {
ConnectionsPanel.singleAddVideos(x);
}
}.start();
}
private static void singleAddVideos(String connName) {
String newVideosInfo = "";
for (Iterator<Video> it = ConnectionsPanel.videosToSend.iterator(); it.hasNext();) {
newVideosInfo = newVideosInfo.concat(it.next().toString());
}
try {
MassiveDesktopClient.sendMessage("hi", connName);
if (MassiveDesktopClient.receiveMessage(connName).matches("hello")) {
MassiveDesktopClient.sendMessage(newVideosInfo, connName);
}
} catch (BindException ex) {
MassiveDesktopClient.println("Attempted to use a port which is already being used. Waiting and retrying...", new Exception().getStackTrace()[0].getLineNumber());
try {
Thread.sleep(MassiveDesktopClient.PORT_BUSY_DELAY_SECONDS * 1000);
} catch (InterruptedException ex1) {
JOptionPane.showMessageDialog(null, ex1.toString(), "Error", JOptionPane.ERROR_MESSAGE);
}
ConnectionsPanel.singleAddVideos(connName);
return;
}
for (Iterator<Video> it = ConnectionsPanel.videosToSend.iterator(); it.hasNext();) {
try {
MassiveDesktopClient.sendFile(it.next().getAttribute("name"), connName);
} catch (BindException ex) {
MassiveDesktopClient.println("Attempted to use a port which is already being used. Waiting and retrying...", new Exception().getStackTrace()[0].getLineNumber());
try {
Thread.sleep(MassiveDesktopClient.PORT_BUSY_DELAY_SECONDS * 1000);
} catch (InterruptedException ex1) {
JOptionPane.showMessageDialog(null, ex1.toString(), "Error", JOptionPane.ERROR_MESSAGE);
}
ConnectionsPanel.singleAddVideos(connName);
return;
}
}
}
Your question is not very clear - I understand that you want to rerun your task until it succeeds (no BindException). To do that, you could:
try to run your code without catching the exception
capture the exception from the future
reschedule the task a bit later if it fails
A simplified code would be as below - add error messages and refine as needed:
public static void main(String[] args) throws Exception {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(corePoolSize);
final String x = "video";
Callable<Void> yourTask = new Callable<Void>() {
#Override
public Void call() throws BindException {
ConnectionsPanel.singleAddVideos(x);
return null;
}
};
Future f = scheduler.submit(yourTask);
boolean added = false; //it will retry until success
//you might use an int instead to retry
//n times only and avoid the risk of infinite loop
while (!added) {
try {
f.get();
added = true; //added set to true if no exception caught
} catch (ExecutionException e) {
if (e.getCause() instanceof BindException) {
scheduler.schedule(yourTask, 3, TimeUnit.SECONDS); //reschedule in 3 seconds
} else {
//another exception was thrown => handle it
}
}
}
}
public static class ConnectionsPanel {
private static void singleAddVideos(String connName) throws BindException {
String newVideosInfo = "";
for (Iterator<Video> it = ConnectionsPanel.videosToSend.iterator(); it.hasNext();) {
newVideosInfo = newVideosInfo.concat(it.next().toString());
}
MassiveDesktopClient.sendMessage("hi", connName);
if (MassiveDesktopClient.receiveMessage(connName).matches("hello")) {
MassiveDesktopClient.sendMessage(newVideosInfo, connName);
}
for (Iterator<Video> it = ConnectionsPanel.videosToSend.iterator(); it.hasNext();) {
MassiveDesktopClient.sendFile(it.next().getAttribute("name"), connName);
}
}
}

How to reschedule a task using a ScheduledExecutorService?

I saw this in the java docs: ScheduledAtFixedRate, it says
If any execution of the task
encounters an exception, subsequent
executions are suppressed
I don't want this to happen in my application. Even if I see an exception I would always want the subsequent executions to occur and continue. How can I get this behavior from ScheduledExecutorService.
Surround the Callable.call method or the Runnable.run method with a try/catch...
eg:
public void run()
{
try
{
// ... code
}
catch(final IOException ex)
{
// handle it
}
catch(final RuntimeException ex)
{
// handle it
}
catch(final Exception ex)
{
// handle it
}
catch(final Error ex)
{
// handle it
}
catch(final Throwable ex)
{
// handle it
}
}
Note that catching anything other than what the compiler tells you too (the IOException in my sample) isn't a good idea, but there are some times, and this sounds like one of them, that it can work out if you handle it properly.
Remember that things like Error are very bad - the VM ran out of memory etc... so be careful how you handle them (which is why I separated them out into their own handlers rather than just doing catch(final Throwable ex) and nothing else).
Try VerboseRunnable class from jcabi-log, which does the wrapping suggested by TofuBeer:
import com.jcabi.log.VerboseRunnable;
Runnable runnable = new VerboseRunnable(
Runnable() {
public void run() {
// do business logic, may Exception occurs
}
},
true // it means that all exceptions will be swallowed and logged
);
Now, when anybody calls runnable.run() no exceptions are thrown. Instead, they are swallowed and logged (to SLF4J).
I had the same problem. I also tried that try block within run() method but it doesn't work.
So I did something is working so far:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
public class Test2 {
static final ExecutorService pool = Executors.newFixedThreadPool(3);
static final R1 r1 = new R1();
static final R2 r2 = new R2();
static final BlockingQueue deadRunnablesQueue = new LinkedBlockingQueue<IdentifiableRunnable>();
static final Runnable supervisor = new Supervisor(pool, deadRunnablesQueue);
public static void main(String[] args) {
pool.submit(r1);
pool.submit(r2);
new Thread(supervisor).start();
}
static void reSubmit(IdentifiableRunnable r) {
System.out.println("given to an error, runnable [" + r.getId()
+ "] will be resubmited");
deadRunnablesQueue.add(r);
}
static interface IdentifiableRunnable extends Runnable {
String getId();
}
static class Supervisor implements Runnable {
private final ExecutorService pool;
private final BlockingQueue<IdentifiableRunnable> deadRunnablesQueue;
Supervisor(final ExecutorService pool,
final BlockingQueue<IdentifiableRunnable> deadRunnablesQueue) {
this.pool = pool;
this.deadRunnablesQueue = deadRunnablesQueue;
}
#Override
public void run() {
while (true) {
IdentifiableRunnable r = null;
System.out.println("");
System.out
.println("Supervisor will wait for a new runnable in order to resubmit it...");
try {
System.out.println();
r = deadRunnablesQueue.take();
} catch (InterruptedException e) {
}
if (r != null) {
System.out.println("Supervisor got runnable [" + r.getId()
+ "] to resubmit ");
pool.submit(r);
}
}
}
}
static class R1 implements IdentifiableRunnable {
private final String id = "R1";
private long l;
#Override
public void run() {
while (true) {
System.out.println("R1 " + (l++));
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
System.err.println("R1 InterruptedException:");
}
}
}
public String getId() {
return id;
}
}
static class R2 implements IdentifiableRunnable {
private final String id = "R2";
private long l;
#Override
public void run() {
try {
while (true) {
System.out.println("R2 " + (l++));
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
System.err.println("R2 InterruptedException:");
}
if (l == 3) {
throw new RuntimeException(
"R2 error.. Should I continue to process ? ");
}
}
} catch (final Throwable t) {
t.printStackTrace();
Test2.reSubmit(this);
}
}
public String getId() {
return id;
}
}
}
You can try to comment out Test2.reSubmit(this) to see that without it, R2 will stop working.
If all you want is subsequent executions to occur and continue even after exceptions, this code should work.
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Runnable task = new Runnable() {
#Override
public void run() {
try{
System.out.println(new Date() + " printing");
if(true)
throw new RuntimeException();
} catch (Exception exc) {
System.out.println(" WARN...task will continiue"+
"running even after an Exception has araised");
}
}
};
executor.scheduleAtFixedRate(task, 0, 3, TimeUnit.SECONDS);
If a Throwable other than Exception has occurred you might not want subsequent executions get executed.
Here is the output
Fri Nov 23 12:09:38 JST 2012 printing _WARN...task will
continiuerunning even after an Exception has raisedFri Nov 23
12:09:41 JST 2012 printing _WARN...task will continiuerunning
even after an Exception has raisedFri Nov 23 12:09:44 JST 2012
printing _WARN...task will continiuerunning even after an
Exception has raisedFri Nov 23 12:09:47 JST 2012 printing
_WARN...task will continiuerunning even after an Exception has raised

Categories