Sunday Nov 04, 2007
Tuesday Oct 16, 2007
By user13366078 on Oct 16, 2007
Now that I'm back from CEC and out of jetlag, I've had some time to clean up the CEC 2007 Message Prompter source code. Thanks to all those who asked for it, that was quite a motivation.
The CEC Message Prompter source code is free for your reading pleasure under an as-is basis, no warranty, no support, etc. Still, comments are of course very welcome.
The easiest way to try this out is to load up NetBeans (I use the current Beta 6), install the JavaFX module, then create a new JavaFX project. The stuff in the source code archive goes into the src subdirectory of your new JavaFX project. Choose "Main.fx" as the main class and feel free to enable Java Web Start.
After starting the app, you'll see the window above. To the top is the message source selection GUI. Choose whether you want to have a database or a URL (for XML) connection. A sample XML file with some messages is included, so you probably want to use the URL method. Enter the file URL where you have your messages stored into the URL field, then click on the right (next) or left (previous) or the X (clear) buttons to display the messages. The optional Session field is for filtering messages by session ID but we never got to use it yet.
Before I start with the code, a few words of introduction: This is my first JavaFX project and I welcome any suggestions on how to better code in JavaFX. It is also my first Java/NetBeans project since a long time, so I'm sure I can still learn a lot more about how to properly do it. But the learning journey into creating this app has been a fun and instructive one, so I hope this code can help others learn more about JavaFX too. If I had to do it again (And I hope I will, next year), I'd do some stuff differently, which I'll discuss at the end of this posting.
Let's walk through the code in roughly the order of how the message flow works:
- The basic idea is this:
- The audience sends their questions, feedback, messages etc. to the CEC backstage team through either Email, Instant Messaging or SMS through special Email or IM accounts or mobile phone numbers. The CEC backstage team reads the messages and stores them in a database where they can be approved, marked for deletion, marked for display on the Message Prompter and assigned a sequence to display in.
- The CEC Message Prompter is the application that the people on stage and occasionally the audience see/s and where the current question to be asked to the people on stage is displayed. So the app has to fetch messages from the database and display them on screen on demand and in a visually intuitive way.
- For testing/development/backup purposes, the Message Prompter can also accept messages out of a single XML file instead of a database.
- The top level directory is supposed to go into the src subdirectory of a NetBeans JavaFX project, but I guess you could as well include this easily into any other IDE or just work from the command-line out of this directory. CECMessage.xsd is an XML schema courtesy of Simon Cook that defines the XML format for a list of messages. ExampleMessages.xml contains a bunch of messages for testing purposes. Most of the source code is in the cecmessaging subdirectory which is the name of the Java package bundle for this app. If you apply the Message schema to the JAXB xjc.sh script, it creates the java classes in the org/netbeans/xml/schema/cecmessage directory which describe the corresponding java objects.
- Some things are best left to real Java, in this case the message fetching and conversion into JavaFX digestable classes. The nice thing about JavaFX is that it can seamlessly use Java classes for doing any heavy-lifting. Messages can come in as an XML file or from a database, in both cases they are fetched from Java helper classes that handle the complexity of fetching messages and who return an object structure to the main JavaFX application.
In the case of messages coming in as a single XML file, the file is parsed by the XMLHelper class in cecmessaging/XMLHelper.java using the JAXB framework. The resulting object structure can then be interpreted by the JavaFX main class. Make sure you include JAXB 2.0 or later if you use J2SE 5, in J2SE 6 it's already included.
If messages are to be retrieved from a database, then the DBHelper class in cecmessaging/DBHelper.java is used. It uses the mySQL JDBC connector for database access but you could easily plug in any other database connector. For simplicity, the database data is converted into a JAXB structure as if it was coming out of an XML document. Here is the definition of the database that Simon created:
Database: cec Table: messageBoth XMLHelper and DBHelper sort the messages by the displayOrder field, then by id. The sort comparator for this particular ordering is in CECMessageComparator.java.
Field Type id int(11) sender varchar(100) message text topic varchar(100) timeSent timestamp session_id int(11) device varchar(10) approved tinyint(1) deleted tinyint(1) to_be_asked tinyint(1) display_sort_order int(11)
- The heart of the Message Prompter lives in cecmessaging/Main.fx.
- It starts with a few data structures:
- The AnimValues class stores font sizes, colors, duration and other parameters used for animation. JavaFX does not let you specify defaults as part of the class definition, hence the attribute commands.
- The Message class is modeled after the corresponding JAXB CECMessage class. It adds a few attributed to track a particular message's color and font size. The font size and color of a message depends on its position (whether it is highlighted or not) and can change while it is animated during transitions. That's why we need to keep track of them. The alive attribute is not used right now, it may become useful if I rework the animation stuff.
- The Tag class is for handling, well, tags. Every word that shows up as a message, author, device or topic is treated as a tag and a tag cloud is generated based on how often the word shows up on the screen. This class stores the tag word, counts the number of appearance and stores the current font size of that tag on screen. Again, we need to track font size for animation.
- The MessageList class is the main model class the application uses. It contains an AnimValue class, a list of Message class messages and list of tags. It knows where the messages come from and where the original message data in JAXB format is. It keeps track of the GUI Labels that graphically represent the messages on screen plus it knows how many messages to display at once, which one is to be highlighted and other useful parameters.
- The following operation statements are really methods. They are written in script-like manner rather than in object-oriented manner. This means that they are not associated to a particular class other than the main one. Next time, I might use a more strict object-oriented approach, but hey, this is a scripting language, isn't it?
- MessageSignature computes a single string out of all fields in a message for comparison purposes. Somehow .equals or .toString didn't work for me as expected, so Implemented this simple mechanism to see if two messages are equal.
- ClearMessages clears all messages, its associated Label objects and makes sure that dying messages are animated and their tags updated. Actually, today the death of a message isn't animated yet but I hope to implement a nice way of dying for messages. I loved my Apple Newton back then in the 90ies and it had this nice animation where deleted stuff would vanish in a puff of smoke :).
- CecmToMessage takes a JAXB message created by the XMLHelper or DBHelper class and creates the corresponding JavaFX Message instance. It also handles basic true/false associations for the approved and deleted fields, which are meant to be boolean but are actually strings in the XML schema.
- MessageToLabel creates a Java Swing Label object that displays the message on screen. The nice thing about the JavaFX Label implementation is that it understands HTML. So we can use HTML freely here to control how the message is to be seen. Notice the bind statement where the Label text is: It ties the Label's text content to the Message's attributes (color, size, content). This means that whenever any of these attributes are changed in the Message object, the corresponding Label object is changed as well! This is a very nice mechanism for Model-View-Controller like programming and a big time saver when coding.
- The messageDisplayable function decides whether a message is supposed to be displayed. This is just a logic expression checking the approved, deleted and toBeAsked fields and filtering by sessionId (In case one wants to restrict messages to a particular session). One could have implemented the filtering at the XMLHelper or at the DBHelper level, but I felt it would be better to have full control over displaying messages from the app itself.
- UpdateMessages checks all currently displayed messages against their counterparts in the XML file or the DB. The idea here is that we want to be able to change a message even if it's already displayed in the application (you know, when accidentally a bad word came through :) ). This is called regularly before adding new messages to the screen.
- compareMessageOrder does just that. Messages come in already sorted, but we still need to decide on ordering when going through them to detect whether a message is missing etc. (The naming is wrong, it should start uppercase. This is because this operation started as a function but then if-then is not accepted in functions by JavaFX...).
- NextMessage adds a message to the display list. It also deals with the unexpected complexity of deciding which message to highlight in certain corner cases. For instance, when we want to preview 2 messages, the third one is to be highlighted, but if you only have 0-2 messages on screen, the highlight should be on the last etc. When done, the message animator is called to animate any newly higlighted or unhighlighted messages and the tags are updated.
- PreviousMessage does the opporsite of NextMessage. Again, the handling of the highlight is a tad more complex than I would have wanted it. Again, we animate here as well.
- RefreshTags goes through all messages displayed on screen and makes sure the tag list is up to date. Then it starts animation for those tags that have changed.
- AnimateMessages checks all messages and whether their font sizes match their position and highlighting status. Then, it animates all messages that have changed their status into their destination sizes and colors. Animation is handled through the dur operator. It assigns a list of values to a single parameter in sequence, during a specified time. So when we want a piece of text to grow from 0 to 20 pixels in size during 500 milliseconds, we say something like text.size = [0..20] dur 500. Very nice! Color animations work by applying a string array with the color values in sequence to a string variable. I wasn't confident on how the animation works in terms of concurrency (for instance, if another thread happens to change a value while it is animated) and I've seen cases where the font sizes weren't correct (and that cost me quite some sweat drops!) so I added some watchdog code to make sure the font size is correct after the end of the animation. Now that I've seen the CEC 2007 JavaFX session (sic!), I know a bit more about how this is supposed to work so hopefully I won't need it any more :).
- AnimateTags does similar things to the tags, a tad easier to do.
- The LoadProperties stuff is not used at the moment, so isn't the properties file included with the source. I was planning to outsource all relevant defaults and constants into an external properties file, but didn't have the time to do it. But here's a start...
- The Main part is fairly straight forward: It first instantiates the MessageList model structure with some default values, then proceeds to instantiate the GUI elements. Another nice thing about JavaFX is the declarative syntax where you just write down the GUI class with all the desired values and the runtime system takes care of instantiating the classes, hooking them together and assigning values to them, as well as tying in the methods to be called when a GUI element is activated. Also, the bind command is your best friend here in that it automatically binds GUI attributes to the model classes and saves you the hassle of implementing callback methods etc. You don't even need a GUI builder, just write down the widget hierachy and you're done. Very convenient.
That was it. All in all, learning JavaFX was a fun experience. And you can do it too, just go to the OpenJFX website and check out the tutorials and references.
What would I do differently if I had to write this app from scratch? Probably one or more of the following:
- Use real object oriented style by attaching methods to classes etc. Possibly different classes in different files, loosely coupled by the main class, as in this nice Mariah Carey website example.
- Rework the animation so it works on triggers. Triggers are a way of coupling code to variables, similar to binding. So, whenever a variable is changed, the trigger code gets executed. For instance, the tags could be updated and animated using triggers.
- Introduce more eye-candy. JavaFX comes with full Java2D support, so I'd dig in deeper into its classes to implement nicer animations.
- Make it more interactive by letting GUI elements slide in and out only when necessary so there's more real estate for the messages.
- Introduce images and symbols to help with the eye-candyness.
Thank you for reading this and I hope you enjoyed this JavaFX example. Let me know your thoughts by using the comment function or by sending me email!
Tuesday Oct 09, 2007
By user13366078 on Oct 09, 2007
Boy is this CEC 2007 conference a busy place! Here's a couple of things that got me excited since my last post:
- Yesterday we had an Unconference session coupled with a couple of speed geeking sessions. Three of the speed geeking sessions were centered around Web 2.0: Neeraj presented on CE 2.0, our new collaborative infrastructure for the field that leverages a lot of Web 2.0 principles. Hal Stern shared some fascinating thoughts about why DRM is Morons and why sharing content is always a good thing, even if it's professional music or movies or other traditional content.
- Today, after the morning sessions and the big launch, a couple of colleagues and I sat down to record the second episode of the CEC 2007 Podcast. This time, Jonathan Schwartz and John Fowler joined in, together with Matthias Pfützner, Robert Holt, Dave Levy and Michael Ramchand. Don't miss this episode where we share our impressions of CEC and discuss some thoughts about the value of Web 2.0 to us.
- This CEC has also probably seen the debut of JavaFX and JavaFX Script on a big stage :). To the top, you see a screenshot done by Rajesh of an application that we use to prompt questions from the audience to the presenters on stage. Questions come in through SMS, Email and Instant Messaging while the presenter on stage gives his talk. They are aggregated and fed into a database by the CEC Backstage Messaging Team. Finally, they are displayed onto a screen through the CEC Message Prompter for the speaker and the audience to see.
The message prompter is written in JavaFX Script. It uses traditional Java classes to access the database through JDBC and it can also digest messages in an XML format through the JAXB API and this is the first significant feature of JavaFX: You can mix traditional Java Classes with JavaFX Script seamlessly, leaving all the heavy-lifting to Java so you can concentrate on the GUI through JavaFX script. Another nice feature of JavaFX Script is the declarative syntax: You just write down how what you want and the JavaFX runtime takes care of instantiating the objects, initializing their parameters and fiddling them into the Swing event loop.
The above photo only shows a screenshot, but the application is animated: Every time a new message is highlighted, old messages are reduced in size and color while the highlighted message grows and becomes a darker color. Also, to the right, there is a dynamic tag cloud that reflects all of the words visible on screen and where the size of the word indicates its multitude. Again, the tags are animated based on the changes in the message part. Programming animations in JavaFX is very easy thanks to two constructs: Variable binding and parameter streaming. Variable binding means binding an object attribute (i.e. the HTML code that describes the rendering of the message) to a variable (the position of the message in the message list). After the binding, the attribute behaves much like a marionette: As soon as something changes in the data model (i.e. a new message is added to the display list), the attribute is updated in real time and the font characteristics are updated to reflect the change (in this case, the next message grows while the older one shrinks). And here comes another mechanism to help, the "dur" statement. A line like "myVariable = [0..100] dur 500" means: Assign the values 0 to 100 to the variable myVariable during the next 500 milliseconds. Perfect for animation control! JavaFX takes care of all the setting up of timer threads etc. under the hood, while the programmer can essentially animate everything in their application. Very nice.
Of course, the CEC Message Prompter is not bugless, and unfortunately, the highlighting went wrong a few times :). Fortunately, this didn't seem to confuse anyone, but today I implemented a watchdog mechanism to make sure stuff always has the right size no matter what. I hope that this works more smoothly tomorrow...
I'd like to encourage everyone to try JavaFX script out. It still feels a lot like beta but it's already quite useable, heck, we're using it in production right now at CEC :). Let me know if you want the source code to the CEC Message Prompter application.
Tune in and find out useful stuff about Sun Solaris, CPU and System Technology, Web 2.0 - and have a little fun, too!
- Blog migration
- Six Weeks of Constant Thinking
- A New Home For My Blog
- OSDevCon 2009 Paper: Implementing a simple ZFS Auto-Scrub Service with SMF, RBAC, IPS and Visual Panels Integration - Lessons learned
- Fun With DTrace: The Windows-Key Prank
- A Small and Energy-Efficient OpenSolaris Home Server
- Gonzalez Goes GeekAndPoke: My First Co-Authored Webcomic
- New OpenSolaris ZFS Home Server: Requirements
- New OpenSolaris ZFS Auto-Scrub Service Helps You Keep Proper Pool Hygiene
- How to Fix OpenSolaris Keyboard Irregularities with Virtual Box
- X4500 + Solaris ZFS + iSCSI = Perfect Video Editing Storage
- OpenSolaris Home Server: ZFS and USB Disks
- How to compile/run MediaTomb on Solaris for PS3 and other streaming clients
- ZFS Snapshot Replication Script
- VirtualBox and ZFS: The Perfect Team
- How to get Audio to work on OpenSolaris on VirtualBox
- 7 Easy Tips for ZFS Starters
- How to install the TwonkyMedia UPnP server on Solaris
- How to burn high resolution DVD-Audio DVDs on Solaris and Linux (And it's legal!)
- ZFS and Mac OS X Time Machine: The Perfect Team
- Alec Muffet
- Aleister Kronos
- Andreas Weinberger
- Chris Gerhard
- Claudia Hildebrandt
- Danilo Poccia
- Das Solarium
- Dave Levy
- Dirk Wriedt
- Franz Haberhauer
- GSE Divas
- Gerald Beuchelt
- Gerhard Hofweber
- Gilles Gravier
- Horst Thieme
- Jim Laurent
- John Meyer
- Jörg Möllenkamp
- Linda Fellingham
- Mark Johnson
- Matthias Pfützner
- Michael Gottwald
- Michael Stevens
- Mike Ramchand
- OneStop Secret Sauce
- Peter Reiser
- Simon Breden
- Tim Foster
- Wolfgang Stief
- Rolf Kersten
- Gene Bob
- Marc Hamilton
- Dan Templeton
- Keith Bierman
- Alan Coopersmith
- Get Simpsonized!
- Solaris@Home Part 4: Streaming Music
- ZFS Interview in the POFACS Podcast (German)
- Cool Apple-Like Photo Animations With POV-Ray, ImageMagick and Solaris
- How to find the right network driver in Solaris
- Solaris@Home Part 2: Being eco-responsible while saving a buck or two
- CEC 2007: JavaFX on stage, podcasting with Jonathan and Web 2.0 at the unconference
- So, where's the future of HD Audio?
- Solaris@Home Part 1: The Basics