Chapter 10. Spring and Flex

It's an interesting time for web developers. The decade of the 2000s saw the rise of the rich Internet application. The idea of an Internet application as we know it is essentially a series of tradeoffs made by developers for the Internet, the ultimate platform. In the early '90s when client/server architectures ruled the day, every application communicated with its server differently, and most clients were operating system specific. Java came around and promised to unshackle the client from the operating system, but the fact remains that the fundamental interface between client and server was variable.

Along came the Internet and then the Web, and with it HTTP. The Internet wasn't designed to be an application platform; in fact, the earliest versions of what we know as the Internet didn't prescribe any of the things a Visual Basic or Delphi user would've expected, for example. If the Internet was going to work as a platform, it would need to be made to work.

The Internet is the largest platform on earth. It's application neutral—you can do whatever you want that needs connectivity. The Web, built on top of the Internet, was originally conceived as just a mesh of hyperlinks, forming a huge reference library: don't knock that; it's immensely useful. To make applications run over the Web, you need to do a lot of work and—more than likely—dilute some of the original notions extended about the Web. Developers have made do with it. We plugged the gaps where required; we took an inherently stateless platform and created "sessions;" we took SGML and added markup for describing presentation to get HTML; we took incompatible browser clients and abstracted away the considerable differences behind libraries, which we deploy with religious zealotry. We evolved the ideas behind the venerable MVC pattern to yield model 2 MVC (the MVC variant as espoused by Struts and a lot of early Java MVC frameworks) and the model-view-presenter (MVP) pattern (this variant seems more common in the .NET world). In short, we've done a lot to bend the Web to our will, and it still isn't easy to figure out all the constituent parts of a working web application.

A skilled developer can work with all the technologies built on the Internet and assemble an impressive application—one that feels coherent and quick and that looks good—leveraging HTML, CSS, JavaScript, DOM, XML, JSON, REST, and—of course—a server-side programming language. A really skilled developer will endeavor to also build an application that works on all browsers and that works consistently. A really, really skilled developer (indeed, a whole team) might even endeavor to completely hide all the fragility of the underlying platform altogether. This developer will build a compiler that emits JavaScript given input Java code and perhaps work to change the HTML specification to better support requirements.

For the rest of us wanting to simply ship a working application, we can let somebody else do the work. We can't remove all problems, but we can remove the ones that are not sufficiently abstracted already. We can't obfuscate the need for HTTP—that's the part that makes the Web a worthy platform. We need that part. If you've chosen a server-side programming language, then you've also already got a working abstraction for sessions, for example, so there's no need to "fix" that. It's a problem that's already been solved for you. Most of what we need is there already. The only irksome bit is how we will actually build the user-facing application. We have web frameworks that handle a lot of the minutiae on the server, and we've built component libraries that take us a long way in abstracting away the differences between browsers, but the fact remains that these approaches are common-ground solutions, designed to work across many clients by not exceeding the capabilities of any one client.

The modern day web programmer (as of 2010) contends with a long list of things he or she can't (readily) do that a client/server programmer of the 1990s (20 or more years ago!) took for granted: file system access, multimedia, cutting edge graphics, persistent local storage, skinning, and so on. Worse, while applications are increasingly quicker in today's browsers, few provide compiled-code execution speed. The idioms we use today for form validation, server communication, and rendering change with every web framework.

When Macromedia, now owned by Adobe, initially released Flash, it was to bring the animation finesse of Macromedia Shockwave to the Internet. As time progressed, the animation facilities grew to include a sophisticated programming environment. The only thing missing was support for data-intensive applications, the niche formerly occupied by Visual Basic. It was no surprise then that, in 2004, when Macromedia announced Flex, an environment with a generous complement of data-bound controls and full support for RPC, that developers in the Java camp were curious. There were early adopters, even if the interest was muted. The problem—the platform was closed source and expensive. The tooling, SDK, and integration middleware were all costly, and Flex was an unproven architecture few were willing to risk and prove at cost. The barrier to entry was too high.

Adobe, which bought Macromedia in 2005, eventually started opening up the bits. The Flex platform's SDK itself was made available so that you could compile from the command line. Flash offers a binary protocol called Action Message Format (AMF) by which Flash VMs talk to servers: it's quick and compact, especially when compared to JSON and SOAP. While many open source projects had reverse-engineered the protocol and exposed similar, alternative integrations for their favorite platform (PHP, Python, and in the Java world, Granite DS), the ideal solution was a proper implementation like that exposed by Adobe's expensive Lifecycle Data Services (LDS) middle project. Key bits of LDS were released under an open source license (various bits are licensed differently: BlazeDS proper is LGPL) in 2007 and renamed BlazeDS.

The tooling—an Eclipse derivative called Adobe Flex Builder—is still a pay-for-use product, but this doesn't impede others from creating their own tooling. Particularly, IntelliJ's IDEA product, versions 8 and 9, support Flex and AIR development with aplomb. Additionally, it is entirely possible to build entire Flex and AIR applications using only the command line, or with third-party tools, like the Maven 2 FlexMojos project, which supports building Flex applications using Maven.

As the platform's become more open, so too has the groundswell of open source projects supporting the Flex environment gown. If you want to leverage BlazeDS to consume Spring services, there is specialized support built in cooperation between SpringSource and Adobe available called the Spring BlazeDS Integration. The integration makes working in Spring and exposing services using a Spring a piece of cake. On the client side, in ActionScript, the Spring ActionScript project provides a lot of the familiar niceties provided by the Spring platform for Java developers. We'll use this project to explore dependency injection (DI) in ActionScript, wiring up remote services, and wiring in services to your Flex components.

Getting started with Flex

Problem

The Flex platform is huge, and it helps to have a sense of the landscape. How does one know what tool does what and why?

Solution

We will explore the tools and technologies that make up the Flex platform. First, we'll look at the SDK and the tooling offerings. Then, we'll explore the middleware technologies and the state of the platform itself today.

How It Works

The first thing to realize when working with Flex is that it is, technically, a library implemented on top of the Flash virtual machine. If you've ever been victim to an annoying animated ad of late, then you've probably seen Flash. Flash maintains a frame-based timeline. Every second that ticks by causes the Flash runtime to paint the screen at a fixed interval. If you are using regular Flash code, then you will need to concern yourself with the frames of the animation, since these play into values like the duration of an animation or time. In Flash, the displayed area where animation and rendering occurs is called the stage. Components are added to the stage and given a life cycle, very much like objects in the DOM of your browser.

Flash uses a language called ActionScript 3.0. ActionScript is a variant of JavaScript that has drawn inspiration from the various specifications for JavaScript. You'll likely encounter two variants of ActionScript code in the wild: ActionScript 2, which shipped with the Flash 8 VM, and ActionScript 3, which is what's been available since Flash 9. The two versions are very different beasts: ActionScript 2 is more similar to the JavaScript variant in your web browser, and ActionScript 3 is more similar to JScript.NET or C#/Java. Unlike the JavaScript variant most developers are probably familiar with in the browser, ActionScript is compiled. ActionScript code pages end in .as, and ActionScript binaries are .swf files. You can use ActionScript tools to build linkable libraries, much like a .dll on Windows, or a .so on Linux. These linkable libraries have the extension .swc. This can be a strange thing to grasp for a Java developer with no history in C/C++ or even .NET; in Java all "binaries" are .jars. The ability for .jar "a" to use another .jar, "b," is not dictated at compile time. We will see later when we use Spring ActionScript that the library is shipped as a .swc file.

Flex is implemented on top of the Flash VM as a library. You can inspect the source code, and you'll see that most of it is written using ActionScript. It enjoys a special status among libraries, however, in that most people will get their instance of the Flex library directly from their player.

Flash, and Flex, run inside of a web page in your browser, in the same way as Java applets run. Flex applications can't install themselves in the OS's application menus or manipulate the file system (except when doing things like uploading files, in which case there's no control surfaced to the programmer; it's an isolated use case). Similarly, the life cycle of a Flex application is constrained by that of the host browser: when the browser stops, so does Flex. To address these issues, Adobe released AIR. AIR is a superset of Flex that has APIs for manipulating the file system, installation, automatic updates, and more. The AIR runtime, like the Flex runtime, will typically come from Adobe. AIR deployments are more akin to Java Web Start deployments than applets. It is very easy to write a Flex application and change the outermost container component to run it in AIR. Thus, Flex applications are AIR applications, though AIR applications aren't Flex applications, per se.

Flex Development Basics

Whereas Flash provides an animation-centric environment with support for creating timelines and importing graphics and the like (with the expectation that script that was added would be done so as event handling logic in ActionScript), Flex is a code-centric platform. The two source artifacts of a Flex application are the ActionScript files (ending in .as) and the .mxml files.

ActionScript files can contain classes, public functions and variables, and annotations. Indeed, ActionScript files may house any number of public classes. The look and feel of the language will feel familiar to anyone who's used Java, C#, or Scala.

MXML files are an XML variant that describe the UI components and provide a DOM. Scripts may be inline, which we will do for this chapter's trivial examples in the name of expediency. Each tag in MXML describes a component or object that is registered with its container. In the case of Flex applications, the outermost tag is the <mx:Application/> tag; in AIR, it's <mx:WindowedApplication/>. These tags describe containers that themselves handle all the minutiae of creating components on the Flash Stage object.

You can use MXML to instantiate regular ActionScript objects, like you might in Spring, but you'll find that some uses will more naturally lend themselves to code and some to MXML. You could create an entire Flex application using only ActionScript with no MXML. When the MXML files are compiled, they are converted to ActionScript expressions as an intermediary format, and that is what is ultimately compiled. MXML files support a limited form of expression language binding (limited when compared to the EL support in frameworks like Tapestry or JSF, anyway). Flex components are wired together using the expression binding support, as well as with powerful event mechanism.

Let's look at a simple MXML file:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
applicationComplete="applicationCompleteCallback(event)">

    <mx:Script>
        <![CDATA[

             import mx.events.FlexEvent;
             import mx.controls.Alert;

             public function applicationCompleteCallback(fe:FlexEvent):void
             {
                 Alert.show('Hello World!'),
             }

        ]]>
    </mx:Script>

</mx:Application>

This MXML describes the simplest possible Flex application. As soon as the application has been loaded and all the objects configured, an event will be fired—the applicationComplete event. Just as you can in the browser, you have two choices for listening to events in Flex: programmatic registration and through MXML attributes on the component that fires the event. Here, we've installed our listener using the applicationComplete attribute on the mx:Application tag. It's very convenient to use MXML event subscription in most cases, though sometimes, you want to programmatically register listeners.

Let's try another example with a button that displays an alert when clicked. We'll register the event through ActionScript, instead.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="applicationCompleteHandler(event)">

    <mx:Script>
        <![CDATA[

        import mx.controls.Alert;
        import mx.events.FlexEvent;

        private function applicationCompleteHandler(evt:FlexEvent):void
        {
            button.addEventListener(MouseEvent.CLICK, onClick);
        }

        private function onClick(me:MouseEvent):void
        {
            Alert.show('Hello, world!'),
        }

        ]]>
    </mx:Script>
    <mx:Button id="button" label="Say Hello"/>
</mx:Application>

I won't belabor the point but for one more example; this is a JavaScript variant, after all! The previous example could have been rewritten to use an anonymous function:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="applicationCompleteHandler(event)">

    <mx:Script>
        <![CDATA[

        import mx.controls.Alert;
        import mx.events.FlexEvent;
private function applicationCompleteHandler(evt:FlexEvent):void
        {
            button.addEventListener(MouseEvent.CLICK,
            function (me:MouseEvent):void  {
              Alert.show('Hello, world!'),
            });
        }

        ]]>
    </mx:Script>
    <mx:Button id="button" label="Say Hello"/>
</mx:Application>

In the preceding examples, we did nothing special to access the object created by the <mx:Button /> tag, except to refer to it by its id attribute, button. We didn't need to use something like document.getElementById(String), as you might expect from the browser.

Now, let's look at a simple ActionScript class. We'll flesh out this class more later, but for now, it's helpful to see what it looks like to define a Plain Old ActionScript Object (yes, Virginia, that is what it's called!):

package com.apress.springwebrecipes.auction.model
{
public class Item
{
    private var _sellerEmail:String;
    private var _basePrice:Number;
    private var _sold:Date;
    private var _description:String;
    private var _id:Number;

    public function get id():Number {
        return _id;
    }

    public function set id(value:Number):void {
        _id = value;
    }

    // ...

}
}

This should seem pretty intuitive. Most striking is the C++/C#–style package syntax; the package declaration wraps the classes. Here, we only have one, but we could have as easily defined 20. We won't get into the gory details of visibility rules, but suffice it to say that public and private work pretty much the way you'd expect them to. The next element to note is that ActionScript has a proper property syntax. We declared a private class variable _id, of type Number. Then, we declared two functions that look a little different than the other functions you've seen thus far. These functions have the keyword get and set and share the same function name. This is the language support for defining accessors and mutators, just like with Java's JavaBeans. Unlike JavaBeans, however, there is syntactic support to let the users manipulate properties like they would public instance variables. To use the preceding class, we could write something like this:

package com.apress.springwebrecipes
{
    import com.apress.springwebrecipes.auction.model.Item;

    public class ItemDemo
    {
        public function ItemDemo()
        {
            var item:Item = new Item();
            item.id = 232;
        }
    }
}

The Tools

The most common tool for building Flex applications, by far, is Adobe's Flex Builder tooling. It is what we'll use in this chapter. Flex Builder is an Eclipse-based IDE. The IDE provides compilation, debugging, and wizard support for developing ActionScript- and Flex-based applications. It also has a visual designer, but experience has shown that it serves little purpose for moderate-sized applications. Flex Builder features support for working with other tools in the Adobe Creative Suite, including Flash Professional (which, as indicated previously, is useful for timeline based applications and components), Photoshop, and Illustrator.

Arguably, Flex's most powerful feature is its integration with other Adobe tools, in support of Adobe's vision of the designer/developer workflow.

Flex supports the concept of states. With a state, you can assign arbitrary names to given sets of components and configuration and, by setting the currentState property, have the engine activate that state. A state can prescribe that components be added or removed a display. It can also outright replace the current display. There is a lot of potential here. These states are used to describe a widget's state on screen, too. The Adobe Creative Suite facilitates exporting Photoshop or Illustrator assets, for example, and mapping the layers of those assets to states in a Flex component. You might imagine a button whose mouse-over, mouse-down, mouse-up, hover, focus, and other states each are each assigned a layer in a Photoshop file—a layer for the focus state might render the button with a glow; the layer for mouse down might darken the colors, and so on.

There are many other such niceties, but ultimately, they are all available even without Flex Builder, using the stand-alone compiler, if you know where to look.

Indeed, the single simplest and most cost-effective way to get started with Flex is to use the SDK. You can download the SDK from Adobe's site for free. At the time of this writing, the most current version of the SDK is 3.5. You may download it at http://www.adobe.com/cfusion/entitlement/index.cfm?e=flex3sdk.

To use the SDK, download it, and extract the archive. Put the bin folder inside the SDK on your system's PATH variable, just as you might with Ant, Maven, and the Java SDK. The Flex SDK offers two compilers: one for components, and one for Flex applications. If you want to compile components, you will use compc. If you want to compile an application, you will use mxmlc. An example invocation of the mxmlc says it all:

mxmlc main.mxml

The Flex SDK compilers are written in Java, so you could also invoke them using the java command. What follows is an invocation of the compiler using Java. This does the same thing as the preceding command. The following invocation is assumed to be in the bin directory of the SDK. There should be no breaks in your command, though; it should all be on one line.

java -jar ../lib/mxmlc.jar +flexlib ../frameworks test1.mxml

Another approach for developing application is to use Maven. The FlexMojos project (http://flexmojos.sonatype.org/) is a set of plug-ins supporting building Flex projects and handing dependency resolution, just as Maven does for Java projects.

Because the runtime and compiler are available, other IDEs have built support for Flex. One very good integration is IntelliJ's IDEA 9.0. It supports coding, compiling, refactoring, debugging, and all the basics you'd expect of a good IDE. While it doesn't support the visual design mechanism or many of the wizards that Flex Builder has, it does excel in some areas where Flex Builder falls short: IDEA supports formatting code, for example, import of Maven FlexMojos projects, and refactoring (renaming) of MXML fragments. Flex Builder is a set of plug-ins built on Eclipse. You may run Flex Builder stand-alone or integrated with an existing Eclipse installation. By itself, Flex Builder lacks an XML editor, support for Subversion, and any kind of support for Java. Additionally, Flex Builder is usually coupled with an older revision of Eclipse.

Clearly, there is no shortage of ways to approach the problem. If you can swing it, the Flex Builder tooling will be familiar to Adobe users. IntelliJ users have no need to switch. Failing those options, the SDK and various build systems welcome any would-be Flex developers.

Leaving the Sandbox

Problem

You know you want to integrate Flex and Java, but there are many options. What technologies work best, and when? Which—if any—is preferable?

Solution

The Flex platform is nothing if not flexible! The answer, of course, is "it depends." You have several options to talk to a remote service from Flex including HTTP requests, AMF communication, JSON serialization, and even specialized support for consuming services exposed with REST, XML, and SOAP.

Flex also talks plays nicely with the web page hosting the Flex application using JavaScript. This communication is bidirectional: Flex may manipulate the containing page, and the containing page may manipulate the Flex application.

Your main limitation in all of these options is the Flash sandbox which—much like the applet sandbox—was put in place to assure that Flex applications are well-behaved.

With Flex, breaking outside of the sandbox means ensuring permission with the crossdomain.xml file, proxying access to services on hosts not otherwise available using something like BlazeDS, and ensuring proper host-of-origin etiquette in all other cases.

How It Works

We will spend most of the balance of this chapter exploring the Spring BlazeDS integration. However, there are other options, and it pays to be aware of them. Perhaps they offer an easier path.

FlashVars

Let's start with the simplest option: talking to the host page. Often, a Flex application will need to be configured at launch. You can launch a Flash application using FlashVars as parameters, just as you can launch a Java application with arguments.

Before diving into that, let's talk about Flex deployment. To deploy a Flex application, you need to embed markup in the browser to enable the browser to display the Flash content, since it is not part of browser's native capabilities. There are many ways to generate this configuration. The configuration for the embed and object tags can be quite convoluted because no single configuration works reliably over the many target browsers. Flex Builder will generate a default version, but this markup is fragile and can quickly become unmaintainable. To save space, we won't bother reprinting it. Instead, we recommend using the SWF Object JavaScript library (http://code.google.com/p/swfobject/wiki/documentation). Simply download the JavaScript library and put it in your web application. The library dynamically generates the relevant object and plugin tags, along with the FlashVars parameters. Because the JavaScript library dynamically adds the plug-in content to the page, it triggered the "Dynamic Content" warning in Internet Explorer 6 for a few years. This is, in practice, less and less of an issue now that newer versions of Internet Explorer 6 (and indeed, all subsequent versions, too) no longer have this issue.

Here is a minimalistic use:

<html>
    <head>
        <title> Hello World </title>
        <meta http-equiv="Content-Type"
              content="text/html; charset=iso-8859-1" />
        <style>
            body,html { padding:0; margin:0 }
        </style>
        <script type="text/javascript" src="swfobject/swfobject.js"> </script>
        <script type="text/javascript">
            var flashVars = {
              parameter1: 'parameter1',
              parameter2: 'parameter2'
            };
            swfobject.embedSWF(
              "helloworld.swf",
              "helloworld",
              "500",
              "500",
              "9.0.0",
              "swfobject/expressInstall.swf",
              flashVars );
        </script>
    </head>
<body>
        <div id="helloworld"></div>
    </body>
</html>

The first bold line shows the inclusion of the script. Further down, you see the invocation of the swfobject.embedSWF method. The first parameter is the name of the SWF asset (relative to the current page, of course). The second parameter is the id of the HTML element on the page that should be replaced when the SWFObject tries to paint the Flex application. Here, we want the Flash application to render where the div element with the ID of "helloworld" is, which is shown further in the following example in bold. The plug-in and object instances will be created with an ID of helloworld.

Finally, coming around to the original point, toward the top, we define a variable (flashVars) that is an associative array of keys and values. These keys and values are passed as parameters to the Flex application (note that it is passed as the last parameter to the swfobject.embedSWF method). The Flex application may get at these variables from inside using the Application instance:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="setup(event)">

    <mx:Script>
        <![CDATA[

        import mx.controls.Alert;
        import mx.events.FlexEvent;

        private function setup(evt:FlexEvent):void
        {
           Alert.show (
' param1='+Application.application.parameters['parameter1'] +
' param2='+Application.application.parameters['parameter2'] );

        }
        ]]>
    </mx:Script>
</mx:Application>

You can use these variables to configure the state of your Flash (and Flex) applications. This works well for configuration options—which may be all you need to communicate from the host application. It is, tragically, only a one-way solution. If you want a bi-directional channel from the containing application (in our case, a Spring web application) to the Flex application, you need to pursue other options.

ExternalInterface

Because Flex lives inside the Flash VM, it's easy to think of it as being confined, when in fact, it is freely able to talk to the host environment. To talk to the containing HTML page, you can use the flash.external.ExternalInterface class. This class defines two static methods, call() (which lets the Flex application talk to the host) and addCallback() (which lets the host talk to Flex). The ExternalInterface class provides a communication line going both directions: Flex-to-host and host-to-Flex. This solution is appropriate when you don't have a lot you need to inside the Flex environment—perhaps you need to notify the host page of some events when drawing some graph, or perhaps your Flex environment only needs to make one Ajax call. Similarly, perhaps you're embedding Flex to take advantage of its multimedia support or its rendering capabilities, but you want to drive that rendering through events in the host container. This indirection is slower (naturally) than doing something directly from the host page or directly from the Flex application, but it can be quite appropriate in many situations.

If we want to talk to the host environment, we can use the call() method on ExternalInterface. It is a blocking call. Assume we have a function defined in the host HTML page called updateStatus:

<html>
  <head><title>Addition</title></head>
 <body>
  <div id ='status'></div>
  <script language='JavaScript'>
   function updateStatus (msg){
      document.getElementById('status').innerHTML = msg;
   }
  </script>

  <!--
    ...
    Flex/flash
    ...
  -->
 </body>
</html>

We could, of course, call methods that are already in the host environment, like window.alert(String). To call the updateStatus function from within Flex, you might do something like the following:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="setup(event)">

    <mx:Script>
        <![CDATA[
        import mx.events.FlexEvent;
private function setup(evt:FlexEvent):void
        {
             ExternalInterface.call(
               'updateStatus',
               'Hello, from the future! '+
               'The Flex application has '+
               'finished loading.'),
        }
        ]]>
    </mx:Script>
</mx:Application>

Now, to go the other way—from the host environment to the Flex application—you have to register the functions that you want the host environment to see using the ExternalInterface's addCallback method. Perhaps we surface a method in our Flex application to play a track of music given the String path to an .mp3 file. Let's first build the Flex code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="setup(event)">

    <mx:Script>
        <![CDATA[
        import mx.events.FlexEvent;

        public function enqueueMP3Track( trackName:String) : void
        {
            //...
        }

        private function setup(evt:FlexEvent):void
        {
             ExternalInterface.addCallback('playTrack', enqueueMP3Track );
        }
        ]]>
    </mx:Script>
</mx:Application>

The first parameter to ExternalInterface.addCallback is the alias used to reference the function from the host page. On the host, calling the function should be pretty easy, but isn't, unless you're aware of the nuances between browsers. The following is a fairly fail-safe way of invoking the playTrack function from the host page:

<html>

  <head>
  <title>Our Jukebox</title>
  <script language='JavaScript'>
   function getFlexApplicationReference( flexAppId ){
      return navigator.appName.indexOf('Microsoft')!= −1 ?
        window[flexAppId] : document[flexAppId];
   }

   function startTheMusic(evt){
        getFlexApplicationReference('jukebox').playTrack(
                        'La_Camisa_Negra.mp3'),
   }
  </script>

  </head>

  <body onload='startTheMusic(event)'>

  <!--
    ...
    Flex/flash setup using the swfobject
      as before, with an ID of 'jukebox'
    ...
  -->
 </body>
</html>

This option works well when you need to expose functionality in one direction or another. You might have an application that's predominantly built using conventional web frameworks (Struts, Spring MVC, JSF, etc.) and Ajax (perhaps you've exposed Spring services using DWR). If you have an island of functionality that's better suited to Flex, you can use this integration to provide the few touch points your application needs to talk to the containing application.

HTTP and HTTPS

Talking to HTTP or HTTPS resources is one of the simplest mechanisms for talking to other systems, yet it remains one of the most powerful. With the rapid adoption of REST-based services, HTTP is even more powerful an option for many architectures. Additionally, HTTP support opens the door to the numerous services exposing JSON or XML data. Fortunately, Flex provides robust support HTTP. Actually, it provides two robust options for HTTP.

The first option is the Flex class mx.rpc.http.HTTPService. You can employ the HTTPService using ActionScript or using MXML. The HTTPService works like wget or curl. You specify a URL (or a resource) to request and then specify a listener to react to the retrieved data. The HTTPService supports GET and POST methods. If you use a proxy, you can also use PUT and DELETE (which you're more likely to use when consuming REST-ian services.) You cannot request an HTTP resource unless the resource is on the same domain as the .SWF or that resource's domain's crossdomain.xml file is open to requests from your domain.

To use the object from code, you create an instance of mx.rpc.http.HTTPService. Let's look at an example usage:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="applicationCompleteHandler(event)">

    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.events.FlexEvent;
        import mx.rpc.events.FaultEvent;
        import mx.rpc.events.ResultEvent;
        import mx.rpc.http.HTTPService;

        private function applicationCompleteHandler(evt:FlexEvent):void
        {
             var service:HTTPService = new HTTPService('http://127.0.0.1:8080/'),
             service.url = 'test.txt';
             service.method ='GET'; // redundant
             service.addEventListener(ResultEvent.RESULT,
              function(re:ResultEvent):void {
                Alert.show('the response is ' + re.result);
             });
             service.addEventListener(FaultEvent.FAULT,
 function(fe:FaultEvent):void {
   Alert.show('the error is ' + fe.fault.content);
             });
             service.send();
        }
        ]]>
    </mx:Script>
</mx:Application>

Here, we construct an instance of HTTPService and pass the root URL against which subsequent requests should be evaluated. You can, of course, leave this blank and specify an absolute URL for the url property. We're using relative requests, so the url, which is test.txt, is relative to http://127.0.0.1:8080. Therefore, the fully qualified URL for the asset is http://127.0.0.1:8080/test.txt. If you have placed the web application under a different context than the root one (/), be sure to prefix test.txt. Here, the method property lets us specify which HTTP verb to use. There's no need to specify GET though, because it's redundant. To receive notification that the HTTP request was successful, we configure an event listener (for the result event). From this listener, we can access the payload of the response via the ResultEvent object instance's result property, re. If the request is not successful, we can get a notification by specifying an event listener for the fault event. Here, we interrogate the fault from the FaultEvent instance's fault property. Finally, we send the request. We could optionally specify headers as parameter to the send method using an associative array. Thus:

...
service.send({customerID: '23232432sssh543'});

There are other options available, including support for HTTPS, cookies, more sophisticated encoding of requests and responses, and so on.

If you prefer a more declarative approach, there is MXML support for declaring and configuring an HTTPService. To do what we did previously, we might configure the following:

<mx:HTTPService
id="service"
   rootURL="http://127.0.0.1:8080/"
   url="test.txt"
   method="GET"
   result="Alert.show( 'the response is ' + event.result )"
   fault="Alert.show('the error is ' + event.fault.content )"
/>

The HTTPService can now be referenced using the id property. Invoke the request just as you did before, in ActionScript code: the event listeners (for a result or a fault) specified in MXML will be invoked.

...
service.send();

The second option for creating HTTP requests is to use the class flash.net.URLLoader. There is little difference between the two options, but both are worth knowing about. To use flash.net.URLLoader, instantiate an instance, configure it, and invoke the load method with a parameter of type flash.net.URLRequest, in much the same way as you used the HTTPService.

To access the same resource as in the HTTPService examples, we might write the following:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="applicationCompleteHandler(event)">

    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.events.FlexEvent;

        private function applicationCompleteHandler(evt:FlexEvent):void
        {
             var urlLoader:URLLoader = new URLLoader();
                urlLoader.addEventListener(Event.COMPLETE, function(evt:Event):void  {
                Alert.show('the response is ' + urlLoader.data);
             });
             urlLoader.load(
                 new URLRequest('http://127.0.0.1:8080/test.txt'));
        }
        ]]>
    </mx:Script>
</mx:Application>

There are a few key differences here. First, the reference to the result data came from the urlLoader variable itself, and not as a reference on the event dispatched when result data became available. Second, instead of configuring the URL to be accessed on the urlLoader, we passed it as a parameter to the urlLoader.load method. Depending on your tastes, this API may feel more natural to use. It's a slightly more lower level API and slightly less verbose.

HTTP services provide a generic mechanism to talk to many other resources, including REST services. You might also prefer REST—generic and platform neutral—to exposing AMF- and Flex-specific services. With Java EE 6.0 and/or Spring 3.0, creating REST services is a snap, and you may prefer to interact with them over traditional remoting APIs like SOAP going forward.

If you do need to interface with SOAP services, however, manually consuming XML resources and parsing them using ActionScript's XML support can be tedious. Instead, it's better to use something more suited to the task.

Consuming SOAP Services

Sometimes, you need to consume SOAP. It happens. Spring makes it dead simple to expose SOAP services. If your application is charged with consuming them, then you'll find the Flex framework is well equipped to do so. It works very much like the HTTPService: instantiate an instance, configure it, and then send a request.

Using the WebService object from MXML is only a bit more tedious than using the HTTPService. Here, we have to enumerate which operations on the SOAP endpoint to expose.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="applicationCompleteHandler(event)">


    <mx:WebService id="echoService"
                   wsdl="http://localhost:8080/echoService?wsdl">
        <mx:operation name="echo"/>
    </mx:WebService>

    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.events.FlexEvent;
        import mx.rpc.AsyncToken;
        import mx.rpc.events.FaultEvent;
        import mx.rpc.events.ResultEvent;

        private function applicationCompleteHandler(evt:FlexEvent):void
        {
            var at:AsyncToken =
                   echoService.echo.send('Hello, World!'),
            at.addEventListener(ResultEvent.RESULT,
              function(re:ResultEvent) :void {
                Alert.show('the result is ' + re.result + ''),
                // 'Echo: Hello, World!'
            });
at.addEventListener(FaultEvent.FAULT,
              function(fe:FaultEvent) :void {
                Alert.show(fe.fault.toString());
            });
        }
        ]]>
    </mx:Script>
</mx:Application>

Using the WebService functionality from JavaScript is plain and fundamentally the same.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="setup(event)">
    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.events.FlexEvent;
        import mx.rpc.AsyncToken;
        import mx.rpc.events.FaultEvent;
        import mx.rpc.events.ResultEvent;
        import mx.rpc.soap.WebService;

        private function setup(evt:FlexEvent):void
        {
            var echoService:WebService = new WebService();
            echoService.wsdl = 'http://localhost:8080/echoService?wsdl';
            echoService.loadWSDL();

            var at:AsyncToken = echoService.echo.send('Hello, World!'),

            at.addEventListener(ResultEvent.RESULT,
                function(re:ResultEvent):void {
                Alert.show('the result is ' + re.result + ''),
                        // 'Echo: Hello, World!'
            });
            at.addEventListener(FaultEvent.FAULT,
                function(fe:FaultEvent):void {
                Alert.show(fe.fault.toString());
            });
        }
        ]]>
    </mx:Script>
</mx:Application>

Here, we import the base class (whereas the first example uses an MXML-friendly subclass) and have to remember to call loadWSDL() once we've configured the WebService. SOAP services work fine for integrating legacy systems, but we think few people would be interested in setting them up for new projects, specifically for Flex, if there's a better alternative. What's required is something really quick and compressed with binary, something that plays well with Flex and Flash itself so that marshalling complex types isn't an issue. It should also be easy to support from the Java end. Creating SOAP is decidedly not easy to support.

This is where AMF comes in.

Flash Remoting with AMF

This is the most powerful option. AMF is a format that Flash clients can read efficiently. Because it's just binary data, it's efficient when sent across the wire (especially when compared to porous text formats like SOAP, XML, or JSON). Basically, AMF is everything you could ask for! It's compact, efficient, plays well with Flash, and it's easy to use. It's got everything! Well, almost everything, anyway. The first problem is that AMF services aren't ubiquitous, and even if they were, that doesn't mean you're exposing AMF services inside the enterprise. We'll discuss solving that problem in recipes to come, but first, let's look at how we might consume an AMF endpoint in Flex, both in MXML, and ActionScript.

Using an AMF service from MXML is a matter of configuring it and sending requests, similar to the other options you've seen so far.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="setup(event)">

    <mx:RemoteObject
     endpoint="http://yourhost:8080/mb/amf"
     showBusyCursor="true"
     destination="auctionService"
     id="auctionService"
    />

    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.events.FlexEvent;
        import mx.rpc.AsyncResponder;
        import mx.rpc.AsyncToken;
        import mx.rpc.events.FaultEvent;
        import mx.rpc.events.ResultEvent;

        private function setup(evt:FlexEvent):void
        {

            var resultHandler:Function = function(
                resultEvent:ResultEvent, asyncToken:AsyncToken) :void {
                Alert.show('result = ' + resultEvent.result);
            };
            var faultHandler:Function = function(
                faultEvent:FaultEvent, asyncToken:AsyncToken) :void {
                Alert.show('fault = ' + faultEvent.fault);
            };
auctionService.getItemsForAuction().addResponder(
                new AsyncResponder(resultHandler, faultHandler));
        }
        ]]>
    </mx:Script>
</mx:Application>

Toward the top, we configure an instance of a <mx:RemoteObject />. We don't tell it specifically which service endpoint to connect to. Instead, we tell it the address of the broker (in this case, BlazeDS) and the destination (the destination is, roughly, the abstract name of the resource that's exposed). A destination is where we send messages. Whether the message is ultimately routed to a service or a JMS queue, for example, is configured on the server, but the client doesn't necessarily know either way. IN the following example, we invoke services by calling them asynchronously. To be notified of a result or of a remoting fault, we configure callback functions. The functions are used as arguments for the AsyncResponder. It is the responder that ultimately handles the results of an invocation of a method. There are two callbacks: one to receive notifications that a result was returned from the server and another to receive notifications of errors in the messaging.

To achieve the same thing using ActionScript, we need only move the configuration of the objet from MXML to ActionScript code. This, as usual, involves removing "mxml" from the package name and simply constructing the object in ActionScript.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        applicationComplete="setup(event)">

    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.events.FlexEvent;
        import mx.rpc.AsyncResponder;
        import mx.rpc.AsyncToken;
        import mx.rpc.events.FaultEvent;
        import mx.rpc.events.ResultEvent;
        import mx.rpc.remoting.RemoteObject;

        private function setup(evt:FlexEvent):void
        {

            var auctionService:RemoteObject = new RemoteObject();
            auctionService.endpoint = 'http://localhost:8080/mb/amf';
            auctionService.showBusyCursor = true;
            auctionService.destination = 'auctionService';

            var resultHandler:Function = function(
                resultEvent:ResultEvent, asyncToken:AsyncToken) :void {
                Alert.show('result = ' + resultEvent.result);
            };
var faultHandler:Function = function(
                faultEvent:FaultEvent, asyncToken:AsyncToken) :void {
                Alert.show('fault = ' + faultEvent.fault);
            };

                auctionService.getItemsForAuction().addResponder(
                        new AsyncResponder(resultHandler, faultHandler));
        }
        ]]>
    </mx:Script>
</mx:Application>

So, AMF is a very convenient protocol. If you've got an easy connectivity layer that works with one of the protocols described in this recipe, then you might consider using those. In all other cases, AMF is a very solid option. The issue is then, "how do I expose my services in AMF?" You can expose an AMF endpoint using a broker like Adobe's LiveCycle Data Services product, a commercial offering; an open source offering like Granite DS; or as we'll discuss in this chapter, an open source project that is a subset of Lifecycle Data Services called BlazeDS. BlazeDS sits on your domain and can be used to expose Java objects and services as AMF-friendly services. It can also expose messaging middleware (like JMS) in such a way that a Flex client can consume messages asynchronously, very much in the same way Ajax clients can use Comet to receive messages pushed from the server. Finally, it can be used to simply proxy other services on other domains if required. There are servers like this that implement the AMF endpoint functionality for most platforms, including Python, PHP, Perl, and Ruby.

If you're using Java, and Spring, you may want to consider BlazeDS. It's a very powerful broker, has good support, and—as you'll see—is an absolute breeze to configure. BlazeDS can actually be downloaded and run stand-alone as a .war project. But, this introduces at minimum an unnecessary deployment into your architecture and at worse an extra server. So, most people tend to pick apart BlazeDS and install the relevant bits in their own web applications. This process was tedious, but worked. It didn't play well with Spring or EJB, however, so other brokers like GraniteDS presented a stronger alternative. Now, with the Spring BlazeDS integration, you can have your cake and eat it too: the Spring BlazeDS integration lets you easily install BlazeDS into your application and works perfectly with all the things you can imagine wanting to work with from Spring: messaging (both straight JMS and with the Spring Integration service bus) and Spring components or services.

Adding the Spring BlazeDS support to an application

Problem

Now that we've gone through the options, and you've decided you want to install the Spring BlazeDS integration in an existing web application, the installation itself can vary depending on the target integration.

Solution

If you've installed any of the standard Spring web infrastructure (Spring MVC) into your application already, then installing the Spring BlazeDS support will seem trivial. If not, we'll need to explore configuring the Spring BlazeDS integration, as well as configuring BlazeDS itself.

How It Works

There are two parts to work through when installing the Spring BlazeDS integration. First, we need to install the Spring support, namely the Spring MVC DispatcherServlet. Then, we need to configure BlazeDS itself. The balance of the work to configure BlazeDS is minimal. I'll include it here, but you can as easily download the resulting file from this book's web site and plug it in to your own code, unchanged.

Installing the Spring Support

The Spring BlazeDS integration requires us to set up the same infrastructure we'd set up if we were using Spring Faces, Spring Web Flow, Spring MVC, and so on: the DispatchServlet. There is no need for any custom BlazeDS servlet or web.xml configuration at all. The configuration lives in the web.xml file, and we provide a mapping so that we know where to send Flex requests. Note that the following configuration could be used in setting up Spring MVC or Spring Web Flow, too! There is nothing BlazeDS-specific, or even specific to the Spring BlazeDS integration, in the following code:

<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
     http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

   <display-name>Spring Flex BlazeDS integration example</display-name>

   <servlet>
      <servlet-name>spring-flex</servlet-name>
      <servlet-class>
         org.springframework.web.servlet.DispatcherServlet
      </servlet-class>
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>/WEB-INF/auction-flex-context.xml</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
   </servlet>

    <servlet-mapping>
        <servlet-name>spring-flex</servlet-name>
        <url-pattern>/mb/*</url-pattern>
    </servlet-mapping>
</web-app>

This configuration is the simplest configuration possible, but it is enough to install support for Spring in your web application. You need to set up the Flex message broker and so forth, which we do inside a Spring context file. Here, we reference /WEB-INF/auction-flex-context.xml, where we set up the message broker.

The contents of the auction-flex-context.xml file follow. We won't expose any services or messaging endpoints, yet. Let's leave those for the next few recipes.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:file= http://www.springframework.org/schema/integration/file
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:flex="http://www.springframework.org/schema/flex"
xsi:schemaLocation="
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util-3.0.xsd
    http://www.springframework.org/schema/tool
    http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
    http://www.springframework.org/schema/lang
    http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/integration
    http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
    http://www.springframework.org/schema/integration/file
    http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd
    http://www.springframework.org/schema/integration/jms
    http://www.springframework.org/schema/integration/jms/spring-integration-jms-1.0.xsd
    http://activemq.apache.org/schema/core
    http://activemq.apache.org/schema/core/activemq-core-5.3.0.xsd
    http://www.springframework.org/schema/flex
    http://www.springframework.org/schema/flex/spring-flex-1.0.xsd
">

    <context:annotation-config />
    <context:component-scan base-package="com.apress.springwebrecipes.flex.auction" />

    <flex:message-broker services-config-path="/WEB-INF/flex/services-config.xml">
        <flex:message-service default-channels="my-amf" />
    </flex:message-broker>

</beans>

This context file imports quite a few namespaces because we'll use them later as we integrate services and messaging channels. Technically, though, you don't need them right now. You could import the context namespace and the flex namespace and be done with it.

Here, we use the context element to enable annotation configuration and to tell the Spring framework in which package to scan for, and automatically register, components (beans). Again, we'll use those as part of using standard Spring. So, the only thing we've configured of any interest here is the BlazeDS message broker. The message broker, in turn, creates channels, which are like named ports where messages may be sent or received from. We configure the specifics of the channel—like polling frequency, timeout settings, the specific sub-URL, streaming, and so on, all from the services configuration file. The message broker will, by default, look for the services-config.xml file in /WEB-INF/flex/services-config.xml. You can thus remove our explicit declaration, as it is redundant. We only mention it because you may want to vary that location, though the default (WEB-INF/flex) is a very broad de-facto standard.

Let's look at a very sophisticated services-config.xml configuration file. We'll reuse only a very small subset of all of the configuration in this file throughout this chapter. There are numerous configurations, some of which you may reuse to great effect. It will give you an idea of some of the things that you can support, however, to study this file.

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <services>
        <default-channels>
           <channel ref="my-amf"/>
        </default-channels>
    </services>

    <channels>
        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/mb/amf"
                      class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
        <channel-definition id="my-secure-amf"
                 class="mx.messaging.channels.SecureAMFChannel">
            <endpoint url="https://{server.name}:{server.port}/{context.root}/mb/amfsecure"
                      class="flex.messaging.endpoints.SecureAMFEndpoint"/>
            <properties>
                <add-no-cache-headers>false</add-no-cache-headers>
            </properties>
        </channel-definition>
        <channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/mb/amfpolling"
                      class="flex.messaging.endpoints.AMFEndpoint"/>
            <properties>
                <polling-enabled>true</polling-enabled>
                <polling-interval-seconds>4</polling-interval-seconds>
            </properties>
        </channel-definition>
        <channel-definition id="my-longpolling-amf"
                            class="mx.messaging.channels.AMFChannel">
            <endpoint
                url="http://{server.name}:{server.port}/{context.root}/mb/amflongpolling"
                class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
                <polling-enabled>true</polling-enabled>
                <polling-interval-seconds>5</polling-interval-seconds>
                <wait-interval-millis>60000</wait-interval-millis>
                <client-wait-interval-millis>1</client-wait-interval-millis>
                <max-waiting-poll-requests>200</max-waiting-poll-requests>
            </properties>
        </channel-definition>
        <channel-definition id="my-streaming-amf"
                            class="mx.messaging.channels.StreamingAMFChannel">
            <endpoint
                 url="http://{server.name}:{server.port}/{context.root}/mb/streamingamf"
                 class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
        </channel-definition>
     </channels>

    <logging>
        <target class="flex.messaging.log.ConsoleTarget" level="Warn">
            <properties>
                <prefix>[BlazeDS] </prefix>
                <includeDate>false</includeDate>
                <includeTime>false</includeTime>
                <includeLevel>false</includeLevel>
                <includeCategory>false</includeCategory>
            </properties>
            <filters>
                <pattern>Endpoint.*</pattern>
                <pattern>Service.*</pattern>
                <pattern>Configuration</pattern>
            </filters>
        </target>
    </logging>

    <system>
        <redeploy>
            <enabled>false</enabled>
        </redeploy>
    </system>
</services-config>

We won't review every line here. You may look into these options specifically in greater detail on your own, as required. For our purposes, we can use the following configuration, which comes in at a staggering dozen or so lines of XML!

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <channels>
        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/mb/amf"
                      class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
     </channels>
</services-config>

The endpoint's url attribute defines where we should expect this service to be mounted. Any service that's exposed using the my-amf channel will be available via that URL. You'll need this value in the client Flex application. For the sample auction application we'll develop in this chapter, we deploy the application at the root web context. You'll use the URL http://127.0.0.1:8080/mb/amf in your Flex client code.

To keep our code as ready-to-deploy as possible, you should parameterize the URL of the service in the client. Alternatively, if you know the domain name for your application, you might add an entry to your /etc/hosts file on Unix derivatives or, on Windows, to your C:WINDOWSsystem32driversetchosts file mapping 127.0.0.1 to your target domain. Thus, all references to the server—whether in production or in development—will resolve to the correct host. In this chapter, we are explicitly referencing localhost, but only because we're writing a book and don't have a separate domain we control the rights to that we can print here.

Our sample application's interface is very simple (see Figure 10-1). We'll use two synchronous services and consume one asynchronous message. Our application displays a UI with items in a grid. We also have a form with which new items may be posted. As soon as the items are posted, an asynchronous message will trigger an update of the view on all the clients who are looking at the grid, so that their display updates immediately.

This is the UI of the Flex client. On the left is the form with which clients may post new items that appear in the grid (to the right), and they appear nearly instantly thanks to our integration with Spring BlazeDS's messaging support.

Figure 10.1. This is the UI of the Flex client. On the left is the form with which clients may post new items that appear in the grid (to the right), and they appear nearly instantly thanks to our integration with Spring BlazeDS's messaging support.

With our message broker up and running, we'll look next at how to create those services and how to build the Flex client above to consume them.

Exposing Services Through BlazeDS / Spring

Problem

You've got the message broker up and running. Now, you want to deploy a Spring service as an AMF service, much like we might use a Spring exporter to export our service as an RMI service. You'll also want to put it together with what you learned before about consuming AMF services.

Solution

We'll use Spring BlazeDS to set up a simple service and demonstrate its invocation from the client—a simple auction application that we'll build on in subsequent recipes. The service, in this case, will simply fetch all the items that are for auction and return the description, a photo, and price for each item. Our implementation is a simple in-memory, POJO service, but you easily build any kind of other service, just as you would for any other application.

How It Works

Spring BlazeDS lets you expose existing Spring beans as AMF endpoints. To do this, you define your service as usual, and then modify the configuration of the service itself, or independently create a reference.

Let's look at our sample service's interface first.

package com.apress.springwebrecipes.flex.auction;

import java.util.Set;
import com.apress.springwebrecipes.flex.auction.model.Bid;
import com.apress.springwebrecipes.flex.auction.model.Item;

/**
 * provides an implementation of an auction house
 *
 **/
public interface AuctionService {

/**
Create a new item and post it.
In our client we use this to demonstrate messaging
*/
Item postItem(
        String sellerEmail,
        String item,
        String description,
        double price,
        String imageUrl);

    /**
     * Returns a view of all the items that
     * are available (which, in the sample, is everything, always).
     * We'll use this to demonstrate services.
     */
    Set<Item> getItemsForAuction() ;

    /** We don't use this in the sample
     * client, but we include it for posterity
     */
    Bid bid(Item item, double price);

    /** We don't use this in the sample
     * client, but we include it for posterity
     */
    void acceptBid(Item item, Bid bid);

}

Let's look now at the implementation:

package com.apress.springwebrecipes.flex.auction;

import com.apress.springwebrecipes.flex.auction.model.Bid;
import com.apress.springwebrecipes.flex.auction.model.Item;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.jms.*;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
@Service("auctionService")
public class AuctionServiceImpl implements AuctionService {

    static private Logger logger = Logger.getLogger(AuctionServiceImpl.class.getName());

    private ConcurrentSkipListSet<Item> items = new ConcurrentSkipListSet<Item>();
    private AtomicInteger uuidForBids = new AtomicInteger(0);
    private AtomicInteger uuidForItems = new AtomicInteger(0);
    private String images[] = "boat,car,carpet,motorbike".split(",");

    @Resource(name = "itemPosted")
    private Topic itemPostedDestination;

    @Resource(name = "bidPosted")
    private Topic bidPostedTopic;

    @Resource(name = "bidAccepted")
    private Topic bidAcceptedTopic;

    @Autowired
    private JmsTemplate jmsTemplate;

    @PostConstruct
    public void setupFakeItems() {
        Assert.isTrue(jmsTemplate != null);

        String[] items = "boat,car,carpet,motorbike".split(",");
        String[] sellerEmails = "[email protected],[email protected],[email protected], 
How It Works
[email protected],[email protected],[email protected]".split(","); for (String item : items) { String sellerEmail = sellerEmails[(int) Math.floor(Math.random() * sellerEmails.length)]; String description = String.format("A lovely %s", item); double basePrice = Math.random() * 100; String imageUrl = String.format("/images/%s.jpg", item); postItem(sellerEmail, item, description, basePrice, imageUrl); } logger.info(String.format("setupFakeItems(): there are %s items", "" + this.items.size())); }
private Message mapFromBid(javax.jms.Session session, Bid b)
  throws JMSException {
        MapMessage mm = session.createMapMessage();
        mm.setLong("itemId", b.getItem().getId());
        mm.setLong("bidId", b.getId());
        mm.setLong("acceptedTimestamp",
 b.getAccepted() == null ? 0 : b.getAccepted().getTime());
        mm.setDouble("amount", b.getAmount());
        return mm;
    }

private Message mapFromItem(javax.jms.Session session, Item item)
throws JMSException {
        MapMessage mapMessage = session.createMapMessage();
        mapMessage.setLong("itemId", item.getId());
        mapMessage.setDouble("threshold", item.getThreshold());
        mapMessage.setDouble("basePrice", item.getBasePrice());
        mapMessage.setString("sellerEmail", item.getSellerEmail());
        mapMessage.setString("description", item.getDescription());
        return mapMessage;
    }

    @Override
    public synchronized void acceptBid(Item item, final Bid bid) {
        Date accepted = new Date();
        item.setSold(accepted);
        bid.setAccepted(accepted);
        jmsTemplate.send(bidAcceptedTopic, new MessageCreator() {
            public Message createMessage(Session session)
              throws JMSException {
                return mapFromBid(session, bid);
            }
        });

    }


    @Override
    public synchronized Bid bid(Item item, double price) {
        final Bid bid = new Bid();
        bid.setAmount(price);
        bid.setItem(item);
        bid.setId(uuidForBids.getAndIncrement());
        item.addBid(bid);
        jmsTemplate.send(bidPostedTopic, new MessageCreator() {
            @Override
            public Message createMessage(Session session)
throws JMSException {
                return mapFromBid(session, bid);
            }
        });
        if (item.getThreshold() <= bid.getAmount()) {
            acceptBid(item, bid);
        }
        return bid;
    }


    private String randomImage() {
        int indexOfImage = (int)
(Math.random() * (this.images.length - 1));
        return this.images[indexOfImage];
    }


    @Override
    public Item postItem(
        String sellerEmail,
        String itemTitle,
        String description,
        double basePrice,
        String imageUrlParam) {

        String imageUrl = imageUrlParam;
        if (StringUtils.isEmpty(imageUrl)) {
            imageUrl = String.format("/images/%s.jpg", randomImage());
        }

        final Item item = new Item();
        item.setItem(itemTitle);
        item.setBasePrice(basePrice);
        item.setId(uuidForItems.getAndIncrement());
        item.setImageUrl(imageUrl);
        item.setThreshold(10 * 1000);
        item.setDescription(description);
        item.setSellerEmail(sellerEmail);

        System.out.println("adding " + ToStringBuilder.reflectionToString(item));
items.add(item);
        jmsTemplate.send(itemPostedDestination, new MessageCreator() {
            @Override
            public Message createMessage(Session session)
                throws JMSException {
                return mapFromItem(session, item);
            }
        });
        return item;
    }

    @Override
    public Set<Item> getItemsForAuction() {
        Set<Item> uniqueItemsForAuction = new HashSet<Item>();
        uniqueItemsForAuction.addAll(this.items);
        CollectionUtils.filter(uniqueItemsForAuction, new Predicate() {
            @Override
            public boolean evaluate(Object object) {
                Item itm = (Item) object;
                return itm.getSold() == null;
            }
        });
        return uniqueItemsForAuction;
    }
}

There's a lot going on here, but nothing too unusual. We're not using Hibernate or any backing data store to keep the code simpler. Instead, the service uses a ConcurrentSkipListSet<Item> instance variable (imaginatively named items). Because there's no real backing store, we spend some time constructing seed data in the setupFakeItems method, which is called after the component's been configured by Spring. Because we want to notify other viewers of any new items posted, we use JMS and a javax.jms.Topic. We'll get into the configuration for those bits in a bot, but suffice it to say that we need a simple way of issuing JMS messages. This is why we inject an instance of Spring's org.springframework.jms.core.JmsTemplate class. The acceptBid and bid methods aren't used in this example, though they are left in for posterity. Finally, toward the bottom, we get to the real meat of the service: the postBid and getItemsForAuction methods.

The bid method takes parameters required to describe a bid and creates it. A less trivial example might do validation on the object. In our case, we simply add it to the items collection and publish news of its addition to the itemPosted javax.jms.Topic.

The getItemsForAuction method returns all the items that haven't been sold (and thus, should be displayed in the grid on the client). For our application, this effectively always returns every item in the collection, as we don't use the bidding functionality and thus no item will ever be sold.

With the Java out of the way, let's examine the Spring configuration a bit. There are two parts to the configuration: the standard services configuration, and the Spring BlazeDS configuration. We've already gone a long way to configuring the standard Spring beans with the addition of the context:component-scan element from the last recipe. We'll skip the configuration of the JMS functionality for now, as we'll cover that in the next recipe. All that leaves us with is the service itself.

If you add the Spring org.springframework.stereotype.Service("auctionService") to the AuctionServiceImpl, then you don't need to do anything to configure the Spring service itself. Otherwise, add the following to your Spring XML context (/WEB-INF/auction-flex-context.xml):

<bean id="auctionService"
  class="com.apress.springwebrecipes.flex.auction.AuctionServiceImpl"/>

Finally, we tell Spring BlazeDS about the bean using the following XML:

<flex:remoting-destination ref="auctionService"/>

This allows you to take existing beans and expose them as endpoints on BlazeDS. If you're defining a bean in the same logical tier as your BlazeDS services, perhaps as a more coarse-grained service façade inside your web application, then you can simply annotate that:

<bean id="auctionService"
      class="com.apress.springwebrecipes.flex.auction.AuctionServiceImpl">
      <flex:remoting-destination destination-id="auctionService"/>
</bean>

Here, we don't need the ref attribute on the remoting-destination element.

You can exercise quite a bit of control over how the bean is exported. The remoting-destination tag will let you exclude and/or include methods on the bean that should be accessible from remote clients. Use the exclude-methods and include-methods attributes to excluded methods and include methods, respectively.

Additionally, you can configure what exported service should be exposed as under BlazeDS. By default it uses the bean name to set the destination-id. If you want something else, you can configure that here using the destination-id attribute.

Recall that in our Spring context XML for the message broker, we had a flex:message-broker element, in which we had a flex:message-service element. That element had an attribute, default-channels. If specified, <flex:remoting-destination /> may omit the channel they'd like to use when communicating with the server, as the default will be used. If, however, you'd like to use a different channel, then you may override it on the flex:remoting-destination element.

<flex:remoting-destination ref="auctionService"
                           exclude-methods="acceptBid, bid"
                           destination="theBestAuctionService"
 />

Under the hood, this support is simply instantiating an instance of the class org.springframework.flex.remoting.RemotingDestinationExporter. If you'd like to debug it and see how it works, that's the class to look at. Finally, if you—for some reason—wanted to configure it explicitly and avoid the namespace support, then that's the bean to look at.

So, given our earlier and simplest definition of the remoting destination (<flex:remoting-destination ref="auctionService"/>), we can make a call to the service from the client, just as we did before. You've already seen the full code for this example, so we won't rehash the entire thing. Here are the salient bits of ActionScript code, though:

var auctionService:RemoteObject = new RemoteObject();
   auctionService.endpoint = 'http://localhost:8080/mb/amf';
   auctionService.showBusyCursor = true;
   auctionService.destination = 'auctionService';
   var resultHandler:Function = function(
      resultEvent:ResultEvent, asyncToken:AsyncToken) :void {
         Alert.show('result = ' + resultEvent.result);
   };
   var faultHandler:Function = function(
      faultEvent:FaultEvent, asyncToken:AsyncToken) :void {
         Alert.show('fault = ' + faultEvent.fault);
   };
   auctionService.getItemsForAuction().addResponder(
      new AsyncResponder(resultHandler, faultHandler));

Working With Server-Side Objects

Problem

In the last recipe, you built a service that returned a result by invoking an operation on a server-side Spring service. The service returned a Collection of Items (Collection<Item>). The ActionScript code didn't have any type information, so your client code could have accessed incorrect properties with no warning.

Solution

You can tell the Flex runtime how to preserve the type information of objects that are serialized in the AMF format using the ActionScript [RemoteClass] attribute on a client-side mapping of the Java object.

How It Works

On the Java side, the getItemsForAuction method returns a collection of Item objects. In ActionScript, this translates into a collection of dynamic Object instances. In ActionScript, "dynamic" means that you can arbitrarily add or reference fields and methods on an object without type information. Essentially, ActionScript treats a class just like you could in the standard JavaScript you're no doubt familiar with from the browser. In the browser, this behavior is called expando properties and these are useful for us because Flash doesn't have any built-in way of knowing (and surfacing to tools like your IDE's auto-completion) the type information of the returned object. There's no mapping anywhere by default.

So, to iterate over the code inside the preceding resultHandler function, we might write

import mx.collections.ArrayCollection;
...

var acOfItems:ArrayCollection = resultEvent.result as ArrayCollection;
for each(var item:* in acOfItems)
        Alert.show( 'item.id = '+ item.id +
           ', item.description='+ item.description);

Here, we cast the resultEvent instance's result property into an mx.collections.ArrayCollection instance. We access the JavaBean properties of the Item class (an entity on the server) as properties on the Object reference on the client. This works well enough for small applications.

There are many occasions where an application is better suited to the type safety we enjoy on the server-side Java environment. We need to provide a client-side double of the server-side Java object. This object provides a mapping. We create this by a building a client-side version of the server-side entity. Here's our declaration of the Java Item class in ActionScript code:

package com.apress.springwebrecipes.auction.model
{

[Bindable]
[RemoteClass(alias="com.apress.springwebrecipes.flex.auction.model.Item")]
public class Item
{
    private var _sellerEmail:String;
    private var _basePrice:Number;
    private var _threshold:Number;
    private var _sold:Date;
    private var _item:String;
    private var _description:String;
    private var _imageUrl:String;
    private var _id:Number;


    public function get sellerEmail():String {
        return _sellerEmail;
    }

    public function set sellerEmail(value:String):void {
        _sellerEmail = value;
    }

    public function get basePrice():Number {
        return _basePrice;
    }

    public function set basePrice(value:Number):void {
        _basePrice = value;
    }
public function get threshold():Number {
        return _threshold;
    }

    public function set threshold(value:Number):void {
        _threshold = value;
    }

    public function get sold():Date {
        return _sold;
    }

    public function set sold(value:Date):void {
        _sold = value;
    }

    public function get item():String {
        return _item;
    }

    public function set item(value:String):void {
        _item = value;
    }

    public function get description():String {
        return _description;
    }

    public function set description(value:String):void {
        _description = value;
    }

    public function get imageUrl():String {
        return _imageUrl;
    }

    public function set imageUrl(value:String):void {
        _imageUrl = value;
    }

    public function get id():Number {
        return _id;
    }

    public function set id(value:Number):void {
        _id = value;
    }
}
}

Importantly, the class is annotated with the RemoteClass tag. Usually, this tag is used to tell the Flex runtime to preserve type information when serializing Flash objects into and out of the AMF format. Here, in conjunction with the alias attribute, it tells the Flex runtime the type information it should imbue AMF data coming from the server. The alias attribute should be set with the Java object for which this class is a mapping.

Our previous code snippet may now be rewritten as:

import mx.collections.ArrayCollection;

import com.apress.springwebrecipes.auction.model.Item;
...
var acOfItems:ArrayCollection = resultEvent.result as ArrayCollection;
for each(var item:Item in acOfItems)
        Alert.show( 'item.id = '+ item.id +
           ', item.description='+ item.description);

This doesn't represent a giant change in the code by any stretch, but now, auto-completion works in your favorite IDE, and you can refactor. Additionally, you may define methods on the ActionScript object—perhaps to update state in peculiar ways. Additionally, you now have a place to put some validation or constraints.

It can quickly become tedious to have to define ActionScript classes for every entity on the server side. There are solutions that can help, however. We mention the Granite DS entity broker earlier, for example. It has an Eclipse plug-in or Ant task (the Ant task can, in turn, be used with Maven if you'd like) that can be used to scan compiled Java classes and emit equivalent ActionScript. For every Java class, the plug-in will emit two ActionScript classes of the form XBase.as, and X.as, where X is the name of your Java class.

The plug-in will write all the accessor/mutator code and some optimized serialization logic in the XBase class and have the X class extend the XBase one. As an example, if you want to add logic to ensure that phone numbers comply with some sort of validation routine, you may put an override in the X class and have it call super once the property has been validated. If you rerun the generator, it will not overwrite the X class, only the XBase one, so your changes survive iterations. There are other such features like this in various projects. We've had good experience with the Granite Data Services Gas3 plug-in described here. Note that the code generated by the Granite Data Services plug-in works well with BlazeDS, too. To find out more about the generator, refer to http://www.graniteds.org/confluence/display/DOC/2.+Gas3+Code+Generator.

Consuming Message-Oriented Services Using BlazeDS and Spring

Problem

How do you build solutions that can handle so-called push-oriented messaging? In the Ajax world, these are called Comet–style applications. How do you connect Flex clients to each in an asynchronous manner? How do you work with message-oriented middleware (MOM) services like JMS to send and receive messages? Finally, how do you architect solutions that talk to a more sophisticated event bus like Spring Integration?

Solution

We'll use Spring BlazeDS to connect our client to the BlazeDS messaging facilities. BlazeDS supports JMS, as well as a regular BlazeDS messaging facility already. The Spring BlazeDS integration simplifies both integrations and provides a third to let client applications send and receive messages on Spring Integration channels. Spring BlazeDS works with Spring Integration to let you bind any arbitrary endpoint (be it an e-mail server, a Twitter user's update feed, an FTP server, a file system, or anything for which you want to write an adapter) to the Spring BlazeDS messaging facilities.

How It Works

Thus far, our auction application has talked to the server, but it's not really been a conversation, has it? If the client asks the server for something, the server responds. What if the server has something more to say? This question is ignored in most web applications because the answer is usually "we can't afford to find out." HTTP itself is stateless and a pull-only protocol. It has no way of pushing data to clients. This a problem in that many applications are better served by being able to push data, like for example chat rooms, or stock tickers, or generally anything whose frequency of change is variable and out of the client's control (which, if we're honest, applies to most applications).

We've built abstractions to solve all sorts of oddities with HTTP to make it more suitable as application platform, why not solve the push problem? It turns out, people have tried; it's just not that easy to solve. In the web browser environment, people use polling, Comet, and piggybacking to mimic push-style architectures. Polling works by polling a well-known server resource at a consistent interval and retrieving updates. Comet works by opening a request and keeping it open, pushing out a ping byte whenever there's updated state on the server. Another option is to send data only when a request is sent to the server (not polling, in that the request isn't specifically designed to ascertain whether some resource has pushed data, so much as it is to request some other resource). The pushed data—if any—piggybacks the request.

Each of these options has its own advantages and disadvantages: Polling can overwhelm a server if the request is processing intensive, time intensive, or too frequent. Comet can quickly bog down a server because it requires keeping open a thread for each client. Piggyback treats the server nicely but can be unreliable or untenable for an application requiring guarantees of freshness of data: what happens if the client doesn't ever send a request to piggyback on?

People have tried to bake in support for these architectures at the server level. Newer servers have built more efficient threading options so that threads can be passivated or so that threads require minimal amounts of memory. Jetty, for example, supports asynchronous and Comet-friendly threading models through its continuations support. Tomcat eventually added similar support for this through its CometProcessor class. Grizzly (Glassfish 3) has the CometEngine class. Languages like Google's Go offer solid support for spawning numerous lightweight threads with almost no cost. Indeed, HTML 5 itself offers the WebSocket standard, which provides a way to solve these problems. The third edition of the Servlets specification, part of Java EE 6, offers support for asynchronous servlets too. But none of these is quite there yet, and in the meantime, there are applications to build. So, we're back to simulating the effect.

We use the word "simulate" here because for a Java developer in the enterprise world or even in the world of regular protocols, this stuff seems like a horrid hack. In the enterprise world, if one service publishes a message, any subscriber can register as a listener, and as soon as there's a message, the subscriber is notified instantly. Sure, we know that behind the scenes there's some sort of very efficient polling happening, but we don't have to work in terms of the polling. We work in terms of the event raised by the arrival of a new message. This is called an event-driven architecture (EDA). When Spring developers write message-driven POJOs, or EJB developers write message-driven beans, the objects are isolated from the means by which messages arrive.

It seems a shame then that web developers don't enjoy such elegance. The only way to restore it is to make the use of message-oriented services, which are an aspect in the framework code, not the client code. If you use the DWR project (a popular Ajax project), then you know that you can create "reverse Ajax" applications, where your client code enjoys essentially the same benefits.

Flex too enjoys a framework–level solution for handling push messaging when working in concert with a message broker like BlazeDS. Messages are routed to and from BlazeDS, which talks to your Flash client. The advantage of this solution is that Flex and BlazeDS are smart enough to keep track of the state of the client. Also, they can do things to help you out, even when you don't realize you need it. For example, most browsers cannot open more than a certain number of HTTP connections. Behind the scenes, though, when a Flex client opens a connection to listen for push messages, it is, in fact, doing one of the three techniques we described before—Comet, piggybacking, or polling—or some combination thereof. Thus, were a Flex client to open a channel for consuming push messages using long-lived connection (Comet), it's using the browser's HTTP connections, and the number of available HTTP connections is decremented by one. If you then try to invoke an RPC call, that's another connection that's used. This works because most browsers support two concurrent connections. If, however, the limit were two, and you instead created two connections for consuming push messages, there would be no connection available for invoking services. In this situation, you can configure BlazeDS to vary the type of functionality it provides by configuring the BlazeDS services-config.xml file. Here, you can specify that requests against a destination "a" are handled using Comet and requests against a lesser-priority destination "b" are handled using polling. Thus, connections to the server are intermittent on that destination, but the client is still able to perform RPC invocations.

Let's look at how you consume messages pushed from the server in Flex before we dig into the nitty-gritty configuration behind the scenes. In our auction application, we want the UI notified whenever any new message is published on the itemPosted topic. In our client's setup method (called when the applicationComplete event fires), we initialize the consumer:

import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.messaging.*;
import mx.messaging.channels.AMFChannel;
import mx.messaging.events.*;
import mx.rpc.*;
import mx.rpc.events.*;

...

private var itemPostedDestinationConsumer:Consumer;

...

public function onApplicationComplete(flexEvent:FlexEvent):void
{
      var aChannelSet:ChannelSet = new ChannelSet();
      aChannelSet.channels = [new AMFChannel('my-amf','http://localhost:8080/mb/amf')];
      itemPostedDestinationConsumer  = new Consumer() ;
      itemPostedDestinationConsumer.channelSet = aChannelSet;
      itemPostedDestinationConsumer.destination='itemPostedDestination' ;
itemPostedDestinationConsumer.addEventListener(
         MessageEvent.MESSAGE, function(me:MessageEvent):void {
            // react to the new item (perhaps re-paint the UI)
            layoutGridOfItems();
            Alert.show('A new item has arrived!'),
         });
      itemPostedDestinationConsumer.subscribe();
}

First, while this may seem verbose compared to the rest of the examples in this chapter, we dare you to write all the code you need to set up a MessageContainer and subscribe to messages using raw JMS! Second, we can improve on even this Spartan code, as you'll see in the next recipe.

The code is about what you'd expect. We create a Flex Consumer and tell it the BlazeDS destination it should consume messages from, and on what channel the client should communicate. The destination, in this case, is the BlazeDS assigned name for the resource from which we will consume messages. The destination, on the BlazeDS side, could in turn be linked to a JMS queue, a Spring Integration channel, and so on. This indirection is valuable because it keeps our client code flexible and unaware of how messages are being produced. Then, we register a listener in exactly the same manner as we would listen for a button click. Finally, we issue the subscribe method, telling the client to start consuming messages as they come in. It doesn't get much simpler than this!

Now, let's see how we configure the server side Spring BlazeDS message broker. No matter which option you choose, you'll use the Spring BlazeDS namespace support to declare a destination in Spring and assign it an ID of itemPostedDestination (because that's what the client code we just created is subscribed to).

We already looked at the code for the service before, so we know that we're using JMS in this particular integration. Let's first look at using Spring BlazeDS to talk directly to JMS. In our example, we want to react to messages declared on the javax.jms.Topic named itemPosted. In JMS, a javax.jms.Topic is used to describe a named channel in a JMS server whose messages are sent to all clients that are subscribed to it. This option is the enterprise equivalent of adding an action listener using Flex. This is as opposed to a javax.jms.Queue, where each message sent to the queue is read by one client and then removed.

In our example, we're using the freely available ActiveMQ JMS broker because it provides good support for Spring. You can configure connection factories as you might with any JMS broker, but you can also use a Spring namespace to configure options on the server like your destinations (javax.jms.Topic, javax.jms.Queue), and it's got many best-of-breed messaging-oriented-middleware (MOM) features like clustering and XMPP support. Plus, it's easy to get started with. Here are the steps you'll need to get it installed:

  1. Download the freely available Apache Active MQ project (http://activemq.apache.org/). We're using version 5.3 for this book, but you should have no trouble with later versions (or even earlier ones, especially if only off by a few point releases).

  2. Unzip it.

  3. Start the project by executing the appropriate activemq script (there's one for Unix-variants and one for Windows) in the unzipped project's bin directory.

We'll use the ActiveMQ broker for both the JMS example and the Spring Integration example. Let's explore the various ways to produce and consume messages from Flex.

JMS

The support for JMS on the BlazeDS involves configuring a JMS connection factory in Spring. We want to ensure we are using a javax.jms.Topic, and not a javax.jms.Queue, so we will use the ActiveMQ library namespace support for Spring to create it. This is standard configuration that you will do no matter what your ultimate use is for JMS.

Add the following to the /WEB-INF/auction-flex-context.xml file that we defined in the previous recipe. We have already imported the amq (Active MQ) namespace in that file.

<bean id="connectionFactory"
  class="org.springframework.jms.connection.CachingConnectionFactory"
  p:sessionCacheSize="10"
  p:cacheProducers="false">
      <property name="targetConnectionFactory">
          <amq:connectionFactory brokerURL="tcp://localhost:61616" />
      </property>
</bean>

The inner amq:connectionFactory element defines a org.apache.activemq.spring.ActiveMQConnectionFactory instance. This defines the connection factory, but to add an extra layer of robustness, we create a connection factoring pool, org.springframework.jms.connection.CachingConnectionFactory.

To be able to work with the JMS connection factory from our services, we create a JmsTemplate, wiring in the connection factory proxy that we've just configured.

<bean id="jmsTemplate"
      class="org.springframework.jms.core.JmsTemplate"
      p:connectionFactory-ref="connectionFactory" />

Finally, we use the ActiveMQ namespace support to create the javax.jms.Topic instances that we'll need (if they don't already exist).

<amq:topic id="itemPosted" name="itemPosted" physicalName="itemPosted" />
<amq:topic id="bidPosted" name="bidPosted" physicalName="bidPosted" />
<amq:topic id="bidAccepted" name="bidAccepted" physicalName="bidAccepted" />

With all this in place, our service has what it needs to be able to talk to JMS. We've not configured any piece that's specific to Flex. Let's get that out of the way now. Add the following, near where you defined the message broker previously:

<flex:jms-message-destination id="itemPostedDestination"
             jms-destination="itemPosted" channels="my-amf"
             connection-factory="connectionFactory"  />

And that's it! The id defines the string that we use to connect to the JMS destination from the Flex client. That's the only part that permeates your code. The jms-destination attribute correlates to an ID used in defining the topics with the amq:topic element. So, the form to post a new item is completed and submitted. The form invokes the postItem method on the Spring service, which in turn sends a message, using the jmsTemplate, out to the itemPosted destination. We've then configured Flex to listen for that message here using the flex:jms-message-destination. Because it's being sent on a Topic, it will multicast the message, so that everyone who's using the application will see the message. To try it, open the Flex application from multiple browsers, or computers, and log in. You want to use different browsers so you can avoid reusing the same session. Post an Item from one of them. Watch as all the others are updated instantaneously. You should see an alert message saying, "A new item has arrived!"

Spring Integration

In the previous example, we used JMS to create a publish-and-subscribe style architecture. Because it was a sort of circuit, it worked fine. However, there is often a requirement to have Flex user interfaces be aware of other events besides ones that the Flex UI itself generates—events that are either specific to your business or don't talk to JMS by default. The list of such applications is infinite. You don't have to think long before the possibilities become overwhelming, and exciting. What if you could have your user-interface update each time e-mail arrives? Or an RSS feed updates? Or what if somebody logged in via a Jabber server (XMPP; for example, using Google Talk)? What if your Flex client was designed to facilitate an editorial workflow and needed to load up new entries from a content management system for approval when ever any changes were made? Or perhaps it is the user interface for an audit system, so new customer loan applications need to be reviewed as they arrive. Need to load up the contents of files dropped on a shared mount? What about reacting to Twitter or Facebook status updates? What about when new rows are inserted in a watched view on the database?

These kinds of integration problems require an extra piece of middleware to let our code consume these various events while remaining indifferent to their source. The prescription for these kinds of solutions is usually an enterprise service bus (ESB). So, we will use one too, of a sort. We will use Spring Integration. Spring Integration is great because it's not a proper server, but instead an embeddable set of components that you configure using Spring. It lets you consume and produce messages for arbitrary endpoints, and if there's no support out of the box, it's trivially easy to build your own.

In our example, we will simply rework the JMS example: instead of sending messages to the JMS topic and then consuming them in Flex, we'll provide two ways to add items: one from JMS, as before, and one from a shared file system where new files are to be processed and their contents transformed into new Items, which get added via a call to the service. Either input source will ultimately be sent to JMS, where Spring Integration will consume the messages and forward it to the BlazeDS destination. Our Flex client will consume messages from this destination, as usual, and remain unchanged. We'll work entirely in our Spring application context to achieve these changes, using Spring Integration.

The way Spring Integration works is it lets you establish a sort of gauntlet for your message to pass through. It has to go through each component until the goal is finished. It lets us configure as much as possible about the way messages get into a pipeline and about where they ultimately end up. You use adapters to read and write to external systems. You use other specialized components to handle transformation of the message and so on. Each component has an input message and an output message. Some components let you take an input and transform it, so the resulting output is different than the input. Others let you do things like forking control on the process: perhaps one message (a File object) enters and multiple messages (each line of the file) leave the component. Just as you do with Spring Web Flow or Struts, Spring Integration adds a layer of indirection between components. Given two components, component "a" and component "b," a product for component "a" may be passed to component "b." Perhaps in the future it's decided that there needs to be processing between the two components, so you introduce component "c" and stick it between them. In normal solutions, this would present a problem. With Spring Integration, however, there's a level of indirection. Components are connected by channels, which are basically named pipes. The pipe takes the output of a component and pipes it as the input of another. You can rearrange the components in a pipeline by simply changing the pipes going into and out of it, in much the same way that you can isolate your Spring Web Flow pages of the knowledge of what page will succeed the current one.

The files should have a payload with a well-known format: here, for the sake of simplicity, we'll assume that the contents of the file are on one line and that the line can be split into columns along comma (,) characters. The first column will be the seller's e-mail, the second will be the item, the third description, and the fourth the base price. We'll then call the service to add this item and send it to JMS, as usual. So, we have two contributories feeding into the JMS topic that eventually gets consumed and piped to the BlazeDS destination: the file system integration and the service, which sends the event directly to the JMS topic.

To make these changes, we'll configure a few Spring Integration components: We need a way of reading files from a well-known directory, so we'll use an inbound file adapter. We'll need a way to convert the java.io.File from the file system into a String with the contents of the java.io.File, so we'll use a file-to-string transformer. We'll need a way of converting the String into an Item that we can call the service with, so we'll create a transformer. We'll need a component to actually call the postItem method on the service, which will send the Item to JMS. We'll have a component consume all messages that come off the JMS topic—regardless of source—and then feed those to the BlazeDS destination. Should be simple enough! In this process, all that we need to do is tell the Spring Integration message bus how to convert our simple CSV format into an Item and how to invoke our service. For that purpose, we'll create two Java classes: FileToItemTransformer and ItemCreationServiceActivator to transform the CSV String and to invoke our service, respectively.

There is a lot of power here.

Let's look at our initial solution. First, we must declare the channels that connect the components.

<integration:channel id="inboundItemFiles"/>
    <integration:channel id="inboundItemFileStrings"/>
    <integration:channel id="inboundItems"/>
    <integration:channel id="inboundItemsPosted"/>
    <integration:publish-subscribe-channel id="inboundItemsAudited"/>

With the channels introduced, let's start analyzing the components that the channels connect.

<bean id="itemFilesMount"
      class="org.springframework.core.io.FileSystemResource"  >
   <constructor-arg
    value="#{ systemProperties['user.home']+'/flexCsvFiles' }"/>
</bean>

<file:inbound-channel-adapter
       auto-create-directory="true"
       directory="file:#{itemFilesMount.file.absolutePath}"
       channel="inboundItemFiles">
        <integration:poller>
<integration:interval-trigger interval="1000"/>
        </integration:poller>
</file:inbound-channel-adapter>

First, we create a Spring framework Resource to declare the directory where we will look for new files. We avail ourselves of the new Spring 3.0 expression language to use a standard System property, user.home, to determine the home directory of the current user. The folder we want will be in the home directory, under a folder named flexCsvFiles. Now, we can create a file:inbound-channel-adapter to poll that directory and consume new files as they become available. To ensure that the directory is created when the integration is deployed, we set the auto-create-directory attribute to true. To tell the adapter which directory to use, we again employ the Spring Expression Language to reference access the String representation of the Resource we created by dereferencing the Resource's file property, which we dereference again to get the absolutePath. The <file:inbound-channel-adapter /> is configured here to scan the directory at an interval of 1,000 milliseconds (every second) and if it sees any new files, it creates a message, which it puts through the inboundItemFiles channel. Note that you can also specify a glob for the inbound channel adapter, though we don't here.

<file:file-to-string-transformer
       input-channel="inboundItemFiles"
       output-channel="inboundItemFileStrings"
       delete-files="true" />

The next two components in the pipeline are instances of a standard Spring Integration component called a transformer. The first one, shown in the preceding snippet, is a standard transformer that we'll reuse. It knows how to take a java.io.File object and read its contents into a String. The String contents are returned on the inboundItemFileStrings channel where the next component, another transformer, will be expecting it. Since we don't want to throw the integration processing into an infinite loop, we use the delete-files option on the file-to-string-transformer component to remove the file from the directory once it has read the file and sent it.

<integration:transformer
     input-channel="inboundItemFileStrings"
     ref="fileToItemTransformer"
     output-channel="inboundItems"/>

The next component, shown in the preceding snippet, is a customer transformer because Spring Integration has no way of knowing how to transform that String into an Item object. It takes the input of the input-channel and transforms it using transformation logic that we supply.

package com.apress.springwebrecipes.flex.auction.integrations;

import com.apress.springwebrecipes.flex.auction.model.Item;
import org.apache.commons.lang.StringUtils;
import org.springframework.integration.annotation.Transformer;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class FileToItemTransformer {

    @Transformer
    public Item transformFromFileStringToItem(String fileContent)
      throws IOException {


        if (StringUtils.isEmpty(fileContent))
            throw new RuntimeException(
                "the file content is empty; can't create Item");
String[] parts = fileContent.split(",");

        if (parts.length != 4)
            throw new RuntimeException(
             "couldn't parse the file; can't create Item");

        String seller = parts[0],
               item = parts[1],
               description = parts[2],
               basePrice = parts[3];

        Item itemObj = new Item();
        itemObj.setDescription(description);
        itemObj.setItem(item);
        itemObj.setSellerEmail(seller);
        itemObj.setBasePrice(Double.parseDouble(basePrice));
        return itemObj;
    }
}

Spring Integration looks at the Spring bean configured with the ref attribute—as in the preceding code—to get our transformation logic. It scans the class for any methods annotated with a @Transformer annotation, and it invokes that. It takes the result and sends it out on another channel, inboundItems. Because this is a simple demonstration, the code is intentionally and artificially optimistic and simple; no error handling or validation is done at all.

<integration:service-activator
        input-channel="inboundItems"
        ref="itemCreationServiceActivator"/>

The next component in the pipeline—in the preceding snippet—actually calls our Spring service, AuctionService, to insert the Item into our repository. Note that the component has no output-channel configured here. This is because we know that the service invokes JMS directly, sending the Item to the itemPosted javax.jms.Topic.

package com.apress.springwebrecipes.flex.auction.integrations;

import com.apress.springwebrecipes.flex.auction.AuctionService;
import com.apress.springwebrecipes.flex.auction.model.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;

@Component
public class ItemCreationServiceActivator {

    @Autowired
    private AuctionService auctionService;
@ServiceActivator
    public void postItem(Item item) throws RuntimeException {
        auctionService.postItem(
            item.getSellerEmail(),
            item.getItem(),
            item.getDescription(),
            item.getBasePrice(),
            null);
    }
}

The preceding code is very simple. It takes the input message, an Item, and uses it to call the postItem method on our AuctionService instance.

Our next step then is to consume those messages and funnel them to BlazeDS. We don't care if the JMS messages came from postings inside our Flex application or if they came from this file integration. We need to send them to the BlazeDS middleware, so it can publish the new Item to all the logged in Flex clients.

<jms:message-driven-channel-adapter
       connection-factory="connectionFactory"
       destination="itemPosted"
       channel="inboundItemsPosted"
    />

We'll set up a jms:message-driven-channel-adapter, as shown in the preceding code, which will consume messages and send them on the inboundItemsPosted channel, in much the same way as our file:inbound-channel-adapter consumed files and sent them out on its channel attribute. Because this is JMS-specific, we need to configure the connection factory, as well as what destination it should look at. We simply use references to the connection factory and ActiveMQ topics that we declared earlier.

Normally, we would send each message received from JMS directly to the BlazeDS integration. There is one small fly in the ointment, however. A Spring Integration pipeline works on the assumption that each component in the pipeline is available. In this case, we'll do all this processing and then send it to BlazeDS where we hope the event will be consumed. If there is no one logged into a Flex client consuming messages, then the message itself can't be consumed, and we get an exception. So, what we do is send the message ourselves and ignore the failure result. We don't actually care if BlazeDS consumes it. Remember, JMS is being used here to send event messages, not document messages that need to be consistent and honored. We can ignore the events, as we reload the UI with the latest data on the Flex client's load, anyway. So, we'll add a middleman who can swallow the error state for us:

<integration:service-activator
      input-channel="inboundItemsPosted"
      ref="messageAbsorber"
      output-channel="inboundItemsAudited"  />

Our component is a service-activator. As before, the service-activator takes input on the input-channel and delegates the heavy lifting to the Spring bean referenced by the ref attribute. Again, we could send the result out through an output-channel, but that may, in some cases, cause an Exception when there is no consumer on the other end. So, we'll cheat and simply handle the send operation by ourselves. You can inject any Spring Integration component just as you would any other bean. We use this to good effect to inject the implementation class for the inboundItemsAudited channel itself.

package com.apress.springwebrecipes.flex.auction.integrations;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageChannel;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;

@Component
public class MessageAbsorber {

    @Resource(name = "inboundItemsAudited")
    private MessageChannel publishSubscribeChannel;

    @ServiceActivator
    public void handle(Message<Map<String, Object>> msg) {
        boolean status = publishSubscribeChannel.send(msg);
        // do not care about status
    }
}

Here, we simply swallow the status—throwing no exceptions or anything. If there's a consumer for this message, then the status will be true. If not, it's no problem either.

Ultimately, the BlazeDS needs to know to look for messages coming from a Spring Integration channel. Just as we told BlazeDS where to look for new JMS messages, we will tell BlazeDS where to look for new messages coming from a Spring Integration channel.

<flex:integration-message-destination
     channels="my-amf"
     id="itemPostedDestination"
     message-channel="inboundItemsAudited"
    />

The channels attribute and the id attribute should appear familiar. The only thing different here is the use of the flex:integration-message-destination and the message-channel attribute. These simply tell BlazeDS to consume messages being sent from the MessageAbsorber.

BlazeDS

You've seen the incredibly rich support the Spring BlazeDS project offers for tying your application to existing MOM and integrations. Sometimes, however, you have no other messaging middleware or integration processing. Sometimes, you simply want other Flex clients to be notified of something. BlazeDS supports this use case as well. To declare a destination that can only receive messages from Flex clients simply configure a <<flex:message-destination />.

<flex:message-destination channels="my-amf" id="myDestination"/>

Sending Messages from Flex

Such a destination can only be used by clients who know how to talk to BlazeDS, like Flex. So, how does Flex send messages to this destination, or indeed any of the destinations you've seen? The API, like the one for consuming messages, is minimalistic. Let's walk through the nominal BlazeDS messaging example: the chat room!

<?xml version="1.0" encoding="iso-8859-1"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
                pageTitle="Chat" creationComplete="setup(event)">

    <mx:Panel title="Chat" >
        <mx:TextArea id="output" height="200" />
        <mx:TextInput id="input" />
        <mx:ControlBar horizontalAlign="center" >
            <mx:Button id="send" label="Send" click="sendChatMessage(event)"/>
            <mx:Button id="clear" label="Clear" click="output.text =''" />
        </mx:ControlBar>
    </mx:Panel>

    <mx:Script>
        <![CDATA[

        import mx.events.FlexEvent;
        import mx.messaging.ChannelSet;
        import mx.messaging.Consumer;
        import mx.messaging.Producer;
        import mx.messaging.channels.AMFChannel;
        import mx.messaging.events.MessageEvent;
        import mx.messaging.messages.AsyncMessage;

        private var chatPublisher:Producer;
        private var chatConsumer:Consumer ;

        public function setup(fe:FlexEvent):void
        {

            var chatDestination:String = 'chatDestination';

            var cs:ChannelSet = new ChannelSet();
            cs.channels=[ new AMFChannel(
                 'my-amf','http://localhost:8080/mb/amf') ];

            chatPublisher = new Producer();
            chatPublisher.channelSet =cs;
            chatPublisher.destination = chatDestination;
            chatPublisher.connect();
chatConsumer = new Consumer();
            chatConsumer.channelSet = cs;
            chatConsumer.destination = chatDestination;
            chatConsumer.addEventListener(MessageEvent.MESSAGE,
              function (msgEvent:MessageEvent):void {
                 var msg:AsyncMessage = msgEvent.message as AsyncMessage;
                 output.text += msg.body + "
";
              }
            );
            chatConsumer.subscribe();
        }

        private function sendChatMessage(me:MouseEvent):void
        {
            var msg:AsyncMessage = new AsyncMessage();
            msg.body = input.text;
            chatPublisher.send(msg);
            input.text = "";
        }
        ]]>
    </mx:Script>
</mx:Application>

This application connects a Consumer and a Producer to the same BlazeDS destination, chatDestination. The definition of the chatDestination in the Spring BlazeDS configuration is as follows:

<flex:message-destination channels="my-amf" id="chatDestination"/>

In the application, we set up a simple text area in which text may be entered. When the text is submitted, we use the Producer to send the message to BlazeDS. If you open up this example in multiple browsers, you'll see that all the other browsers update their view of the chat instantly.

Bringing Dependency Injection to your ActionScript Client

Problem

How do you use dependency injection on the Flex client? Spring has long made Java and .NET applications cleaner by providing a powerful way to configure your components. How do you do the same for your ActionScript code? In Flash or Flex? In the previous examples, whether we configured the Producers and Consumers and RemoteObjects using MXML or ActionScript, it still represented a source of code duplication. If we wanted to connect to the same services in a component, for example, we'd have to worry about passing around references, or rewrite the resource acquisition logic. Because ActionScript is statically typed and Flex is a component-oriented system, it is very easy to build sophisticated applications with hundreds of components that communicate. It is thus very important to put in place best practices for isolating that which changes from that which stays the same. In the Java world, we've solved this problem using Spring.

Solution

We'll use the Spring ActionScript project (formerly called Prana) to rebuild the simple Flex chat client with remote services and consumers configured externally, where it may be reused and reaccquired by components in the application in much the same manner as you're familiar with from Spring in Java. The similarities run deep between the two; Spring ActionScript even includes an [Autowired] annotation!

How It Works

The Spring ActionScript project is an ActionScript implementation of the ideas behind the Spring framework. It is a Spring extension project whose homepage is http://www.springsource.org/extensions/se-springactionscript-as (additionally, the main project page is http://www.springactionscript.org). Spring ActionScript can be used in regular Flash, Flex, or AIR environments. We'll concentrate on using it with Flex. Spring ActionScript won't help us reuse Spring services from Java in ActionScript, but it will help us clean up our ActionScript code. The Spring ActionScript project works in much the same way as a Spring project: you configure objects (not beans!) in an XML file and load the XML file in ApplicationContext subclass. There are two ApplicationContext implementations: one for Flex environments (org.springextensions.actionscript.context.support.FlexXMLApplicationContext) and one for pure Flash environments (org.springextensions.actionscript.context.support.XMLApplicationContext).

The Flex one has some extra niceties that support automatic injection of objects into Flex components. This is powerful because Flex components are managed by the Flex runtime, not the Spring container, and so being able to have variables tagged with the [Autowired] tag resolve correctly is a powerful win, even despite those limitations.

Let's put them to use and rework our chat example to get its mx.messaging.Producer and mx.messaging.Consumer references from the Spring application context. First, we need to instantiate the application context.

<?xml version="1.0" encoding="iso-8859-1"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" pageTitle="Chat"
How It Works
applicationComplete="setup(event)"> <mx:Panel title="Chat"> <mx:TextArea id="output" height="200"/> <mx:TextInput id="input"/> <mx:ControlBar horizontalAlign="center"> <mx:Button id="send" label="Send" click="sendChatMessage(event)"/> <mx:Button id="clear" label="Clear" click="output.text =''"/> </mx:ControlBar> </mx:Panel> <mx:Script> <![CDATA[
import mx.controls.Alert;
        import mx.events.FlexEvent;
        import mx.messaging.Consumer;
        import mx.messaging.Producer;
        import mx.messaging.channels.AMFChannel;
        import mx.messaging.events.MessageEvent;
        import mx.messaging.messages.AsyncMessage;

        import org.springextensions.actionscript.context.support.FlexXMLApplicationContext;
        import org.springextensions.actionscript.context.support.XMLApplicationContext;

        private var chatProducer:Producer;
        private var chatConsumer:Consumer;

        private var _applicationContext:XMLApplicationContext;

        [Bindable]
        [Embed(source="app-context4.xml",
mimeType ="application/octet-stream")]
        public var contextConfig:Class;

        public function setup(fe:FlexEvent):void
        {
            _applicationContext = new FlexXMLApplicationContext();
            _applicationContext.addEmbeddedConfig(contextConfig);
            _applicationContext.load();

        }

        private function sendChatMessage(me:MouseEvent):void
        {
            var msg:AsyncMessage = new AsyncMessage();
            msg.body = input.text;
            chatProducer.send(msg);
            input.text = "";
        }
        ]]>
    </mx:Script>
</mx:Application>

We've torn out all the initialization logic in our chat application and focus in this example on bootstrapping a Spring ActionScript application context. There are many ways to load an XML context, including via remote URL or with an embedded resource. The simplest and most reliable is probably the embedded resource approach—to simply have the compiler embed the contents of the resource in the compiled SWF file itself. To access it, we use the [Embed] attribute to tell Flex which resource to embed and the MIME type of that resource. Flex injects the content of that resource into the variable that's been tagged. Then, in our listener for the applicationComplete event dispatched by the Application, we initialize the FlexXMLApplicationContext instance. We call the addEmbeddedConfig method to tell it to bootstrap the XML context using the contents of that embedded XML file resource. Finally, we call the load method on the context.

In other scenarios, when using some other kind of resource besides an embedded one, loading is an asynchronous operation. In those cases, you need to attach a listener and have it tell you when the XML file has been loaded. This is why the load operation is pivotal and why instantiation of the context doesn't happen on the construction of the FlexXMLApplicationContext itself.

Now, let's flesh out our first Spring XML application context file, app-context4.xml.

The configuration support for Flex ActionScript is very powerful and become even more with every release. As of version 0.8.1, there is preliminary support for namespaces, for example. If we're honest, you're not going to need a lot of the complex functionality you have in the Java version, either. Put another way: in Java Spring, we use the configuration options to both enforce the DRY principle and to abstract away complex object construction scenarios like those involved in declarative transaction management, and so on. Flex can be tedious, but it's far more likely you'll use Flex to avoid duplicated code.

The simplest possible application context looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<objects
xmlns="http://www.springactionscript.org/schema/objects"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springactionscript.org/schema/objects
http://www.springactionscript.org/schema/objects/spring-actionscript-objects-1.0.xsd"
>

<!-- hello, world! -->

</objects>

It should be clear what's happening here. In lieu of beans elements, we're using objects elements. The namespaces have been changed a bit, too. This should be familiar ground for us Spring developers!

Let's add some features to our Spring application context to support us. One thing we will want is the ability to resolve Flex runtime properties made available by the Application class. These properties give your application an idea of its context—where and how fast it is running, at what URL, and so on. Among the properties supported are applicationurl.port, application.url, application.url.host, and application.url.protocol. To resolve the properties, we'll add the Spring ActionScript analog of a BeanPostProcessor. Add the following in between the objects elements:

<object class="org.springextensions.actionscript.ioc.factory.config.flex.
ApplicationPropertiesResolver"/>

Now, just as you'd expect, you can refer to these properties in your configuration using the variable interpolation syntax, like this: ${application.url}.

The next thing to do is add support for having Spring ActionScript automatically wire dependencies on all components it adds to the stage. To do this, add the following:

<object class="org.springextensions.actionscript.stage.DefaultAutowiringStageProcessor"
        id="defaultAutowiringStageProcessor" />

To use this, you need only declare a variable on a component that you add to the stage, like so:

import mx.rpc.remoting.RemoteObject;
// ...
Autowired(name = "auctionService")]
public var auctionService:RemoteObject ;

Remember, the main .mxml file is not a component. Creating components is not very difficult but is out of the scope of this book. A component lets you create functionality (and the view for that functionality, if you want), and reuse it elsewhere. An example of this is the buttons that you use in the framework. You use XML and a button (whose behavior is completely hidden from you) shows up on the user interface, ready to use.

This initial setup should precede any work you do in ActionScript, because it saves a lot of time for almost no cost. Let's now turn to actually configuring objects worth injecting!

In our chat application, we created an mx.messaging.Consumer and an mx.messaging.Producer. To create those objects, we need a valid mx.messaging.ChannelSet. Let's configure that first. Add the following to your application context:

<object id="channelSet" class="mx.messaging.ChannelSet">
        <property name="channels" >
            <array>
                <object class="mx.messaging.channels.AMFChannel">
                   <constructor-arg value="my-amf"/>
                   <constructor-arg value="http://localhost:8080/mb/amf"/>
                </object>
            </array>
        </property>
    </object>

This example is small but demonstrates a lot. We demonstrate how to create an array in the Spring ActionScript XML, how to do constructor configuration, and even how an object is configured. Creating the Producer and Consumer are similarly easy, dispatched with three lines of XML each:

<object id="consumer" class="mx.messaging.Consumer" scope="prototype">
        <property name="channelSet" ref="channelSet"/>
        <property name="destination" value="chatDestination"/>
    </object>

    <object id="producer" class="mx.messaging.Producer" scope="prototype">
        <property name="channelSet" ref="channelSet"/>
        <property name="destination" value="chatDestination"/>
    </object>

So far, this should all read very naturally. In fact, for the most part, you could swap out object and replace it with bean to get a valid Java Spring XML context!

All that's left now is to use them. We'll revisit the chat application and obtain references to the Producer and Consumer from the application context when the application's completely loaded.

Using beans from Spring is slightly different with Spring ActionScript because of the way the Flex compiler works. The obvious first thing to do is replace the old initialization logic with acquisition logic after the FlexXMLApplicationContext has been realized in the setup method that we've wired to react to the applicationComplete event.

chatConsumer = _applicationContext.getObject('consumer') as Consumer;
      chatConsumer.addEventListener(MessageEvent.MESSAGE,
        function (msgEvent:MessageEvent):void {
                var msg:AsyncMessage = msgEvent.message as AsyncMessage;
                  output.text += msg.body + "
";
      });

       chatConsumer.subscribe();
       chatProducer = _applicationContext.getObject('producer') as Producer;
       chatProducer.connect();

This should be all that we need to do to realize an equivalent program to the one we started with, but it isn't. The problem is that the Flex compiler prunes classes that aren't used to minimize the size of the resultant compiled binary. This includes classes in the base framework library. Normally, this would be laudable. However, because we're constructing a lot of our objects using Spring, it has no way of knowing what classes are being used. Since we aren't declaring any variables anywhere of type AMFChannel, for example, it won't include that class—even though we're constructing an instance of it using Spring! There are many solutions, but the simplest is an explicit reference to any class you want to retain in the compiled code. Our habit is to simply create anonymous blocks. You can put them in different modules, or at higher places like the main class. Here's how ours looks:

<mx:Script>
    <![CDATA[
     import mx.messaging.channels.AMFChannel; // imports
     {  // anonymous block
        AMFChannel  // class reference
        // , OtherClass, OtherClass
     }
    ]]>
    </mx:Script>

Now, you have something that replaces the earlier code and keeps you from needlessly reintroducing the same logic as before. Run the Flex client as before in different browsers and confirm that a message produced in one session is reflected instantly in another window.

Summary

In this chapter, you've learned about Flash, and the Flex architecture. You've learned about how the Flash/Flex/AIR trifecta should fit into your architecture. You've learned about the features, and limitations, that may change the way your application works when talking to other services. We explored different ways to interoperate from Flex and then finally looked at setting up BlazeDS, an open source middleware project from Adobe. You learned how BlazeDS can help overcome inherent limitations (or, features) in the Flash platform and how to use the Spring BlazeDS integration to simplify deployment of the BlazeDS middleware with your application. We explored the push and pill types of services models that a client/server application might use and how we could take advantage of those using BlazeDS and Flex; this included how to expose Spring services to Flex clients in a synchronous and asynchronous fashion using messaging-based, JMS-based, and Spring Integration–based services. Finally, you learned about using the Spring ActionScript container to bring the same discipline and elegance to ActionScript programming that Spring Java brings to Java developers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset