I have an app that connects reads file on remote server. File dynamically updates that's why I use Timer class to reread this file periodically.
Workflow is the following:
Open window where text will be displayed.
Start reading file (reread once per 15 sec using Timer)
In 15 seconds window is filled with data or I receive exceptions in log. Exceptions are suppressed and I continue trying to read data.
Exceptions are my problem, because user doesn't know what is happening now with an app.
There are at least two Exceptions I ran at:
- If file is absent, I receive FileNotFoundException.
- If server is on maintenance I receive other Exception (I catch it, so its name doesn't matter).
Here is how above looks like in code:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final RemoteReader reader = new RemoteReader();
Timer timer = new Timer(15000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
reader.getMainPanel().readData();
} catch (IOException e) {
//Here is a counter that increases after each exception throw
if(counter >5) {
JOptionPane.showOptionDialog(chat,
e.getMessage(),
e.getClass().getName(),
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.INFORMATION_MESSAGE,
null,
new String[]{"Retry", "Cancel"}, //on Retry - make another 5 tries to read file, on cancel - close the window
null);
counter = 0;
}
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
});
timer.start();
}
});
}
public String readData() throws IOException {
// read and process data before returning it
// but for test purpose:
//BufferedReader br = new BufferedReader(new InputStreamReader(this.url.openStream()));
throw new IOException("cannot read file");
}
What I want to do is to add JProgressBar. On openning the main window progress bar appears and then data is read. If IOException throws 5 times in a row, show option dialog. Otherwise hide progress bar and show data. If remote file becomes unavailable, show option dialog. And pressing the retry button, show progress bar... then workflow starts from the very beginning.
Some code examples would help me, but I don't expect solution for the whole issue - advice, how it should be done in right way from design point of view will be enough. Samples of Oracle a little bit vague for me.
Even if WatchService, seen here, is not available, I'd still use SwingWorker, seen here and here.
You have considerable latitude in pacing the doInBackground() thread, e.g. Thread.sleep(15 * 1000).
You can setProgress() in the background and listen for any PropertyChangeEvent in the GUI.
You can update any GUI components in process(), which runs on the EDT.
Related
I am trying to set the calculator text to an error message, wait for 2 seconds, then clear the field text. Below is my current code.
public static void wait(int ms) {
try {
Thread.sleep(ms);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
// TODO: 6/30/22 if decimal is clicked multiple times -> setText("Error")
if (field.getText().contains(".") && e.getSource() == decButton) {
field.setText("error text here");
wait(1000);
field.setText("blank here");
} else if (e.getSource().equals(decButton)) {
field.setText(field.getText().concat("."));
}
So far, the field text sets directly to ("blank here") and completely skips the error message. I have tried moving the error message to different areas of the program (within the if statement) but have yet to find a conclusion.
Are you using Swing? Is the code you show running from an event handler (triggered by keypress, mouseclick or something)? Then this cannot work.
The code is running in the Event Dispatcher Thread (EDT). Once you use something like field.setText() you have to exit your code and allow the EDT to fire the updates into the UI. But instead, you 'keep it busy' by waiting a second, then requesting the next update into the UI. Only then your method exits, and the user did not see the first message.
What you need to do is to set the update, then free up the EDT. How do you get the message to disappear one second later? Use a Swing Timer to trigger the action:
field.setText("error text here");
ActionListener errorHider = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
field.setText("blank here");
}
};
Timer t = new Timer(1000, errorHider);
t.setRepeats(false);
t.start();
While using SwingWorker to sequentially update my front end, I think I may have found a bug.
I have create a server which listen for connections. A connection might only initiate in a few minutes, so I created a little connecting animation that plays while the server is waiting for a new incoming connection.
A SwingWorker<T,T> is used to update a JLabel every 0.5 seconds with a character, which in the end, creates a character that bounces from one end of the panel to next, upon connection, the animation is removed and the JLabel is updated with different text.
This has been working all fine and dandy, but I have recently realised that the animation sometimes freezes, but yet, every time I ran the animation, it stopped and the JLabel updated correctly.
I then realised that the JLabel text does not update when I am on a different workspace.
(Edit: Where workspace refers to the ability to switch between desktops (workspaces) in Linux (I'm on Linux Mint 15). See: http://linux.about.com/library/gnome/blgnome2n4a.htm
I tested and retested:
Test A:
Start listening for connections
Application Animation starts (powered by SwingWorker)
Keep app open on current workspace
Client connects and animation correctly disappears
Jlabel correctly updates to new text.
Test B:
Start listening for connections
Animation starts (powered by SwingWorker)
Move to a different workspace by pressing Ctrl+Alt, (arrow key) and leave app on previous workspace.
Wait until client connects and move back to workspace containing app.
Jlabel froze and DID NOT update to new text.
This is where it gets weird: If I select a JMenuItem, which will in turn overlap with the animation, the overlapping part of the text will then UPDATE partially to the correct text.
I have not tested this on another Linux distribution, but will do so after I get feedback that my code is not the cause of this problem.
CODE:
ConnectingAnimation will be running all the time, but only updating the animation when the server is not connected.
public class ConnectingAnimation extends SwingWorker<Void, String> {
#Override
protected Void doInBackground() throws Exception {
StringBuilder sb = new StringBuilder();
while (true) {
while (!connected) {
try {
...
//Append text to StringBuilder
...
//publish updated text to be displayed next
publish(sb.toString());
//Sleep
Thread.sleep(100);
} catch (InterruptedException e) {
return null;
}
}
//Waits until clientConnected() is called
try {
synchronized (clientConnection) {
clientConnected.wait();
}
} catch (InterruptedException e) {
logger.logException(e);
}
}
}
#Override
protected void process(final List<String> chunks) {
for (final String update : chunks) {
if (connected) {
connectionStatusLabel.setText("Client Connected");
break;
}
connectionStatusLabel.setText(update);
}
}
}
clientConnected() will be called by an external module notifying the animation to stop
public static void clientConnected() {
...
synchronized (clientConnection) {
connected = true;
clientConnection.notify();
}
}
So I have been trying to implement a progress indicator with no luck. I am not sure I understand managing threads with JavaFx very well, despite having read a bit about the Platform.RunLater and Tasks. So here is my use case.
My program allows users to connect to a database and look at some of the schemas and other objects in the database. Sometimes connecting to a large database and pulling up all its tables and info takes a while, so I would like to show a progress indicator. I am not trying to update the progress at all I would just like to make the progress indicator visible at a value of -1 while the process is running to pull everything from the database. Ideally I will have a progress indicator loaded in from an FXML file invisible. When I start the process of pulling info from the database I would like to make it visible.
When trying to make my progress visible it never showed up, so I decide to start out having it visible and making it invisible, just to see what happens. The progress indicator rotated nicely when I opened the program up, but as soon as I try to connect to the database it stopped rotating and just froze. I assume this is what happens when I try to make it visible too which is why it was never showing up.
The following is my current code, I would appreciate any detailed help with explanations so I can understand what is going on. Thanks
from the method that is doing most of the work.
//make progress indicator visible
pi.setVisible(true);
// separate non-FX thread
ExtractorThread t = new ExtractorThread();
t.setCp(cp);
t.start();
//Wait until the thread is done
try{
t.join();
}
catch(Exception e){
e.printStackTrace();
}
//Retrieve the dbextractor from the thread
DbExtractor dbe = t.getDbe();
//move on to the next page in the application
this.caster.goToDataSource(c, cp, dbe);
The ExtractorThread which does the work.
private class ExtractorThread extends Thread{
private ConnectionProperties cp;
private DbExtractor dbe;
public void run() {
dbe = new DbExtractor(cp);
try {
dbe.extract();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public DbExtractor getDbe() {
return dbe;
}
public void setCp(ConnectionProperties cp) {
this.cp = cp;
}
}
If I am supposed to use the Platform.RunLater I am not sure where to use it or why. Any help would be appreciated, thanks!
Use the javafx.concurrent API. Extend Task instead of Thread:
private class ExtractorThread extends Task<DbExtractor>{
private ConnectionProperties cp;
public DbExtractor call() throws Exception {
dbe = new DbExtractor(cp);
dbe.extract();
return dbe;
}
public void setCp(ConnectionProperties cp) {
this.cp = cp;
}
}
Then do:
//make progress indicator visible
pi.setVisible(true);
// separate non-FX thread
final ExtractorThread t = new ExtractorThread();
t.setCp(cp);
t.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
public void handle(WorkerStateEvent event) {
DbExtractor dbExtractor = t.getValue();
this.caster.goToDataSource(c, cp, dbe);
}
});
t.setOnFailed(...); // similarly, to handle exceptions
new Thread(t).start();
I don't code JavaFX, and so I can't give you chapter and verse, but this line:
t.join();
will block the calling code until the background thread is through. Don't do this. Instead use some type of listener to get notified when the background thread finishes. If this were Swing, I'd use a PropertyChangeListener added to a SwingWorker to notify me when the background thread was done. I think that you can still use a PropertyChangeListener to do a similar thing with with JavaFX, but I cannot tell you if this would represent the canonical solution.
Also, don't extend Thread but instead implement Runnable. This won't fix your problem but is basic Java common sense.
I am working on a webscraping tool that should perform various operations with the scraped data.
Because of this, I need various different GUIs to work in an orderly manner and because of that, I need the main method to wait before each has completed it's purpose.
After searching for a while, I have found the following StackOverflow questions that provided some clues on how to solve the problem, but that I could not implement because they have some differences to my case:
How to wait for input in a text field
How to make main thread wait a different thread to finish
I know I can trigger code using a Listener to a/the GUI's components (a button, for example), but i'm having a hard time making the main-thread wait for that listener to wake it up, while the code for the GUI's thread (when there is one) is initialized by the main thread...
This is an simplified code to demonstrate how the program is supposed to work:
public class Main {
/*
* Waiter is a simple GUI with just an "Start" button in it. Here in place of my actual GUIs.
*/
private static Waiter auth; //Represents my NTLM-authentication form.
private static Waiter status; //Represents a status-feedback GUI that will be displayed during processing.
private static Waiter operation; //Represents a GUI in with the user choses what to do with the gathered data.
public static void main(String[] args) throws InterruptedException {
auth = new Waiter();
auth.setVisible(true);
System.out.println("NTLM Authentication form. Should wait here until user has filled up the GUI and clicked \"Start\".");
System.out.println("Authenticates WebClient's NTLM using data inputed to the GUI...");
auth.dispose();
Thread srt = new Thread(status = new Waiter());
srt.start();
status.setVisible(true);
//Performs webscraping operations...
System.out.println("Prepares the webscraped data here...Things like downloading files and/or parsing text...");
System.out.println("Keeps the user aware of the progress using the \"status\" GUI.");
status.setVisible(false);
//Clears the status GUI.
operation = new Waiter();
operation.setVisible(true);
System.out.println("Operation selection form. Should wait here until user selects an option.");
System.out.println("Starts performing the operation(s)...");
operation.dispose();
status.setVisible(true);
System.out.println("Performs the operation(s), while giving status-feedback to the user.");
status.setVisible(false);
System.out.println("Displays a file-save dialog to save the results.");
System.out.println("And finally, displays a \"End of operations\" dialog before ending.");
}
}
UPDATE 1:
The main difficulty I'm having is to implement something like this (this is what I want to do):
//Main method...code...
Thread srt = new Thread(status = new Waiter());
//Before "srt.start();"...
status.startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
main.continueExecution();
}
});
//Thread's run() being something like "status.setVisible(true); main.waitGUI();"
srt.start();
//continues here after the Listener is triggered...more code...
Instead of this (what is being the solution to most other people, if I'm understanding it right...) (this is what I don't want to do, if possible):
//GUI before this one...
//code...
Thread srt = new Thread(status = new Waiter());
status.startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
/*
* Code that should come after this GUI.
*/
}
});
//Thread's run() being something like "status.setVisible(true);"
srt.start();
//"ends" here...(Initial code or GUI before this "status")
In other words, I'm having trouble implementing the GUIs and Listeners in a way to trigger main's thread's "sleep" and "wake up" actions, instead of triggering actual processing code.
UPDATE 2:
Following #JB_Nizet 's tip on SwingUtilities.invokeLater(), I took a good look at the SwingUtilities docs, and after I found out about how the SwingUtilities.invokeAndWait() method works, and I think I've found how to do it, using a combination of Semaphore and invokeAndWait().
I need someone with a better understanding of multi-threading and/or GUIs to confirm if it's a safe, valid solution or not. (I'll then edit the question and clean it up, and if confirmed, post this in proper "answer format")
Anyways, here goes the modified code, which seems to be working for me:
public class Main_Test {
//Semaphore:
public static Semaphore semaphore;
//GUIs:
private static Waiter auth; //Represents my NTLM-authentication form.
public static void main(String[] args) {
try {
semaphore = new Semaphore(1);
// semaphore.acquire();
auth = new Waiter() {
#Override
public void run() {
try {
System.out.println(Main_Test.getThread() + this.getName() + " has been created and is now running.");
semaphore.acquire(); //Makes main pause.
this.setVisible(true);
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
auth.jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(getThread() + "NTLM has been hypothetically authenticated.");
semaphore.release(); //Makes main continue after GUI is done.
auth.dispose();
}
});
// semaphore.release();
SwingUtilities.invokeAndWait(auth);
semaphore.acquire(); //<- Where the main effectively gets paused until the permit is released.
/*
* GUI's run() will accquire the semaphore's permit.
* The invokeAndWait() garantees (?) it will happen before main's acquire().
* This causes the main to pause when trying to acquire the permit.
* It stays paused until the actionListener release() that permit.
*/
System.out.println(getThread() + "This message represents the processing, and should come only after the hypothetical NTLM authentication.");
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static String getThread() {
return String.format("%-32s --- ", Thread.currentThread().toString());
}
}
I'm not sure I have completely understood what you want to do, but it seems to me that you have a consumer thread (the main thread, waiting for events from the event dispatch thread), and a producer thread (the event dispatch thread).
The typical way to implement this is to use a blocking queue as a communication mechanism:
Create a blocking queue
Create your GUI and pass it the blocking queue
start a loop which gets data from the queue. Since the queue is blocking, the main thread will be blocked untile there is something in the queue
Have your event listeners, running in the EDT, post data to the blocking queue
I am really unfamiliar with working with threads, so I was hoping someone could help me figure out the best way to do this.
I have a JButton in my java application...when you click on the button, I have a Process Builder that creates a process which executes some external python code. The python code generates some files, and this can take some time. When the python code is done executing, I need to load those files into an applet within my Java application.
In its current form, I have a p.waitFor() within the code that calls the external python file...so when you click on the button, the button hangs (the entire application hangs actually) until the process is done. Obviously, I want the user to be able to interact with the rest of the application while this process is going on, but as soon as it's done, I want my application to know about it, so that it can load the files into the applet.
What is the best way to do this?
Thanks for your help.
You should use SwingWorker to invoke the Python process on a background thread. This way your UI will remain responsive whilst the long-running task runs.
// Define Action.
Action action = new AbstractAction("Do It") {
public void actionPerformed(ActionEvent e) {
runBackgroundTask();
}
}
// Install Action into JButton.
JButton btn = new JButton(action);
private void runBackgroundTask() {
new SwingWorker<Void, Void>() {
{
// Disable action until task is complete to prevent concurrent tasks.
action.setEnabled(false);
}
// Called on the Swing thread when background task completes.
protected void done() {
action.setEnabled(true);
try {
// No result but calling get() will propagate any exceptions onto Swing thread.
get();
} catch(Exception ex) {
// Handle exception
}
}
// Called on background thread
protected Void doInBackground() throws Exception {
// Add ProcessBuilder code here!
return null; // No result so simply return null.
}
}.execute();
}
You really want to create a new thread for monitoring your new process. As you've discovered, using just one thread for both the UI and monitoring the child process will make the UI seem to hang while the child process runs.
Here's some example code that assumes the existence of a log4j logger which I think will illustrate one possible approach...
Runtime runtime = Runtime.getRuntime();
String[] command = { "myShellCommand", "firstArgument" };
try {
boolean done = false;
int exitValue = 0;
Process proc = runtime.exec(command);
while (!done) {
try {
exitValue = proc.exitValue();
done = true;
} catch (IllegalThreadStateException e) {
// This exception will be thrown only if the process is still running
// because exitValue() will not be a valid method call yet...
logger.info("Process is still running...")
}
}
if (exitValue != 0) {
// Child process exited with non-zero exit code - do something about failure.
logger.info("Deletion failure - exit code " + exitValue);
}
} catch (IOException e) {
// An exception thrown by runtime.exec() which would mean myShellCommand was not
// found in the path or something like that...
logger.info("Deletion failure - error: " + e.getMessage());
}
// If no errors were caught above, the child is now finished with a zero exit code
// Move on happily