Java - Periodic updates from MySQL - java

I am developing a Scoreboard Java application for my work. It uses MySQL to store the score values and a Java application accesses them and displays them on a projector. So far I have managed to create a Java application using Swing. I display all of the scores using jLabels so that they can be updated without completely redrawing the scoreboard.
Now, I need to get the scoreboard to update periodically. I have attempted to use Thread.sleep but I don't know how to interrupt the thread. The reason I need to interrupt the thread is that if the number of entries to display on the scoreboard is changed on the config panel, the scoreboard must redraw in order to display the right nummber.
Currently sleep works fine in the code so long as I don't touch anything. But as soon as I change anything in the ConfigPanel things go awry.
package au.thewebeditor.scoreboard.apps;
import java.lang.*;
public class Program {
private static Scoreboard sb;
private static ConfigPanel cp;
public Program(){
sb = new Scoreboard();
cp = new ConfigPanel();
}
public static void redrawScoreboard() throws NullPointerException{
try{
sb.dispose();
} catch (NullPointerException e){
//DO NOTHING
}
sb = new Scoreboard();
try {
cp.toFront();
} catch (NullPointerException e) {
cp = new ConfigPanel();
}
constUpdates();
}
public static void showConfig(){
cp.setVisible(true);
cp.toFront();
}
public static void main(String[] arguments){
new Program();
constUpdates();
}
private static void constUpdates() {
boolean go = true;
while (go){
try {
Thread.sleep(5000);
Scoreboard.updateScores();
} catch (InterruptedException e) {
//DO nothing
}
}
}
}
When the connfiguration has been changed redrawScoreboard() is called.
At the moment, when redrawScoreboard is called it just sits in the queue while constUpdates keeps counting to 5000. How do I interrupt the sleep so I can redraw the scoreboard. Is sleep even the best option here? Or should I try something else?

you have an issue with Concurency in Swing, any create, update, modify Swing GUI must be done on Event Dispatch Thread, maybe reason for wrapping sb.dispose(); into try - catch by throws NullPointerException
Swing GUI must be created on Initial Thread
there no reason to recreate a new Top-Level Container every 5th. seconds, reuse JComponents added to contianer on app's start_up
use util.Timer to invoke SwingWorker,

Calling constUpdates takes the current thread an puts it in an infinite loop.
If the config UI is calling it it will put the UI thread in an infinite loop.
It'd be better just to have that loop in the main function.
Should you get an InteruptedException you should break out of the loop, not keep going.
You've a heady mix of static and non-static things, try to make it so that objects get passed arround instead.
If you want the config to ask the scoreboard to redraw, pass it the scoreboard so it can call redraw directly and leave the polling alone.

Related

Running Threads and Controlling Them

I need help figuring out how to code this problem I am running into.
I'm creating an elevator simulator. I want to run each Elevator object in separate individual threads. I want to control them with my ElevatorController object. I am imagining the Elevator threads sitting in IDLE and then switching to UP or DOWN when the ElevatorController tells it to.
I have created the Elevators and put them into an ArrayList that is stored in the Building object.
What do I do next? My objective is to make elevator1 go to Floor 11. While elevator1 is moving I need to tell elevator2 to go to Floor 14. As elevator2 is moving to Floor 14, I need to tell it to go to Floor 13 first.
I'm unsure how I am supposed to create these threads and still have a reference to the elevator objects in these threads, so I can tell them the new destination.
I'm new to multithreading.
Define each thread as a field in your building so you can access it later. I would do something like:
public class ElevatorThread extends Thread {
public void run() {
while(!this.interrupted()) {
synchronized(this) {
try {
this.wait();
} catch (InterruptedException e) {
return;
}
}
elevatorThreadRunnable.run();
}
}
Runnable elevatorThreadRunnable;
public void setRunnable(Runnable runnable) {
elevatorThreadRunnable = runnable;
synchronized (this) {
this.notify();
}
}
}
If we define the ElevatorThreads as an array it gets even easier. We can simply:
building.elevatorThreads[0].setRunnable(new Runnable() {
public void run() {
...
}
});
Where:
//I belong in the Building constructor!
Thread[] elevatorThreads = {
new ElevatorThread(),
new ElevatorThread(),
new ElevatorThread()
//number of elevators in building
/*
this could be simplified to a method that takes the number of threads as an int
and returns an inflated array, but that is outside the context of this answer
*/
};
If we do this, our Runnable is ran in the elevator thread of your choosing. The thread will also idle like you requested, until a new Runnable is set.
To kill a thread, we call ElevatorThread.interrupt();, this will cause the thread to stop wait()ing if it is, and then break out of our execution loop; killing the thread.

JavaFx Progress Indicator freezes when I run another thread

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.

Applets - init(), EDT and threads

Java is not my mother tongue and I've been fighting with this problem for a little while.
Basically, I am finding a behavioural difference between calling method switchApplets() directly from init(), and calling it from within a new thread spawned by init().
The consequence of calling it from inside the new thread is that the new applet whitescreens -- until/unless the user resizes or minimizes their browser. If called at the end of init(), the new UI renders immediately without any input from the user. But that's not an option because it doesn't wait for the thread to finish its prep work.
Trimmed-down code:
public class PreLoader extends Applet implements AppletStub {
static JProgressBar pBar = null;
static JLabel message;
public void switchApplets() {
try {
Class main_class = Class.forName("MainClass");
Applet main_applet = (Applet)main_class.newInstance();
removeAll();
setSize(0,0);
setLayout(new GridLayout(1,0));
add(main_applet);
main_applet.init();
main_applet.start();
main_applet.setStub(this);
}
catch (Exception e) {
}
}
public void init() {
pBar = new JProgressBar(0, 100);
pBar.setValue(0);
pBar.setStringPainted(true);
message = new JLabel("Beginning work!");
add(message);
add(pBar);
FlowLayout flow = new FlowLayout();
setLayout(flow);
Thread t = new Thread ( new Runnable () {
public void run ()
{
longRunningFunction1();
longRunningFunction2();
message.setText("Work complete! Stand by..");
switchApplets(); //does NOT work as intended from here
return;
}
} );
t.start();
//switchApplets(); //works as intended if called HERE
}
public void longRunningFunction1() {
//perform some tasks, advance progress bar
}
public void longRunningFunction2() {
//perform some tasks, advance progress bar
}
public void start() {
return;
}
public void appletResize(int width, int height) {
return;
}
}
I tried making init() wait for the thread to finish so that I could call switchApplets() from there, but that only blocked the EDT and prevented the UI from updating. Also tried playing with SwingUtilities' invokeLater/invokeAndWait, but even though switchApplets() gets run on the EDT, it seems that it MUST be called directly from init() (or at least the thread init is running on) to have the desired effect.
Why does calling switchApplets() from within a new thread result in a slightly different (and unwanted) UI behaviour?
The consequence of calling it from inside the new thread is that the new applet whitescreens -- until/unless the user resizes or minimizes their browser.
It's likely a deadlock caused by trying to do UI code on the wrong thread.
I tried making init() wait for the thread to finish so that I could call switchApplets() from there, but that only blocked the EDT and prevented the UI from updating.
You're on the right track. You need to call switchApplets() only from the EDT, and only after the work is done on the other thread.
Are you sure you tried using invokeLater() or invokeAndWait() from within the spawned thread after the long running functions were done? It's been a long while since I did applets but I'm not aware of any applet-specific reason why it wouldn't work, and it would work in any other case. I.e.,
public void run()
{
longRunningFunction1();
longRunningFunction2();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
message.setText("Work complete! Stand by..");
switchApplets();
}
});
}
However, the most proper way to do this is with a SwingWorker rather than a manually created thread. SwingWorker (which is not nearly as well-known as it should be) is designed exactly for the goal of performing background tasks on a separate thread while still being able to update the GUI with progress updates and the results. E.g.,
new SwingWorker<Void,Void>() {
#Override
protected Void doInBackground() { // is called on a background thread
longRunningFunction1();
longRunningFunction2();
return null;
}
#Override
protected void done() { // is called on the Swing thread
message.setText("Work complete! Stand by..");
switchApplets();
}
}.execute();
The Void stuff is because SwingWorker is also capable of returning results and sending intermediate progress updates, but this example doesn't use those features.
You indicated that your long running functions are also updating a progress bar. That's another thing that should happen only on the Swing thread. In practice you can often get away without it, but it's dodgy. Your progress updates can use one of the SwingUtilities.invoke methods, or the mechanisms of SwingWorker; either should work. (SwingWorker itself provides two different ways to do it: Call addPropertyChangeListener (Swing thread) and setProgress (background thread), or call publish (background thread) and override process (Swing thread).)
Also, a small suggestion: if it's inconvenient to deal with a checked exception (or impossible to usefully do so), rather than catching and ignoring it, you should at least catch & rethrow it as an unchecked exception:
catch (Exception e) {
throw new RuntimeException(e);
}
That way, the stacktrace and error message of any exception will not be lost.

Adding a repeat function to a simple MIDI player

I am trying to implement a repeat function on a custom MIDI player, but I am unable to implement a repeat function. Here are the classes I am using:
NotePlayer - Plays MIDI notes using Java's MIDI package.
GuitarTunerGUI
Interface to the NotePlayer class.
Provides six JButtons for each guitar string, a JComboBox for selecting the desired tuning, and a JCheckBox for toggling the repeat function.
Provides toggleRepeat() for toggling the repeatEnabled field, a private field of the GuitarTunerGUI class.
I created a SwingWorker that is responsible for playing a MIDI note in a separate thread. This solves the issue of keeping the GUI responsive while the note is being played.
However, a problem arises when repeat is enabled and the user pushes more than one button.
When the user pushes one of the six JButtons the listener does the following:
public void actionPerformed(ActionEvent event) {
// The note param is a private field of the listener object
MusicianWorker clapton = new MusicianWorker(note);
clapton.execute();
}
The execute method does the following:
protected Void doInBackground() throws Exception {
do {
NotePlayer.playNote(thisNote);
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
} while (repeatEnabled);
return null;
}
An issue arises when the user pushes multiple buttons without toggling repeat. For example, when the 'A' button and the 'E' button are pushed sequentially, two threads are created and the 'A' and 'E' notes are both played repeatedly until repeatEnabled is toggled off. When the user pushes a JButton I need to first determine if any worker threads are currently executing and, if so kill those threads before playing the specified note. Thanks in advance for your time and feedback.
You need to maintain shared state between your workers. Introduce new boolean variable "playing". Before execution check whether playing flag is set to true, after execution set it to false again.
The code you have given is great, it just needs to be tweaked a little bit. When you create your SwingWorker, you should keep track of it in an instance variable (maybe in a List if you are going to be wanting to play multiple notes at some point?). Then, before playing a new note you check to see if the last note has finished, and if not, you cancel it.
Whether or not cancellation will have any effect on your MusicianWorker is up to you. The worker thread will be interrupted, which would mean that your Thread.sleep method would prematurely terminate if it is running - you would have to check your docs to see what effect it would have on NotePlayer.
Lastly, it seems that you don't actually need to be using the SwingWorker at all, since your background task is not interacting with the UI. You might want to investigate Executors.
You could try something like this:
public class AlbertHall {
private final ExecutorService es = Executors.newSingleThreadExecutor();
// No longer a local variable in the listener
private Future<Void> clapton; // ... or maybe a Collection of Futures
private class Listener implements ActionListener {
private final Note note;
public Listener(Note note) {
this.note = note;
}
public void actionPerformed(ActionEvent event) {
// Watch out, Clapton may finish after you have asked if he is done
// but before you call cancel
if (clapton != null && !clapton.isDone()) clapton.cancel(true);
// You may need to have a wait loop here if Clapton takes a while
// to leave the stage
// Next note
clapton = es.submit(new MusicianWorker(note));
}
}
static class MusicianWorker implements Runnable {
private final Note note;
public MusicianWorker(Note note) {
this.note = note;
}
public void run() {
boolean cancelRequested = false;
do {
NotePlayer.playNote(thisNote);
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
// Looks like we got cancelled
cancelRequested = true;
}
} while (repeatEnabled && !cancelRequested);
}
}
}

Delay displaying of fields in manager (blackberry)

I have a loop adding fields to a manager, I am trying to delay the time between when each field is painted onto the screen. I have been trying below code but it just paints the manager when all fields have been added to it.
Is this possible ?
manager.add(field);
manager.invalidate();//force a repaint of the manager
Thread.sleep(1000);
Thanks
Invalidate doesn't necessarily force a paint, it simply says that on the next paint the Field (or Manager in your case) needs to be redrawn. It's a subtle difference but it could be causing the confusion. What you might want to try is calling Screen.doPaint(), which will force the entire screen to redraw. Also, putting the sleep() in your Event Thread won't help, because painting is also done on the same Thread.
If you are trying to sequentially add Fields to your Manager with this second delay, you should put this logic in its own Thread and do synchronized(UiApplication.getEventLock()){//add fields} when you call manager.add(field). Then you can call your Thread.sleep(1000) to correctly have the delay in displaying. Also, just as a some added info, calling add() inherently causes an invalidate() call, so you don't need to add it. Here's a simple example of the second delay in adding
protected void addDelayedFields() {
Thread t = new Thread( new Runnable() {
public void run() {
for(int i=0;i<SOME_LIMIT;i++) {
synchronized(UiApplication.getEventLock()) {
manager.add(new LabelField(i.toString());
}
try{
Thread.sleep(1000);
}
catch(Exception e){ }
}
}
});
t.start();
}
The painting should occur after the add(), but if it doesn't you can also make a call to yourScreen.doPaint()

Categories

Resources