SecondaryLoop in JavaFX, like Swing? - java

I have a Java Swing app which I'm investigating if it is even possible to port to JavaFX. The app is a development environment and simulator for an internally used scripting language. The interesting thing about it is you can set breakpoints for this scripting language and step through it, like any programmer would expect for a language.
Now because the language in the simulator is interpreted, deep within the execution of the interpreter, when it hits a breakpoint, it can pop back to the gui with a Java Swing SecondaryLoop class. So when the breakpoint is hit, it calls secondaryLoop.enter(). The gui is then active for the user to inspect variables and gui components are active. When the user hits "Continue" in the program, it calls secondaryLoop.exit() to continue execution of the interpreter. It wouldn't really be feasible for the interpreter to unwind it's entire state to go back to the primary loop, and then take up where it left off at exactly the same point. That's why the SecondaryLoop is invaluable in making it work.
Is this possible in JavaFX?

Yes, it's possible. You need to use the enterNestedEventLoop and exitNestedEventLoop methods (they are inside the com.sun.javafx.tk.Toolkit class). See this usage example:
// Make sure to import the FX Toolkit first
import com.sun.javafx.tk.Toolkit;
// This object will be used as a unique identifier to the nested loop (to
// block the execution of the thread until exitNestedEventLoop is called)
final Object loopLock = new Object();
// Simulate a long process thread (DB call, download, etc)
Thread longProcess = new Thread(new Runnable()
{
#Override
public void run()
{
// Sleep for 12 seconds to simulate a long process
try
{
Thread.sleep(12000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
// Setup a result to pass back to the enterNestedLoop() caller
String result = "Result of this long process";
// We are now done. Call exitNestedEventLoop() to unblock
// the enterNestedLoop() caller. This needs to run from
// the FX Thread so use Platform.runLater()
Runnable fxRunner = new Runnable()
{
public void run()
{
try
{
Toolkit.getToolkit().exitNestedEventLoop(loopLock,
result);
} catch (Throwable t)
{
t.printStackTrace();
}
}
};
Platform.runLater(fxRunner);
}
});
// Start that long process from the FX Thread
longProcess.start();
// The next call will block until exitNestedEventLoop is called, however
// the FX Thread will continue processing UI requests
Object result = Toolkit.getToolkit().enterNestedEventLoop(loopLock);
// Next statement will print: "Result of this long process"
System.out.println("Result is: " + result);
Now, before you use this be warned of two important things:
The com.sun.javafx.tk.Toolkit class is not part of the public API, so Oracle reserves the right of removing it without notice. I've been using it just fine from Java 7 to 8u51 so they could stay there forever, change package/names or disappear completely (unlikely).
Nested loops (and Swing's secondary loops) are great for flexibility and small applications but overusing them often comes with a price. Nesting to many loops (huge stack trace) will often cause "strange" behaviour in your applications since initial parts of your code might end up waiting four or five things ahead completely unrelated to them. I've seen FX nested loops causing "empty" exceptions in FX WebEngine executeScript() calls and duplicating keyboard preprocessing (when pairing FX+Swing) among other problems.
That said I would recommend using the javafx.concurrent.Task instead (if it makes sense). Using the Task class will require a bit more effort but I think it's the correct way of doing things and will probably save you lots of maintenance time.
For extra reference about the FX Task class see this great article: http://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm
UPDATE: enterNestedEventLoop and exitNestedEventLoop will be part of the Java 9 public API (Platform class), more info in JDK-8090865
Hope this helps!

Related

SecondaryLoop.enter() not blocking until exit() is called on the EDT

Summary
For some reason when I call SecondaryLoop.enter() on the AWT Event Dispatch Thread (EDT), it does not wait for SecondaryLoop.exit() to be called before unblocking.
Background
Since I think SecondaryLoop is not a very well-known class, I'll give a brief overview:
In general, it is a bad idea to have any long-executing or blocking code running on the EDT because then your app will not be responsive to any events until that code terminates. The EventQueue.createSecondaryLoop() allows you to create a new event loop that will handle events, allowing you to block the EDT without loss of responsiveness. This is what swing modal dialogs use to allow you to block your EDT while you wait for the dialog to be closed, but still allow controls on the dialog itself to be able to operate.
After creating your SecondaryLoop instance, you should be able to call enter() and it should block until exit() is called.
From the docs
This method can be called by any thread including the event dispatch thread. This thread will be blocked until the exit() method is called or the loop is terminated. A new secondary loop will be created on the event dispatch thread for dispatching events in either case.
I'm not entirely sure what it means when it says "or the loop is terminated" though. That could be my issue.
Test Code
The calling the enter() method on a thread other than EDT, blocks as I would expect:
System.out.println("Enter Loop");
Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter();
System.out.println("Done (we should never get here)");
Output:
Enter Loop
However, if we call it on the EDT, it blocks for about a second, but then continues on:
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter());
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
Output:
Enter Loop
Done (we should never get here)
Per the comment by tevemadar (thanks BTW), I have updated the code to prevent any sort of possible garbage collection issue:
//Storing loop in array as a quick hack to get past the
// "final or effectively final" issue when using this in the invokeAndWait
SecondaryLoop loop[] = new SecondaryLoop[1];
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> {
loop[0] = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
loop[0].enter();
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
//Just printing this to make sure that it is used after the invokeAndWait is done. This is just
//to make sure there isn't some sort of optimization thing that is deciding that we don't
//need this anymore and allowing the loop to be garbage collected
System.out.println(loop[0]);
Output:
Enter Loop
Done (we should never get here)
java.awt.WaitDispatchSupport#2401f4c3
So, while it was a good suggestion, that does not appear to be my issue.
This seems pretty contradictory to the documentation (and the whole purpose of SecondaryLoop to me. Am I missing something?
Environment
OS: Windows 10
Java:
C:\Program Files\Java\jre8\bin>java.exe -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
Update
On a hunch, I tried adding a timer that continually adds more events to the EDT loop. It seems that adding the timer keeps the loop alive and makes it blocking:
//Add a keep alive timer which adds an event to the EDT for every 0.5 sec
new Timer(500, null).start();
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter());
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
With that code, it hangs as I expect, and if I put in some code that calls the exit() method on the loop after some time, it terminates as I would expect. So it seems that the loop must terminate itself once it has gone a certain amount of time without an event (but only if it was originally triggered from the EDT for some reason...).
I suppose I can add timers that do nothing whenever I need to use this feature, but that is definitely more of a work-around hack than a fix in my opinion.
Figured it out (at least this specific problem, I still have some more related issues, but I'm hoping I can figure them out on my own).
I decided to start debugging around in the java source code and I realized that my thread was getting unblocked due to this segment in java.awt.EventQueue:
/**
* Called from dispatchEvent() under a correct AccessControlContext
*/
private void dispatchEventImpl(final AWTEvent event, final Object src) {
event.isPosted = true;
if (event instanceof ActiveEvent) {
// This could become the sole method of dispatching in time.
setCurrentEventAndMostRecentTimeImpl(event);
((ActiveEvent)event).dispatch();
} else if (src instanceof Component) {
((Component)src).dispatchEvent(event);
event.dispatched();
} else if (src instanceof MenuComponent) {
((MenuComponent)src).dispatchEvent(event);
} else if (src instanceof TrayIcon) {
((TrayIcon)src).dispatchEvent(event);
} else if (src instanceof AWTAutoShutdown) {
if (noEvents()) {
dispatchThread.stopDispatching();
}
} else {
if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
getEventLog().fine("Unable to dispatch event: " + event);
}
}
}
In my case src was AWTAutoShutdown, which resulted in my secondary loop to terminate before I called exit().
I found this article which explains that in order to make sure the event queue eventually terminates when all windows are disposed, awt determines when all components are no longer displayable and the event queue is empty, then waits for 1 second, then triggers the event with the AWTAutoShutdown class as the source which terminates the event queue and allows the JVM to terminate. That 1 second timeout is why I observed that it would hang for a little bit.
This explains why adding a timer made my code work (since I was adding an event every half second and the timeout for AWTAutoShutdown is 1 second, the event queue would be kept alive).
The use case for all of this is to basically create an EDT-safe semaphore that will allow events to keep being executed even when it's waited for on the EDT (which I use to display JavaFX dialogs from a Swing application and make it behave like a native swing modal dialog). So in my actual use case, this should work just fine (because there should always be some swing component displaying in my actual applications). However, I hadn't even actually tried out my actual use case. Being a big believer in TDD, I first focused on my JUnit tests, which didn't actually create any UI components.
So, I did a quick test with a little dummy app that does have a GUI, it works just fine. I'm just going to add my 500 ms timer into my unit tests and start it and stop it before each test.
After doing that, I'm still running into some issues with some of my tests, but my minimal verifiable example from my original question works just fine. I'll dig into the remaining test failures and hopefully figure them out on my own. If it seems to be a related issue, then I'll just add a new SO question and put a link to it here.

How to update a JavaFX GUI correctly while processing data

I'm trying to get into JavaFX for making first attempts in making GUIs with Java. Therefore I made a simple neural network which learns the XOR and displays the output in JavaFX. My question is - how can I update the GUI regularly while processing the data?
Everything I achieved so far is a single update in the GUI when the network finished learning. Even if I started the networking in a thread.
I expect that the right handed side of the GUI updates (circle change the colors in dependence of the output) regularly for each n epoch and not only once. The attached image shows the GUI before the network started.
I appreciate any help in advance.
JavaFX has an "Event Thread", which is responsible for handling button clicks, updating labels, and any other GUI-related tasks. When you call button.setOnAction(e -> doSomething());, when your button is pressed, doSomething() happens on the JavaFX thread. During the time that this is running, no other GUI events can occur. This means your interface will completely freeze, which leads to a bad user experience.
Also, you cannot perform GUI operations on any thread other than the JavaFX thread, or you will get an IllegalStateException. (Try calling Executors.newSingleThreadExecutor().execute(() -> label.setText("hello")); to see this in action)
Luckily, JavaFX provides methods to get around this.
First, and easiest, is to call your long-running method inside a new thread (perhaps with ExecutorServices as above), and when you need to modify the interface, wrap those calls in a call to Platform.runLater(() -> updateInterface());. This will post updateInterface() to the GUI thread, and will allow it to run.
However, this can be messy, so the preferred method is to use a Service.
Assume your long running calculation returns an Double, you create a class extending Service<Double>, override its createTask() method, and perform the calculation there, as such:
public class CalculationService extends Service<Double> {
#Override
protected Task<Double> createTask() {
return new Task<Double>() {
#Override
protected Double call() throws Exception {
return doCalculation();
}
};
}
}
Then, in your controller, declare a private final CalculationService service = new CalculationService();
In your controller's initialize() method, you can then bind the output of this service to anything you want. For example:
calculationDisplayLabel.textProperty().bind(Bindings.createStringBinding(service.valueProperty()));
// continuously updates the label whenever the service calculates a new value
Then, whenever you decide you want to start calculating again, you call service.restart() to interrupt the process if it is running, and start again from the beginning.
If you want to call code when the value changes, add a listener to the value of the service. For example, if you want it to recalculate as soon as it has finished, call:
service.valueProperty().addListener((obs, old, newValue) -> service.restart());
If in doubt, refer to https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Service.html

UI updates as expected in debug mode, but does nothing when run normally

I'm creating a simple game turn based game in NetBeans. After the initialization of the GUI it calls the function herosTurn() to which waits for the users choice and and creates the outcome of that choice from a separate class Hero. When I step through the code in Debug mode, I get correct outcomes, but if I just run the code nothing is ever appended to the Text Area unless I have the wait function constantly appending text while it waits for input. I've seen other questions similar to this but they all involved multi-threading, and I don't believe that is what I am doing. Any help would be greatly appreciated.
This is the main class:
package Flow;
import Forms.Battleinterface;
/**
*
* #author Steemo
*/
public class battle {
public static int hAct;
public static int gLife = 200;
public static void herosTurn() {
hAct = 0;
Forms.Battleinterface.biText.append("What will you do?");
while (hAct == 0){
// adding the line below makes code work but is ugly.
//Forms.Battleinterface.biText.append(".");
continue;
}
if (hAct == 1){
Entities.Hero.attack();
}
}
public static void main(String args[]) {
Battleinterface battleinterface = new Forms.Battleinterface();
Battleinterface.Start();
while (gLife > 0) {
herosTurn();
}
}
}
And this is the Hero() class that is in a separate package:
package Entities;
import java.util.Random;
/**
*
* #author Steemo
*/
public class Hero {
static Random hGen = new Random(54154454);
public static void attack() {
int hAtt = 0;
hAtt = hGen.nextInt(6) + 15;
Forms.Battleinterface.biText.append("\nYou swing your axe and do " + hAtt
+ " Damage!!!");
}
}
I am not attaching the class I use to generate the GUI (Battleinterface) because the GUI generates fine and the only other thing happening there is the passing of the input hAct.
If it is needed I can attach it.
Replace this code...
while (hAct == 0){
continue;
}
...with this instead:
while (hAct == 0){
try {
Thread.yield();
} catch (InterruptedException interruptedEx) {
// Log the interruption somewhere.
}
}
Assuming you're using AWT/Swing on some level? This is an infinite loop, preventing other threads from ever running. By doing this you never let the UI thread actually do any updating, which means it appears to hang. You may not be doing any threading on your own, but AWT/Swing comes with Threads built in to do various functions, and they need to periodically get CPU time to do their work.
The reason this works in debugging is because the debugger is pausing the herosTurn method as you're stepping through it, allowing the UI thread to do its updates (including getting input from the user), but when simply running your game, the herosTurn method never pauses, and that method is occupying 100% of the available CPU time for your app.
Finally, as Mike Clark mentions, you typically shouldn't write UI with infinite loops. Instead you define components, which trigger events. Your code is notified of those events and reacts appropriately. This is what is known as the UI's event model. If you're using Swing, the introductory info on working within the event model is covered here.
I also wouldn't typically use AWT/Swing for games, because of the complications of UI coding, rendering performance, and several other reasons relating to the reality that AWT/Swing were not built to be good tools for games. That being said, a turn-based game can work fine this way (because the rendering performance requirements are often much lower) if you're willing to delve into the UI code to get it done, in addition to a few other reasons which I've outlined in a previous answer.
Hmm, there might be an issue with flushing your text buffer to the text area. Try explicitly flushing your text buffer after every print to see if that makes a difference.
It seems to me that your program is stuck in a while loop in the method herosTurn() As long as that function has not returned, nothing is going to update if you program this game as a single thread application.
It works when you uncomment the Forms.Battleinterface.biText.append("."); line cos then you send a signal to the GUI every iteration which causes it to update.
Games usually have a main game loop from which all elements of the program are controlled. Maybe a change of your implementation strategy will help?
I'm not a games programmer, so this might not be the best approach, but why don't you try setting up a javax.swing.Timer that runs every, say, 100 milliseconds. In inside the timer action, you can write all the code which checks and advances the game state and generates output to the user.
I'd suggest collecting the user's input in a JTextField that is separate from the place where the game output is printed. If you want to know when the user presses enter to send what they've typed, you can register an ActionListener on the JTextField.
textField.addActionListener(yourListener);

Putting method calls into an ArrayList?

I was adding a loading bar feature to my program in Java and I was wondering if I could set a list of processes that need to be done, so that it knows how many processes it needs to complete.
For example, saving them as strings?
ArrayList<String> processes = new ArrayList<String>();
processes.add("CSVWriter.createFileOfCompany(\"Random Company\");");
processes.add("FileConverter.convertCSVToXLS(classPath +
\"/Output/Random_Company.csv\");");
for (int i = 0; i < processes.size(); i++) {
// run process
// update loading bar
}
These aren't real methods to my program, but they are pretty similar to what I want to accomplish.
I'm sure this isn't the best way, but I want to have some way to create a list like this so I know how many processes there are before I run them. I have a system set up to break down these processes even further and show their progress, so this bar is pretty precise at the moment, but I have to number each of the processes =/.
Maybe I'm just missing the point. Creating progress bars is totally new to me.
If there are any good articles on progress bar creation, feel free to send them my way as well. Keep in mind that I'm not using an actual swing-based GUI. This bar is all S.O.P text.
Many thanks,
Justian Meyer
Closures will hopefully be coming soon in the next version of Java, but until then you can use anonymous classes implementing a known interface:
List<Runnable> jobs = new ArrayList<Runnable>();
jobs.add(new Runnable() {
public void run() {
CSVWriter.createFileOfCompany("Random Company");
}
});
jobs.add(new Runnable() {
public void run() {
FileConverter.convertCSVToXLS(classPath + "/Output/Random_Company.csv");
}
});
for (Runnable job : jobs) {
job.run();
}
Here's a scheme that just came to my mind:
interface WorkProcess
void setWorkUnits(int units)
void setArguments(Object obj1,...)
void execute()
So you encapsulate all your tasks with an interface that does execute as per the classic command pattern; it's also told (see below) how much work that job will probably take. You'll probably want some mechanism to pass data into these tasks for them to work with.
class WorkProcessFactory
static WorkProcess makeWorkProcess()
static int getTotalWorkUnitsAllocated()
static synchronized int reportWorkDone(int units)
static void addProgressListener(ProgressListener xxx)
When you have a job to do, you ask the factory to churn out one of these processes for you. In doing so, it does the estimate on work units and passes that to the WorkProcess it just created. It also keeps a tally of the total of those units for setting up the top of the progress indicator. One cool side effect of this is that you don't have to wait for all your jobs to be prepared: You can increment the maximum value for your progress report every time you add a job even as jobs are being processed and newly created; your bar will always show a realistic ratio.
You can stuff your WorkProcesses into a queue of some sort. ExecutorService comes to mind if your WorkProcess also implements the Runnable interface (which is a good idea). You'll be at liberty to process those jobs sequentially single-file or concurrently - whatever your execution queue machinery supports.
The WorkProcess' execute() method basically wraps the run() method (or maybe the other way around, since ExecutorService expects a run() method but our work unit magic is in execute()); but when that's done, it conscientiously calls back to reportWorkDone with the number of units it was told its job was worth. Upon receiving these reports, the factory has an updated value for units of work done, which it can report back to the snooping ProgressListener. That, in turn, updates the bar in the GUI.
Done. (I think). Probably needs some refining.

JFrame not working after first instantiation?

As part of a larger application, I am writing a settings class, which collects and stores user-defined settings. This class is a singleton, and is instantiated during application startup.
In order to accept user input, two different GUI frames are insantiated from within ConfigSettings.java, from a public static method, selectSettings(). Both are subclasses of JFrame. Here is the code for the instantiation of the file selection dialog:
private void selectFile() {
SelectFileGUI fileSelector = new SelectFileGUI();
fileSelector.setVisible(true);
synchronized(this) {
try {
wait();
} catch(Exception e) {
e.printStackTrace();
}
}
fileSelector.dispose();
}
This works fine when the application is initially run. However, at a later point the user may alter their selected settings, including selecting a new source file. This is done by calling selectSettings() again.
The issue I'm having is that any subsequent attempt to instantiate and display these GUI components again results in a new JFrame being displayed, but with a grey background, and no buttons or other components shown. While debugging I was also failing to create new instances of SelectFileGUI directly.
What could be causing this sort of behaviour?
I would check to see if the second time you call it you are using the GUI thread or calling from one of your own threads.
At the top of that method you can test for it (The AWT thread is pretty easily identifiable by name) and have it throw an exception so developers know not to call it on the wrong thread--or you can block their thread and do it in a worker thread.
I don't know what is causing this behavior but in your code the following simply cannot possibly be the right way to manage dialogs (more below):
fileSelector.setVisible(true);
synchronized(this) {
try {
wait();
} catch(Exception e) {
e.printStackTrace();
}
}
fileSelector.dispose();
Do you want your dialogs to be modal or not?
If you want them to be modal, then you simply make a blocking call like when you're invoking JColorChooser.showDialog(...) method and your return "value" is your color/file/whatever.
If you want them non-modal, then you use a callback to get your color/file. In the JColorChooser dialog example, you'd call the createDialog(...) method and use the ok/cancel listeners as callbacks.
I suggest you take a look at sun's tutorial, for example the one on color chooser, to see how to correctly display a modal (or non-modal) dialog:
http://java.sun.com/docs/books/tutorial/uiswing/components/colorchooser.html
Once again, that synchronized(this) { try { wait() ... } to manage something as simple as a file selector/dialog frame just cannot be correct.
Agree with BillK: sounds like you're calling it from outside the EDT first time around (so your call to wait() doesn't block the EDT), then from the EDT the second time around. See SwingUtilities.invokeAndWait() and/or Dialog.setModal().
The consensus here is that you are breaking the rules governing the use of the AWT painting thread (the Event Dispatch Thread).
A couple things to note:
If your code attempts to paint your GUI components outside this painting thread, the gray dialog could be the result of a deadlock between the EDT and the thread your application is using to paint.
If you do get into this situation, you will experience the inability to create new dialogs as described.
However, as you mention that you are debugging while experiencing this problem, it might be that you have paused the EDT through your IDE.
Take a look at this tutorial for some guidelines on how use threads in a client application.
To fully appreciate the issue, it would be nice to see some more code - pertinent parts of selectSettings(), for example.

Categories

Resources