ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Flash Remoting for J2EE Developers

by Alon Salant
02/26/2003

Flash Remoting MX enables Flash MX clients running in a browser or on a user's desktop to access and invoke methods on server-side components running in a J2EE, Cold Fusion, or .NET application server. It is an essential piece of Macromedia's toolset for creating Rich Internet Applications. These applications are deployed over the Web and provide a desktop-software-like interface to server-side application features.

This article is targeted toward J2EE developers. It requires some familiarity with Macromedia Flash and its ActionScript scripting language. Flash developers building interfaces to J2EE applications will also benefit from the information provided here.

How Flash Remoting for J2EE Works

For those unfamiliar with Flash Remoting MX, the quickest way to get started is to download evaluation versions of Flash Remoting MX, Flash MX, and the Flash MX Remoting Components from Macromedia. If you download JRun 4, Flash Remoting is included. Macromedia has done a good job of providing simple examples and introductory documentation with Flash Remoting.

Flash Remoting for J2EE depends on features in the Flash MX player, a couple of ActionScript libraries for the Flash MX player, and a Java servlet that runs in the application server. Installing the Flash Remoting Components provides the ActionScript libraries and installing Flash Remoting for J2EE provides the servlet and supporting classes that run in your application server.

Related Article:

Consuming Amazon with Flash Remoting -- Using Flash Remoting and Web Services, you can enable a responsive UI that searches and displays Amazon results. Jason Michael Perry shows you how to build the interface and manipulate the XML Web service.

The Flash MX player handles serializing and deserializing ActionScript objects to and from a proprietary binary data format called Action Message Format (AMF). AMF serialized objects are the payload of HTTP requests and responses sent between the Flash MX client and the application server.

The client-side ActionScript libraries provide the ActionScript objects that a Flash developer uses to connect to and invoke methods on components in the application server. They also provide objects that are helpful for debugging the connection.

In a J2EE application server, Flash Remoting consists of a single servlet, flashgateway.controller.GatewayServlet, and supporting classes. The servlet and supporting classes are commonly referred to as the "gateway." The gateway uses introspection to locate and invoke methods on Java objects.

Related Reading

ActionScript for Flash MX: The Definitive Guide
By Colin Moock

A typical lifecycle of a Flash Remoting call is:

  1. The Flash client connects to the gateway.
  2. The Flash client names a server-side component on which it wants to invoke a method.
  3. The Flash client invokes a method on the server-side component, optionally providing arguments.
  4. The Flash player issues an HTTP request that identifies the component and method to invoke and contains the arguments serialized in AMF format.
  5. The gateway receives the request and deserializes the AMF objects into Java objects.
  6. The gateway uses introspection to locate and create the named object and invokes the identified method passing the deserialized arguments.
  7. The gateway serializes the method return value (or thrown exception) into the AMF format.
  8. The gateway constructs and sends an HTTP response to Flash, including the serialized result.
  9. Flash receives the response and deserializes the return value to an ActionScript object.
  10. Flash invokes a callback defined by the Flash developer to indicate that the method invocation has returned.

Macromedia provides a UML sequence diagram of this lifecycle.

Flash clients may invoke methods on regular Java objects, EJBs, and servlets. In the Cold Fusion and .NET implementations, a Flash client may also invoke methods on web services proxied by Flash Remoting. In J2EE implementations, a little work is required to enable web services communication.

How To Best Use Flash Remoting and Why

With the above description of Flash Remoting, we are ready to get into the details.

Install Flash Remoting in a Web Application

Because the Remoting gateway has to locate and invoke methods on classes in your application, a class loader that has access to your classes must load it. While Macromedia is somewhat vague on the issue, I strongly recommend installing Flash Remoting for your application by:

  1. Locating flashgateway.jar in the flashgateway.ear or flashgateway.war files installed by the Macromedia installer.
  2. Placing it in your web application's WEB-INF/lib/ directory.
  3. Mapping the gateway for your web application by adding the following to your web application's WEB-INF/web.xml file.
  <servlet>
    <servlet-name>FlashGatewayServlet</servlet-name>
    <display-name>Flash Remoting Servlet</display-name>
    <description>Servlet-based plugin to Flash Remoting</description>
    <servlet-class>flashgateway.controller.GatewayServlet</servlet-class>
    <load-on-startup>10</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>FlashGatewayServlet</servlet-name>
    <url-pattern>/gateway</url-pattern>
  </servlet-mapping>

This will make the Remoting Gateway Service available at http://www.yourdomain.com/yourwebapp/gateway. All classes in your application will be visible to Flash Remoting. Because Flash Remoting is installed as a servlet, it can be installed in any J2EE application server that supports servlets 1.2+, including but not limited to Caucho Resin, Jakarta Tomcat, BEA WebLogic, IBM WebSphere, Sun One AS, ATG Dynamo, and Oracle 9iAS.

Use a Service-Oriented Architecture

While you can directly access and invoke methods on servlets, JSPs, and EJBs with Flash Remoting, it does not mean that you should. It is important here to consider what Macromedia is doing today, what they are likely to do tomorrow, and where enterprise application development is going in general. In all of these areas, Service-Oriented Architectures feature heavily.

A Service-Oriented Architecture describes an application designed to expose a set of loosely-coupled business services that may be accessed by a range of clients to assemble application functionality. Clients may be J2EE or .NET applications or Flash clients. This architecture makes the applications providing the services flexible and scalable.

Enterprise application developers are rapidly adopting Service-Oriented Architectures, using web services to communicate between applications. The EJB 2.1 specification will require that all J2EE application servers provide the ability to expose Stateless Session Beans as web services. .NET already relies heavily on web services.

This all suggests that Macromedia released Flash Remoting as an intermediate step toward allowing Flash to communicate via web services. Developers should heed this trend toward Service-Oriented Architectures and use Flash Remoting to support a Service-Oriented Architecture that can easily be moved to web services in their own applications.

Create a Business Delegate on the Server

To use a Service-Oriented Architecture with Flash Remoting, simply create an object in your web application to expose services to Flash. In the terminology of Sun J2EE Blueprints, this object is a Business Delegate. It presents a Flash-friendly interface, exposed through Flash Remoting, to invoke methods on EJBs or any other object in the application server.

Using a Business Delegate addresses the fact that when Flash Remoting deserializes an AMF message into Java objects, it only knows a limited set of Java objects to use in the deserialization. Any developer-defined ActionScript object becomes a flashgateway.io.ASObject in Java. Methods invokable through Flash Remoting must use flashgateway.io.ASObject in their method signatures. An ASObject represents an ActionScript object in Java. It is basically a HashMap with case-insensitive keys and an extra field named type.

ActionScript code that invokes savePlayer() on com.server.Server (the Business Delegate) would look like:

#include "NetServices.as"

NetServices.setDefaultGatewayUrl("http://localhost/gateway");
var gatewayConnection = NetServices.createGatewayConnection();
var service = gatewayConnection.getService("com.server.Service", this);

Player = function() {}

var player = new Player();
player.name = "Joe";

service.savePlayer(player);

The corresponding Java Business Delegate class might be:

package com.server;

import flashgateway.io.ASObject;
  
public class Service {
   public void savePlayer(ASObject asPlayer) {
      Player player = new Player();
      player.setName((String) asPlayer.get("name"));
    
      Roster.savePlayer(player);
   }
}

Using a Business Delegate in this manner provides a single place where Flash-Remoting-specific code is centralized. The Business Delegate is completely stateless and performs no work directly, delegating requests to other objects. For small applications, a single Business Delegate can expose all needed services to Flash. In larger application, it makes sense to create different Business Delegates for different service categories.

I recommend not implementing java.io.Serializable in the Business Delegate. If an object accessed through Flash Remoting implements java.io.Serializable, the Flash gateway will save an instance of it in the user session to use for subsequent service invocations. Since the Business Delegate is stateless and the Flash client is available to maintain state, there is no reason to maintain an instance of the Business Delegate in each user's session.

Map ActionScript Objects to Java Objects

As discussed above, when sending ActionScript objects to the server through Flash Remoting service method calls, the Java service class will either receive one of the known Java object types or an ASObject, if the object type is not known. Converting these ASObjects to model objects in the application server can be tedious and brittle:

package com.server;

import flashgateway.io.ASObject;
  
public class Service {
   public void savePlayer(ASObject asPlayer) {
      Player player = new Player();
      player.setName((String) asPlayer.get("name"));
      player.setEmail((String) asPlayer.get("email"));
      player.setAge(((Integer) asPlayer.get("age")).intValue());
    
      Roster.savePlayer(player);
   }
}

At Carbon Five, we have created an open source library for addressing this issue. ASTranslator (ActionScript Translator) is hosted on SourceForge.net at carbonfive.sourceforge.net/astranslator/, is at its 1.0 release, and has been used in several production applications.

ASTranslator has two methods. ASTranslator.toActionScript() translates Java objects in your application to ASObjects to send to Flash. We'll discuss this method shortly. ASTranslator.fromActionScript() translates ASObjects sent to Remoting service methods from Flash to Java objects, as shown here:

import flashgateway.io.ASObject;
import com.carbonfive.flash.ASTranslator;
  
public class Service {
   public void savePlayer(ASObject asPlayer) {
      Player player = (Player) new ASTranslator().fromActionScript(asPlayer);
    
      Roster.savePlayer(player);
   }
}

Note that you must cast the result of this method call to the expected return type. fromActionScript() can take any object as an argument. It will translate any object and its composite objects as deeply as possible, maintaining references as it goes.

In order for ASTranslator to know what Java class an ASObject should become, you must register your ActionScript objects in Flash MX with the Java class name to which the ActionScript object maps.

// Object definition
Player = function() {}        

// Player in Flash maps to com.server.Player in Java                      
Object.registerClass("com.server.Player", Player);  

The value with which you register the class in ActionScript--com.server.Player, in this case--is set to the value of the ASObject type field by Flash Remoting when deserializing the object from AMF. ASTranslator uses this field to know what Java object to create from the ASObject.

Use ASTranslator to Return Objects to Flash

We created ASTranslator because of an issue we found with Flash Remoting MX for J2EE. When returning Java objects from service calls to Flash, Flash Remoting serializes Java objects according to their member variable state (private, protected, or public), ignoring the publicly-declared property accessors you'd expect it to use. If you have a Java class:

package com.server;

public class Player {
   private String _name;
  
   public getName() {
      return _name;
   }
}

and a service class:

package com.server;

public class Service {
   public Player getPlayer(String name) {
      Player player = Roster.getPlayer(name);
      return player;
   }
}

calling Service.getPlayer() from Flash MX would return an ActionScript object with a property _name instead of name. This removes one of the primary values of object-oriented programming: encapsulation. ActionScript programmers in Flash can see the internal state of Java objects on the server that even other Java objects running on the server cannot see. This is bad. It can be really bad (both for performance and security) if the internal state includes more information than the object was designed to expose through its public interface.

ASTranslator uses JavaBean-style introspection to serialize Java objects for remoting. To use ASTranslator in the above example, we would change the Business Delegate Service class as follows.

package com.server;

import flashgateway.io.ASObject;
import com.carbonfive.flash.ASTranslator;
  
public class Service {
   public ASObject getPlayer(String name) {
      Player player = Roster.getPlayer(name);
      ASObject aso = (ASObject) new ASTranslator().toActionScript(player);
      return aso;
   }
}

Flash will now receive an ActionScript object with the property name.

Additionally, ASTranslator sets the type field of the ASObject as the class name of the translated Java object. If the Flash developer has registered an ActionScript class with the class name of the Java object to which it maps, the object Flash receives will be of the right object type in ActionScript (a Player, in our example). If it has not been registered, the object Flash received will just be an ActionScript Object. Using ASTranslator in this way creates a tight binding between ActionScript objects in Flash and Java objects in the application server. We have found this to be a very effective way to use Flash Remoting.

Create a Service Proxy on the Client

Just as the Business Delegate defines a single entry point for communication coming from Flash through Flash Remoting, a Service Proxy implemented in Flash provides a single place where server-side services are exposed to the Flash application. To an ActionScript developer, the Service Proxy looks like the service provided by your application. The Service Proxy provides a central place to:

A Service Proxy for the example in the previous section may resemble:

#include "NetServices.as"

// define service proxy
function Service(client) {
   this.client = client;
  
   // set global status to call this
   _global.System.server = this;
   _global.System.onStatus = function(status) {
      this.server.onStatus(status);
   }
}

// initialize the service
Service.prototype.init = function(gatewayUrl) {
   NetServices.setDefaultGatewayUrl(gatewayUrl);
   var gatewayConnection = NetServices.createGatewayConnection();
   this.service = gatewayConnection.getService("com.server.Service", this.client);
}

// define status handler
Service.prototype.onStatus = function(status) {
   this.client.onStatus(status);
}

// save user service
Service.prototype.saveUser = function(user) {
   this.service.saveUser(user);
}

and the ActionScript code to use it would be:

var service = new Service(this);
service.init("http://localhost/gateway");

Player = function() {}

var player = new Player();
player.name = "Joe";

service.savePlayer(player);

For many development teams, the Service Proxy will be the place where J2EE developers and Flash developers get together to agree on the service API. We have found that creating a stubbed-out Service Proxy with methods that return the same results for each call is a great way to define the integration point between Flash and a J2EE application. Both teams can work knowing that the integration is already well-defined.

Throw Exceptions to the Client

A Flash developer handles the results of a remoting call (e.g., service.doCall()) by implementing a doCall_onResult(result) callback method for service calls that execute successfully and a doCall_onStatus(error) callback method for exceptions thrown by the Remoting gateway. The Remoting gateway will throw exceptions when it encounters problems locating, loading, or creating the service class and the method to invoke on it, or when the service class itself throws an exception.

Given this behavior and my recommendation to use a service-oriented architecture with Flash Remoting, I recommend having the service implementation throw exceptions to be handled by the Flash MX client. In the simplest case, I recommend having all service methods simply throw Exception.

package com.server;

import flashgateway.io.ASObject;
import com.carbonfive.flash.ASTranslator;

public class Service {
   public ASObject getPlayer(String name) throws Exception {
      ...
   }
}

This allows the Flash MX client to inspect all exceptions thrown during the service invocation and determine what to do about it at runtime. This strategy is discussed in the Flash Remoting documentation at livedocs.macromedia.com/frdocs/Using_Flash_Remoting_MX/UseActionScript8.jsp.

The type field of the error object returned to getPlayer_onStatus() should provide the class name of the thrown exception, allowing us to implement the callback in Flash as:

getPlayer_onStatus = function (error) {
   if (error.type == "com.server.PlayerNotFoundException") {
      trace("unable to find the player");
   } else {
      trace("an unknown error occurred");
   }
}

This approach is limited in that inheritance is not taken into account. A thrown exception that extends com.server.PlayerNotFoundException is an instance of a com.server.PlayerNotFoundException in Java, but would not be handled as one in the above ActionScript code. The type field of the error object would be the class name of the thrown exception.

A more significant limitation is that Flash Remoting MX for J2EE does not actually send exceptions thrown by a service class to the XXX_onStatus callback method. Because the Flash Remoting gateway uses introspection to invoke the service methods, all exceptions thrown by service calls are nested within a java.lang.reflect.InvocationTargetException. Tracing the properties of an exception thrown by a service call results in output that looks something like:

 code:     SERVER.PROCESSING
 level:    error
 description: Service threw an exception during method invocation: null
 type:     java.lang.reflect.InvocationTargetException
 rootcause: 
 details:  java.lang.reflect.InvocationTargetException
           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
           ...
           at flashgateway.Gateway.invoke(Gateway.java:184)
           ...
           at java.lang.Thread.run(Thread.java:536)
             Caused by: com.server.PlayerNotFoundException: 
             There is no player with name 'bob'
             at com.server.Roster.getPlayer(Roster.java:45)
           ...

We would like the nested com.server.PlayerNotFoundException to be used as exception upon which the error object is based, but it is not. This is a known issue in the implementation of Flash Remoting for J2EE. We have not come up with a great solution to this problem.

The best solution we have is to build a new error object in the Service Proxy that inspects the detail field of the error returned by Remoting to determine the correct values for description, type, and details before calling the onStatus method in the client. Unfortunately, the formatting of the stack trace as provided by the details field differs, depending on the JVM version being used on the server. Specifically, between Java 1.3 and Java 1.4, the way in which nested exceptions are printed in a stack trace is different. This makes inspecting the detail field brittle.

I feel strongly that having the XXX_onResult method handle successful Remoting calls and XXX_onStatus handle the failure cases is the best way to use Flash Remoting, but am not satisfied by with the presented workaround. I am interested in hearing other recommendations for dealing with this issue.

Think About Security

Because Flash Remoting uses introspection to locate, create, and invoke methods on any class in your application server, it is important to think about the security implications of using Flash Remoting in your application. Without any security restrictions, a Flash movie can invoke any method on any class that has a no-argument constructor or any object that can be found in JNDI. This is a serious security issue. Theoretically, a Flash movie could shut down the application server, if it knew the right object to access. At the very least, a Flash movie could create an ArrayList and fill it until the server runs out of memory.

There is very little information from Macromedia on this issue. Their recommended strategy for dealing with it is to modify the Java security policy file for your application server to grant or deny access to vulnerable packages in your server and application. Macromedia describes doing this for JRun 4.

Depending on your application server, you may need to edit the server's Java security policy file to grant or deny Flash Remoting access to specific classes. This Macromedia Designer and Developer Center article includes an example of editing the WebLogic security policy file to allow Flash Remoting to access classes in a specific package.

Implications

At Carbon Five, we have found that using Flash Remoting to create Flash interfaces to applications providing services through a Services-Oriented Architecture is a fun and clean way to work. It simplifies application development by removing tedious presentation-layer coding. It enables parallel development of the user experience and application functionality. It streamlines integration of work done by front-end and back-end development teams. Most importantly, it enables us to deliver powerful interfaces to the users of our enterprise Java applications.

As Web-based application development matures, we will see a proliferation of smart, stateful, feature-rich clients using Service-Oriented Applications to do the heavy hitting on the back end. Flash MX, Flash Remoting, and J2EE are powerful tools for creating these applications today.

Resources

Alon Salant is an owner and co-founder of Carbon Five and a co-author of Flash Remoting: The Definitive Guide.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.