Search |
||
Event Based Programming in JavaFXPosted by dwalend on July 1, 2009 at 7:23 PM PDT
Old Song, New World I decided to try my hand at some JavaFX programming to see what the language had to offer. Two of the key features of JavaFX are its ability to bind to data, and its access to all Java libraries. I used that to see how it handles for event-based programming. I built this minesweeper game:
As the World Turns: Reactive Data Models
JavaFX let me build reactive data models using
I'm not completely convinced that TileControl -- and MVC-- is worth the extra class. I could have bound cell.state directly to a field in tileNode. It does prevent these few important lines from being lost in a sea of graphics code, and keeps the model from leaking into the verbose TileNode graphics code. More importantly, it lets a model of several layers, say rules for a more complex board game or some obscure business logic, propagate based on their declarations. An outer layer can define its own dependencies on the inner layer, so the system stays very clean. Old World Meets New: Event-based Programming and Clean Code I like event-based programming. It tends to keep class structures shallow and clean, and separates a program into understandable parts. When I throw in a way to distribute the events I can get multiple machines to form a coherent system, usually fairly painlessly. That minesweeper game shows the idea in JavaFX on a small scale. I used JMS to separate a Server, which knows where all the mines are, from a Client, which only knows what the player has uncovered. The client and server have no direct access to each other's objects; they are loosely coupled via JMS events. It's overkill for this little project, with one player, no reward (not even bragging rights) in the game, and client and server collocated in a single process. However, it'd make creating a distributed multiplayer game, or any other distributed system very easy. (To save me having to work with network connections on your web page, I've used SomnifugiJMS and colocated the Server and Client in the applet. It needs your permission to read system properties and to use JMX.) I set up some simple wrapper classes to handle the JMS calls. Nothing to write home about, but it does bundle up the boiler plate neatly. JavaFX doesn't do much with exception handling. I haven't spotted where uncaught exceptions go yet. (Maybe another blog there...) In any case, here's one of the four helper classes:
Earth To Mars
Once I'd typed the boilerplate, publishing events when something changed was easy with
Receiving Events... "Oh, Crap... Alien Thread" Inbound messages seemed like they'd be just as easy. They kind of worked in JavaFX 1.1, although I saw some screen twitching that reminded me of trying to run Swing-based code on the wrong thread. JavaFX 1.2 seems to spike the whole works and just did nothing -- no error message, just not responsive. I asked Josh for some help, and he sent this reply:
Just before I got that response, I found this two-year-old email from Tom Ball:
Aliens Among Us I normally prefer receive()s in my own threads to MessageListeners, but I didn't see a good way to use receive() or even receiveNoWait() without either polling or locking down the graphics thread with a blocking call. Using FX.deferAction() inside a MessageListener was pretty easy, and everything flowed from there:
The World Is Not Enough JavaFX is already doing some event-based programming in the background, single-threaded, on the graphics thread, using its single queue. The reactive data model is great, so long as it can live on the graphics thread along with everything else, without bogging things down. But bogging down the graphics thread was always one of the risks in AWT and Swing. JavaFX doesn't save us from that. Simon Morris posted an approach for building very clean parsers in JavaFX. If the program is only about parsing, that should work well. However, if you need the graphics thread for graphics, your JavaFX program might sputter or jam during the parse, or any other big computation or big i/o operation. World on a Thread Osvaldo Pinali posted a blog with a postscript about the power of automatic propagation through bind. Fabrizio Giudici's concerns about encapsulation I think are misplaced.* The great thing about bind is that when you create your objects' code, you don't have to predict how those objects will be used and build the corresponding boilerplate. Someone later uses bind when they want an update, binding to the fields they care about. It's getting back to OO's forgotten roots in message-passing, and taking a step beyond. Instead of being limited to API provided by a developer, you ask an object to send a message when something you care about changes. Osvaldo talks about his days in constraint programming. Propagation in constraint programming was tricky to get right. Mixing concurrency and propagation is even tricker. JavaFX solves this problem by only propagating changes on the graphics thread, alongside all the other graphics work. It can't take advantage of multiple threads and multiple cores; it can't dedicate one core to keep graphics responsive and use the rest for computation and i/o. The tail end of Tom Ball's email lays out a long term goal:
Josh says, * Fabrizio has a solid practical point, though. His example shows that some part of control flow and mutability is out of kilter. I'll keep my binds on defs, one-way only, for now. World of Tomorrow
Osvaldo Pinali's blog's main point was to open a discussion about what we need next in JavaFX. I think the ability to use JavaFX for big jobs beyond user interface work should be high on the list. »
Comments
Comments are listed in date ascending order (oldest first)
Submitted by dwalend on Mon, 2009-07-06 18:52.
Fabrizio,
Thanks for the report on firefox. I've realized "applets in Java in N browsers on M operating systems" is too many wheels in wheels to rely on. I'll try jnlp next time so my work only has to spin on top of Java and the OS.
var, def, public-init and bind play together in some interesting ways. bind with def is OK. It seems not so much encapsulation as clashing mutators. I haven't thought through two-way binds or bound functions yet. (Heck, this is my first javafx project.) Do you see something similar or something else?
Submitted by whp on Thu, 2009-07-02 04:31.
Exception: java.io.FileNotFoundException: JNLP file error: https://tokenarranger.dev.java.net/mines/mines_browser.jnlp. Please make sure the file exists and check if "codebase" and "href" in the JNLP file are correct.
JavaFX deployment bites again.
Submitted by dwalend on Thu, 2009-07-02 06:10.
whp,
Which browser? Which OS? Which versions? I've seen a lot of variability by OS and browser. Works for me on Safari - MacOS, Firefox - MacOS (asks for a password), IE - Windows. Haven't seen it work on firefox - windows or anything - linux yet.
Does it work now? (I updated the jnlp. It had a file URL. Now points somewhere more reasonable. Maybe a "feature" for the NetBeans plugin to add.)
Thanks,
Dave
Submitted by dwalend on Thu, 2009-07-02 06:43.
Now working on firefox - windows. Looks like the problem with linux is that the OS thinks its still March and I signed the .jars a few days ago.
Dave
Submitted by aleixmr on Fri, 2009-07-03 11:56.
Hey !! that's pretty crazy, still 10 years and we need to do things on invokelater !!! I don't like that asynchronous solution coz it gets your code messy and difficult to read ! I like the foxtrot aproach I use it for swing and works like a charm, synchronous solution !!!
(Please don't get me wrong, asynchronous callback is needed to !)
Submitted by dwalend on Fri, 2009-07-03 20:34.
Hi aleixmr,
I'm not that bothered by the asynchrony. The user is always on his own thread, so asynchrony is part of the UI puzzle. I like the "one thread for pixels" approach (although "one thread per display" might be better). Driving via interrupts (pre-Mac-OSX for example) I found much harder to get my head around. I think the FX.deferAction is clear, clean and very compact. I just wish we had queue.put(function) instead.
My long-lived complaint about having to use the graphics event thread was that the parts we have to use looked nothing like the rest of the system. There's no non-graphics event queue, non-graphics worker, or non-graphics invokeLater(), so the other concurrency puzzles get solved differently from Swing's. Foxtrot standardizes some other options for Swing UIs, but (1) you still have to learn all of Swing's rules to use it, (2) you have to learn Foxtrot's additional rules, and (3) it's still UI-only.
JavaFX brings us this very profound feature -- easy automatic propagation -- but the feature seems pinned to the graphics thread. Tantalizing.
Submitted by dwalend on Sat, 2009-07-04 09:09.
... queue.put() might be easy to do... I'll try the experiment.
Submitted by fabriziogiudici on Mon, 2009-07-06 00:54.
It took a while for me to understand what's happening, but it looks like FireFox 3.5 is pretty bad screwed out. While the first time I accessed this blog was with 3.0, and I could see the applet, with 3.5 I can't see the applet, and even worse the whole navigator is broken (can't edit the URL in the navigation bar, can't write anything in this comment box; I have been forced to use Safari). I think it's the fault of FireFox 3.5, since it is plagued by a high number of bugs.
Back to the topic.
"Osvaldo Pinali posted a blog with a postscript about the power of automatic propagation through bind. Fabrizio Giudici's concerns about encapsulation I think are misplaced.*"
Well, it depends. Seeing binding as message passing is a great idea as you can define the bindable structures as messages independent of the internal state of the object. Binding to internal state can be a trouble, and that's where my concern is. As usual, we have to distinguish from the binding feature (a powerful tool) and the use people make of it.
|
||
|