Wednesday, July 19, 2006

Flex: Sending Messages from Server-Side Java Code

I've been trying to figure out how to send messages from the depths of my Java application out to Flex clients. I'm not talking about messages that originate on a client, pass through the Java server, and then head back to the clients - that stuff is documented in the manual. I mean there is a process going on in the server that needs to send messages to the clients.

Of course, JMS and the JMS adapter would work, but I've been really leery of the complexity of JMS since it came out years ago. It has the advantage that it would allow my application to be separated from the Flex server, which would work well in terms of scaling. I may yet try JMS, but for the moment, I'm looking to avoid JMS.

So, I was thinking that writing a message service adapter would be the key. Adobe has a brief example of creating a custom Message Service adapter. It seems to be a complete example, but it left me with lots of questions. For example Message is an interface, which concrete class do we use? How do we construct it? What all can we do with the MessageService? What value are we returning from invoke?

After some digging around in the sample code, I came across a couple of Java classes associated with the dashboard sample application. So, I've taken the code from that example, paired it down a bit, and wrote a very simple, Soviet-style (butt-ass ugly but functional) Flex interface for it.

Below is the server-side Java code.

import java.util.*;
import flex.messaging.MessageBroker;
import flex.messaging.messages.AsyncMessage;
import flex.messaging.util.UUIDUtils;

public class FmsDateFeed
{
private static FeedThread thread;
public FmsDateFeed()
{
System.out.println("FmsDateFeed.ctor: " + this);
}

public static void main(String[] args)
{
FmsDateFeed feed = new FmsDateFeed();
feed.toggle();
}

public void toggle()
{
if (thread == null)
{
System.out.println("FmsDateFeed.toggle: starting...");
thread = new FeedThread();
thread.start();
}
else
{
System.out.println("FmsDateFeed.toggle: stopping...");
thread.running = false;
thread = null;
}
}
public static class FeedThread extends Thread
{
public boolean running = true;
public void run()
{
MessageBroker msgBroker = MessageBroker.getMessageBroker(null);
String clientID = UUIDUtils.createUUID(false);
System.out.println("FmsDateFeed.run: msgBroker=" + msgBroker + ", clientID=" + clientID);

Random random = new Random();
while (running)
{
Date now = new Date();

AsyncMessage msg = new AsyncMessage();
msg.setDestination("time_msgs");
msg.setClientId(clientID);
msg.setMessageId(UUIDUtils.createUUID(false));
msg.setTimestamp(System.currentTimeMillis());
HashMap body = new HashMap();
body.put("userId", "daemon");
body.put("msg", now.toString());
msg.setBody(body);
System.out.println("sending message: " + body);
msgBroker.routeMessageToService(msg, null);
try
{
long wait = random.nextInt(8000) + 3000;
System.out.println("sleeping: " + wait);
Thread.sleep(wait);
}
catch (InterruptedException e) { }
}
}
}
}


The toggle method is called from the client to turn the message stream on or off. When messages are turned on, a thread is created and the run method is called. It just loops creating Java Date objects and putting the String representation of the date into Flex messages - instances of AsynchMessage. The key for me was the routeMessageToService method on the MessageBroker class. When you have the class compiled, install the Java class file in samples/WEB_INF/classes.

The client is pretty simple, by comparison. It has two features: a button for turning the message stream on and off (via a RemoteObject call) and an event handler to process the incoming messages.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
backgroundColor="#FFFFFF"
creationComplete="initComp()">
<mx:Script>
public function initComp():void {
consumer.subscribe();
}
public function toggle():void {
feeder.toggle();
}
import mx.rpc.events.ResultEvent;
import mx.messaging.events.MessageEvent;
public function messageHandler(event:MessageEvent):void {
var body:Object = event.message.body;
output.text += body.userId + ": " + body.msg + "\n";
}
</mx:Script>

<mx:RemoteObject id="feeder" destination="time_msgs">
<mx:method name="toggle"/>
</mx:RemoteObject>
<mx:Consumer id="consumer" destination="time_msgs" message="messageHandler(event)"/>

<mx:Form>
<mx:FormItem>
<mx:Button label="Toggle Date Feed" click="toggle()"/>
</mx:FormItem>
<mx:FormItem label="Output Message">
<mx:TextArea id="output" width="250" height="150" />
</mx:FormItem>
</mx:Form>
</mx:Application>

The interesting features are the configuration information for the RemoteObject and the message Consumer. These correspond to destinations configured on the server (see below). When the toggle button is pressed, we just call the toggle ActionScript method, which make a call on our RemoteObject, feeder; we don't care about a return value, which simplifies things.

We configure the Consumer to call our messageHandler method when new messages arrive. It just gets the message body and appends it to our text box. Install this file in somewhere in the samples directory - I created my own subdirectory for the dumb, little programs like this.

To configure the server, edit messaging-config.xml in samples/WEB-INF/flex and clone the destination for "dashboard_chat" and call the new version "time_msgs". You could change the Java and MXML to use the existing dashboard_chat destination (which is what I did initially), but a) you could end up with a conflict with the Flex demos, and b) writing to messaging-config.xml (or any of those other config files) is a convenient way to get the server to restart and see your new Java class.

Oh yeah, you also need to edit remoting-config.xml to contain a RemoteObject destination also called "time_msgs". (You could use separate names for the two destinations, and it would probably be less confusing, but I was too unimaginative.)

If all goes well, you should get messages with the current time showing up randomly in the Flash client.


Enjoy,
Charles.

Wednesday, July 12, 2006

Flex: The Second Simplest Example of RemoteObject

(Update: I wrote this some time ago, and I don't have access to the source code any more. Therefore, I can't send it to you. And, I don't know about newer versions of Flex.)

In a previous post, we developed what I claim to be the world's simplest example of using the RemoteObject facility in Flex 2 to invoke a method on a Java object using the RPC services. In that example, we just had a button on a Flash application that called a method on the server. All the method did was print a message. Although we passed a message into the server method, that message was hard-coded in the ActionScript, and no results were passed back.

In this post, we will build on that to pass some user data into the server and get a response back. In theory, this ought to be very simple: the user data just comes from an input box, and the result will just be displayed in a text box.

Java Code


Below is a simple function that you can add to the SimpleRemoteObject from the previous example:

public String echoMessage(String msg)
{
System.out.println("SimpleRemoteObject.echo: " + msg);
return "The RemoteObject says: " + msg;
}

As you can see, the method writes the message to the console (like before), and returns the message with a little text prepended to it. Pretty simple.

Flash Client


The user interface is pretty simple, and I just added it to the form in the previous example. We have a TextInput to enter data, a Button to invoke the method, and TextArea to display the result from the server.

<mx:FormItem label="Input Message">
<mx:TextInput id="input"/>
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Echo Message" click="echoMessage()"/>
</mx:FormItem>
<mx:FormItem label="Output Message">
<mx:TextArea id="output"/>
</mx:FormItem>

So far, so good. The button calls an ActionScript method (echoMessage) to call the server - just like the previous example:

public function echoMessage():void {
remoteObj.echoMessage(input.text);
}


We just get the text from the TextInput object (input) and call the method on the server. (Note that although I named the ActionScript method the same as the Java method - echoMessage - this isn't necessary; I'm just not creative enough to think of anything else.) But wait, something is missing: what about the message coming back from the server? Based on my experience with Java (and dozens of other languages), I would expect to see code that looks like:

output.text = remoteObj.echoMessage(input.text);

But, that's not how it works in Flash. Flash makes RPC calls asynchronously. This means that Flash doesn't wait for the result to come back from the server, and that's actually "a good thing." Without asynchronous server calls, the Flash client would hang during a (potentially long) server call, and that would be a bad thing.

OK, so how do we get the result back from the server? This introduces some additional complexity, but it's still tractable. It begins in the MXML to describe the server method:

<mx:method name="echoMessage" result="displayResponse(event)"/>

This says that when the echoMessage method on the Java server object is called, and the result arrives at some later time, the displayResponse ActionScript method will be called.

This method is pretty simple, too:

import mx.rpc.events.ResultEvent;
public function displayResponse(event:ResultEvent):void {
output.data = event.result;
}

That's all there is. Note that we don't even have to update the remoting-config.xml file on the server. This is because we are not creating a new destination; we're just using the same Java class - SimpleRemoteObject.

When you run the application in the browser, enter a message in the input area, click the button, and the response will eventually appear in the output area.

If you need the complete code, let me know - post a comment. At the moment, I'm too busy to put together a zip file and post it somewhere.

enjoy,
Charles.

Flex: The World's Simplest Example of RemoteObject

Here is a very, very simple example of using the RemoteObject RPC services in Flex 2 to invoke methods on a Java object. I came up with this because I was struggling to figure out all the stuff needed to do this. In theory, it's pretty simple, but there are a number of components to deal with, and they all get put in different places on the server, and a bunch of those places look very similar to other places on the server.

Introduction


It was especially difficult for me because I'm new to Flex, and I'm coming at it from a server-side background in Java. I don't care about making pretty user interfaces (that's someone else's job) - I'm the guy who has to make the server code work. However, most of the documentation assumes that you're coming at it from the other end - the client side.

I couldn't find any simple examples of using RPC in Flex 2.0. I foolishly started with Flex 1.5 example and wasted a bunch of time updating the syntax in it in order to get it to compile.

After I got that to compile (from the command line - I'm old school), it wouldn't run. After I got the Flash debugger working, I could see that the messaging destination didn't exist, but I couldn't figure out why. So far as I could tell, I had configured it correctly, but there was no indication that things were making it to the server. Finally, I gave up my command line and put the MXML file on the server, and just let the web-tier compiler do it for me.

And, it just worked! The documentation makes reference to using the SDK compiler versus the web-tier compiler, but it just didn't click with me. But, it was still more complicated than I needed. So, I created the following, to prove that I could do it, and that I knew all the voodoo needed.

I will assume that you have the Flex Data Services demo server with Jrun installed an running. I will specify all paths from the root of the jrun server.

Java Code


Let's start with the simplest function in a Java file - basically, "hello world."


public class SimpleRemoteObject
{
public void writeToConsole(String msg)
{
System.out.println("SimpleRemoteObject.write: " + msg);
}
}


This will just write a message to the server console window. (We don't even have to use any fancy Java logging.)

Compile that file and put the SimpleRemoteObject.class file in the directory jrun4/servers/default/samples/WEB-INF/classes on the server. (Note that since the class is in the unnamed, default package, we put the class file in classes, not a subdirectory.)

Flash Client


Now, we need a simple Flash app. Here's a simple MXML file:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
backgroundColor="#FFFFFF"
initialize="initApp()">
<mx:Script>
public function initApp():void {
}

public function writeToConsole():void {
remoteObj.writeToConsole("hello from Flash client");
}
</mx:Script>

<mx:RemoteObject id="remoteObj" destination="sro">
<mx:method name="writeToConsole"/>
</mx:RemoteObject>

<mx:Form>
<mx:FormItem>
<mx:Button label="Write To Server Console" click="writeToConsole()"/>
</mx:FormItem>
</mx:Form>
</mx:Application>

Create a directory called sro under jrun4/servers/default/samples and put the MXML file there.

Now, we can compile the file by loading it in the browser with a URL like:
http://192.168.123.130:8700/samples/sro/SimpleRemoteObject.mxml
(The key is that the file is under /samples/sro based on where we copied the file.)

Don't click the button yet! We still have to configure the FMS destination. Just make sure it compiles correctly.

Configure the Server


Add the following XML code to jrun4/servers/default/samples/WEB-INF/flex/remoting-config.xml. Just put it within the service tag for the remoting-service - e.g., near one of the other FMS destinations.

<destination id="sro">
<properties>
<source>SimpleRemoteObject</source>
</properties>
</destination>

And now, if you press the "Write to Server Console" button, the message, "hello from Flash client" will appear in the Jrun server window.

That's it. It really is as easy as Adobe says it is...
once you figure it all out.

enjoy,
Charles.

Friday, July 07, 2006

Tip: Managing Applications on OS X

I've heard that some people complain that Mac OS X doesn't have anything comparable to the "Start" button on Windows. I've never felt that way, but I have to admit, I'm amazed that some people always use the Finder to navigate to the Applications folder to launch application. If that's your way of starting applications, then I can see why you'd think you need a start button.

Below are some really simple tips to manage the applications and get the "Start button" functionality using the existing Dock.


  1. Put Applications in the Dock: Begin by dragging the Applications folder to the dock. Open a Finder window, grab the Applications folder, drag it down to the dock, and let go of it to the left of the vertical bar that separates the applications from the Trash Can. Now, you have a "Start button." To use it, bring the mouse down to the dock, position it over the Applications folder, click and hold (for a second or two), and the list of applications will shoot up from the folder. Move the mouse over the application you want to launch and (finally) let go of the mouse button.
    I've used this from the very beginning of when I started using OS X. It's so simple, but it makes the system so much easier to use. It drives me up the wall to work on a stock OS X system where you have to use the Finder to go to Applications to launch an application.

  2. Create local applications folder: Once you've installed more than a few applications on your Mac, the "start button" we created above will be too big - you'll have to scroll to find the application you're looking for. What you need is to organize the applications in the Applications folder by creating some folders within the Applications folder. Therefore, create a folder for your new applications. I called my folder "Local Applications." Anytime you install new applications, put them in there instead of Application.
    If you have a bunch of existing applications, it seems to be OK to just move them into your folder. However, take it from me: do not move any of the Apple applications - when Software Update runs, it will install things in the original location. Now, those applications are available from the "start button" under the name for your folder - e.g., "Local Applications."

  3. Create a short list of favorite applications: Even with the benefits of the organization we set up above, the list is still long, and it contains a bunch of applications that you hardly, if ever, use. Create a new folder pretty much anywhere - e.g., in your home directory. Open a separate window in the Finder and navigate over to Applications. For any applications that you use regularly (either in Applications or Local Applications) create an alias of the application (right click/control click on the application icon and choose "Make alias") and drag the new alias into your new application folder. I also include aliases to Applications, Utilities, and Local Applications in my folder.
    Finally, drag your new folder into the Dock (and optionally remove the original Applications folder you put there). From this new "start button," you can quickly reach the applications you use regularly, and if you included Applications in your folder, you can reach everything else, too - the best of both worlds: quick access and complete access.

  4. Change the icon: Optionally you can change the icon of your applications folder - I copied the icon from the original Applications folder into my personal applications folder, but you can use almost icon.
enjoy,
Charles.