Showing posts with label ActionScript. Show all posts
Showing posts with label ActionScript. Show all posts

Thursday, January 22, 2009

Flex HTTPService: When POST is GET

A relatively infrequently encountered nuance of Flex 3's MXML/ActionScript's HTTPService that one may run into occurs when the HTTPService.method public property is set to "POST", but the server actually receives a GET HTTP method instead of the anticipated POST. As documented in Http Service Get vs Post Issue? IS this a bug? and HttpService Post Shows as Get on Server Logs, this occurs when the HTTPService with the method set to "POST" has its send method invoked without any parameters.

In the unlikely event that it is important to have the POST request treated as POST on the server despite having no parameters to pass, one can use a dummy parameter to cause this to happen. The remainder of this blog posting will focus on code examples of this.

The HTTPService can be declared as an MXML tag as shown next.


<mx:HTTPService id="httpService"
useProxy="false"
resultFormat="object"
url="http://localhost:8080/httpServer"
fault="faultHandler(event)"
result="resultHandler(event)" />


The HTTPService instance shown above can have its method set to HTTP POST and be invoked as shown in the next ActionScript snippet.


httpServiceProxied.method = "POST";
httpServiceProxied.send();


Although the method is clearly set to POST, if no parameters are passed, it will actually be treated like a GET instead of a POST. A dummy object can be added to force the POST to be treated like a POST as shown in the next ActionScript code snippet.


const dummyObject:Object = { "key" : "value" };
httpServiceProxied.method = "POST";
httpServiceProxied.send(dummyObject);


Using the dummy object as shown above will force the POST to be treated as a POST. Note that parameters for an HTTPService invocation can be specified not only in the send() method, but can also be specified in the HTTPService declaration with the <mx:request> element.

While it at first seems a little strange that a POST is treated as a GET if no parameter is supplied, it does not seem nearly as strange when one considers that an HTTP POST is designed to serve as a resource-changing method and a parameter (enclosed entity) will typically be associated with such functionality. Conversely, an HTTP GET is a safe and idempotent method designed for retrieval of data and is probably the more usual HTTP method of the two to be called when no parameter is specified.

Finally, it is worth noting here that when the proxied HTTPService is used (such as with BlazeDS), POST is used when the method is set to POST regardless of whether a parameter is included or not.

Thursday, January 1, 2009

Bare Bones BlazeDS Object Remoting

In this blog posting, I will demonstrate using BlazeDS for object remoting between a Flex client and a Java EE-based server. There are several good blog postings and articles on BlazeDS remoting out there already (see Additional Resources section below), but most of them either don't provide a complete example or apply FlexBuilder for some of the "magic." In this post, I will not use FlexBuilder and any IDE or text editor can be used. This allows one to see all the underlying details directly.


Tools Used in these Examples

For the examples in this blog posting, I will be using the following tools:

  • BlazeDS - I am using BlazeDS 3.2 and I am using the binary distribution (not Turnkey that includes Tomcat) to be more "bare bones."

  • Flex 3 SDK - I am using Flex 3.2. For convenience, I have set an environment variable FLEX_HOME to point to the main directory of my Flex 3 installation (where I unzipped it) and I have included %FLEX_HOME%\bin (or $FLEX_HOME/bin) on my PATH.

  • Java SE SDK - I am using Java SE 6 Update 10, but a newer or older version of Java SE should be fine as long as you are using at least J2SE 5.

  • Java EE web implementation - The BlazeDS Turnkey includes Tomcat, but I am using GlassFish for my examples. In theory, it should not matter which Java EE-compliant web server you use.

  • Ant (optional) - I provide a build.xml Ant build file in this posting that will build the Flex clients and will build the BlazeDS/JEE-powered server. However, these operations can be done individually as well. While I build a WAR file with my Ant build for the server-side deployment, one could simply edit directories and files in one's favorite server's deployment directory for the same effect. Similarly, one could build the Flex clients individually on the command line with the mxmlc command rather than using the build.xml file to do this. I am using Ant 1.7, but I am not aware of any features I am using specific to that version. Finally, I have my ANT_HOME environment variable set to my installation of Ant and have %ANT_HOME%/bin in my path for convenience in building.

  • Text Editor or IDE - You could use FlexBuilder, but do not need to for the examples in this posting. Other choices include NetBeans, Eclipse, JDeveloper, IDEA, JEdit, SpketIDE, vim, emacs, and even Notepad/Wordpad. If you do choose to use an IDE that supports XML, you can make things a little easier for yourself by associating MXML with XML as I demonstrate with NetBeans here and with JEdit here. Also, there are plug-ins for Flex for IDEs such as FlexBean for NetBeans. Also, it is my understanding that IDEA has built-in Flex support.



The following screen snapshot demonstrates the versions of these tools used as described above.




Setting Up BlazeDS for the Application

I won't go into any greater detail regarding installation of the Flex SDK, of the Java SDK, of a Java EE-compliant web server, of Ant, or of an IDE or text editor than that provided above. However, I will briefly look a little closer at installation of BlazeDS.

BlazeDS is open source and its source can be downloaded here, but the source code for BlazeDS is not needed for any of the examples in this blog posting.

The BlazeDS download page includes release builds and nightly builds. I am using a release build for the examples here. BlazeDS 3.2 Release Build 3978 is available as of this writing and I am using BlazeDS 3.2 Release Build 3978 for these examples.

An advantage of using the Turnkey download is that Tomcat web server is included with it. However, I intentionally want the examples in this posting to be "bare bones" and so will use a separate JEE implementation. With the non-Turnkey binary BlazeDS release downloaded (it will be called something like blazeds-bin-3.2.0.3978.zip), "installation" simply involves unzipping the ZIP file and extracting the WAR file (blazeds.war) from it. The next screen snapshot shows the contents of this binary BlazeDS download, including the WAR file just mentioned.



With access to the blazeds.war file, we now need to expand the blazeds.war's contents. We have two choices here. One popular choice is to expand this WAR file's contents in a web server's deployment directory. Many web servers will then automatically deploy the contents of that expanded directory. However, I will be taking the second approach here and will rebuild the WAR file for deployment with an Ant script. This provides greater portability between web servers and allows me to show better what needs to be included in the server-side BlazeDS-powered WAR file via the build.xml file. You should be able to use the direct deployment of expanded directory approach and not need Ant for the server-side if you prefer that approach.

The next screen snapshot shows the contents of the blazeds.war file that needs to be expanded.



For my examples, I will be expanding the blazeds.war file's contents into the C:\blazeDSRemotingExample) directory. I typically remove the META-INF directory and its contents from the expanded directory, so that it looks something like that shown in the next two screen snapshots. Note that the red lines through directories in the first screen snapshot are intentional because the entries that they cross out will eventually be built up, but will not be there when blazeds.war is initially expanded (in other words, your newly expanded directory should only have the WEB-INF directory if you deleted the META-INF directory as I did).





As the immediately preceding image shows, the expanded blazeds.war contents include some expected subdirectories beneath WEB-INF including classes and lib. Because we will rebuild our server's WAR file with this same expanded directory structure, we already have the necessary directories we need for the web application's classpath.


Tailoring BlazeDS Web Descriptor Configuration for Custom Application

Up until now, we've merely unzipped the downloaded binary BlazeDS file and then expanded the blazeds.war file into a directory. Now it is time to begin tweaking the configuration files included with the blazeds.war file to accommodate our application.

One of the first things I like to do when customizing my own BlazeDS-based application based on the provided blazeds.war file is to upgrade the web application descriptor file (web.xml) to reflect Servlet 2.5 rather than Servlet 2.3. I provide more detail on this process in my blog entry on OpenLaszlo with Java Servlets 2.5 (OpenLaszlo also provides an older web.xml file). The most important changes to note are that the DTD-described file becomes W3C XML Schema described instead, the version attribute is obviously updated from 2.3 to 2.5, and the <display-name> element is removed as a nested element under the <servlet> element. This particular change of versions of the web.xml file is probably not absolutely necessary in this case because I'm not using Java EE 5 annotations, but I make the change every time anyway.

The less important changes that I make to the provided web.xml file include removal of commented out section specifically intended for WebSphere (which I'm not using here) and other comments. The other change I make is to change the text for the high-level <display-name> and <description> elements. Indeed, when I have completed the transformation, the new web.xml file still looks very similar to the one provided with the blazeds.war file. Several of these changes are depicted in the next screen snapshot taken of a diff run against the two versions of web.xml using the ExamDiff tool.



The new and customized web.xml file is shown next.

New and Customized web.xml File

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
<display-name>Dustin's BlazeDS Hello World Example</display-name>
<description>An extremely simple BlazeDS Remoting Application</description>

<!-- Http Flex Session attribute and binding listener support -->
<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>

<!-- MessageBroker Servlet -->
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>MessageBrokerServlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>

</web-app>



Customizing Other BlazeDS Configuration Files

Besides the classes and lib subdirectories contained under WEB-INF in the directory expanded from the blazeds.war file, we also have subdirectories called src and flex. We will not customize a couple XML files inside the flex directory to tailor BlazeDS for our application's use.

The first file we'll look at is called services-config.xml. This file is central to all BlazeDS-based applications whether they are based on RPC or messaging. In fact, among other things, this file usually imports additional configuration files for things that are specific to proxying, to remoting, and to messaging.

I use the same approach with services-config.xml that I used with web.xml: I adapt the version provided by blazeds.war to work for the custom application. However, there is a twist in this one case. I want to have a slightly different version of this file for my Flex client than I have for my WAR that gets deployed on the server. Keeping the DRY principle in mind, my approach here is to create a services-config-template.xml file and use Ant's text substitution functionality to automatically generate the services-config.xml file to be used by both the Flex client and by the WAR that is deployed to the server. The next code listing shows the template file, which I placed in a newly created directory creatively called "templates".

templates/services-config-template.xml

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

<services>
<service-include file-path="remoting-config.xml" />
<service-include file-path="proxy-config.xml" />
<service-include file-path="messaging-config.xml" />
</services>

<security>
<login-command class="flex.messaging.security.TomcatLoginCommand" server="Tomcat"/>
</security>

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://@host@:@port@/{context.root}/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
<channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">
<endpoint url="https://@host@:@port@/{context.root}/messagebroker/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://@host@:@port@/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>true</polling-enabled>
<polling-interval-seconds>4</polling-interval-seconds>
</properties>
</channel-definition>
</channels>

<logging>
<target class="flex.messaging.log.ConsoleTarget" level="Error">
<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>


The code for the template version of services-config-template.xml shown immediately above includes Ant token markers (@host@ and @port@) that will be replaced by Ant with an actual host name and a port number for the client's version of services-config.xml). These same tokens will be replaced with BlazeDS-recognized tokens {server.name} and {server.port} for the server-side version of services-config.xml. Note that I was able to leave the BlazeDS template {context.root} in the template for the client-side version of services-config.xml because the mxmlc compiler allows me to specify its value for the client as an option.

I left the reference in services-config.xml to all three previously referenced files (remoting-config.xml, proxy-config.xml, and messaging-config.xml), but really only need the reference to remoting-config.xml for this blog post examples. As with the web.xml file, my approach to creation of that referenced remoting-config.xml file is to simply adapt the one provided with the expanded blazeds.war file. The only necessary change is to specify the Java classes that will be used on the server side. We haven't looked at this Java class yet, but the remoting-config.xml file shown in the next code listing tips us off that it will be a class named HelloWorldRemotingServer in a package named dustin.

remoting-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
class="flex.messaging.services.RemotingService">

<adapters>
<adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true"/>
</adapters>

<default-channels>
<channel ref="my-amf"/>
</default-channels>

<destination id="HelloWorld">
<properties>
<source>dustin.HelloWorldRemotingServer</source>
</properties>
</destination>
</service>



The above remoting-config.xml file specifies that the destination associated with this Java class on the server side can be referenced as "HelloWorld" (the 'id' attribute of the destination element).

Whew! That wraps up the BlazeDS configuration modifications. If you have not looked into this before, it looks worse than it really is. Most of the final content shown in the code listings above was already provided by the blazeds.war that we expanded. I only made some relatively small modifications to the web.xml and remoting-config.xml files. The services-config.xml file likewise only required some minor changes, but the more significant issue there was that I needed to prepare a different version of this file for the client than for the server. The server version of services-config.xml can resolve the BlazeDS tokens {host.name} and {host.port} based on the web server settings, but I specify a hard-coded host name and port for the client version of services-config.xml.


The Java Class

The contents of the remoting-config.xml file provided us with a sneak peek at some details about the Java class on the server. The definition of this simple Java class is shown next. Note that this file is placed in a subdirectory called dustin (because it is in the "dustin" Java package) under the src subdirectory under WEB-INF in our expanded directory.

HelloWorldRemotingServer.java (1st Version)

package dustin;

/**
* Simple "Hello World" example meant for use in BlazeDS remoting example.
* This is the server-side POJO that will be exposed to a Flex client via
* BlazeDS remoting.
*
* @author Dustin
*/
public class HelloWorldRemotingServer
{
/** No-arguments constructor. */
public HelloWorldRemotingServer() {}

/**
* Accept a String representing a name and return that name as part of a
* Hello message.
*
* @param nameToSayHelloTo Name to which Hello will be addressed.
* @return Hello address to provided name.
*/
public String processHello(final String nameToSayHelloTo)
{
return "Hello, " + nameToSayHelloTo + "!";
}
}


The important thing to note from the code listing for the Java class immediately above is that it is a regular Java class with no knowledge of Flex or ActionScript.


The Flex Client (Hello World Client)

The Java class we just looked at returns a provided String surrounded with a Hello World prefix and exclamation point. We'll now look at the Flex MXML code that can be used to invoke this Java object and this processHello(String) method via BlazeDS.

HelloWorldRemotingClient.mxml

<?xml version="1.0" encoding="UTF-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="1100" height="700">

<!-- Associate client with BlazeDS destination via RemoteObject. -->
<mx:RemoteObject id="remoteObject" destination="HelloWorld" />

<mx:Panel id="mainPanel" title="Simple BlazeDS Remoting Example - Hello World">
<mx:Form>
<mx:FormItem label="Provide Your Name">
<mx:TextInput id="textInput"
change="remoteObject.processHello(textInput.text)"/>
</mx:FormItem>
<mx:FormItem label="Server's Response">
<mx:Label text="{remoteObject.processHello.lastResult}"/>
</mx:FormItem>
</mx:Form>
</mx:Panel>
</mx:Application>


The MXML code above is very simple. The RemoteObject element (with an 'id' of "remoteObject") specifies a destination (via the 'destination' attribute) of "HelloWorld" (defined above in the remoting-config.xml file).

If you plan to use the build.xml file provided in this blog posting, this MXML file should be placed at parallel to the WEB-INF directory (as opposed to in the WEB-INF directory like the Java class and configuration files previously discussed).


Building it All

Even though I still need to show another Flex client, an ActionScript class, and another Java class whose instantiation is bound to the ActionScript class's instantiation, I am including the entire Ant build.xml file here for convenience. Some of the targets defined in this file will not work until the remaining classes discussed later in this blog posting are included.

build.xml (Ant)

<?xml version="1.0" encoding="UTF-8"?>
<project name="HelloWorldBlazeDSRemotingExample" default="all" basedir=".">
<description>Builds, tests, and runs the Dustin's Hello World BlazeDS Remoting Example.</description>

<!-- NOTE TO USERS: At least three properties set below may need to be
set for this build file to work with different deployment
environments. The three minimum properties to examine
are client.host, client.port, and app.dir.
-->

<!-- GENERAL PROPERTIES -->
<!--
*********************************************************************
Change server's host name and port (determined by web server
deployment of server side of code).
*********************************************************************
-->
<property name="client.host" value="localhost" />
<property name="client.port" value="8080" />
<!--
*********************************************************************
Change definition of app.dir if necessary (to where blazeds.war
has been uncompressed).
*********************************************************************
-->
<property name="app.dir" value="C:\\blazeDSRemotingExample" />
<property name="web.inf.dir" value="${app.dir}/WEB-INF" />
<property name="templates.dir" value="${app.dir}/templates" />
<property name="context.name" value="HelloWorldRemotingServer" />


<!-- GENERAL JAVA PORTION PROPERTIES -->
<property name="src.dir" value="${web.inf.dir}/src" />
<property name="classes.dir" value="${web.inf.dir}/classes" />
<property name="lib.dir" value="lib" />
<property name="dist.dir" value="${app.dir}/dist" />
<property name="javadoc.dir" value="${dist.dir}/javadoc" />
<property name="serverside.war" value="${context.name}.war" />

<!-- Nothing special needed for classpath! -->
<path id="classpath" />


<!-- BLAZEDS RELATED PROPERTIES -->
<property name="services.config.server.name"
value="services-config.xml" />
<property name="services.config.client.name"
value="services-config-client.xml" />
<property name="services.config.template"
value="services-config-template.xml" />


<!-- GENERAL FLEX PORTION PROPERTIES (FLEX CLIENT) -->
<property name="app.flex.dir" value="${web.inf.dir}/flex" />
<property name="flex.debug" value="true" />
<property name="flex.warnings" value="true" />
<property name="flex.context.root" value="${context.name}" />
<property name="flex.services"
value="${app.flex.dir}/${services.config.client.name}" />
<property name="flex.source.path" value="${web.inf.dir}" />
<property name="flex.publisher" value="Dustin" />
<property name="flex.title"
value="'Hello World BlazeDS Remoting Example'" />
<property name="flex.description"
value="'Simple client for BlazeDS Remoting example'" />
<property name="flex.verbose.stacktraces" value="true" />
<property name="flex.actionscript.warnings" value="true" />
<property name="flex.app.helloworld.name.root"
value="HelloWorldRemotingClient" />
<property name="flex.app.helloworld.name.source"
value="${flex.app.helloworld.name.root}.mxml" />
<property name="flex.app.helloworld.name.swf"
value="${flex.app.helloworld.name.root}.swf" />
<property name="flex.app.complexobject.name.root"
value="ComplexObjectRemotingClient" />
<property name="flex.app.complexobject.name.source"
value="${flex.app.complexobject.name.root}.mxml" />
<property name="flex.app.complexobject.name.swf"
value="${flex.app.complexobject.name.root}.swf" />



<target name="-init">
<mkdir dir="${dist.dir}" />
<mkdir dir="${javadoc.dir}" />
</target>

<target name="-copy-respective-services-config-files"
depends="-clean-generated-blazeds-config-files">
<copy file="${templates.dir}/${services.config.template}"
tofile="${app.flex.dir}/${services.config.server.name}" >
<filterchain>
<replacetokens>
<token key="host" value="{server.name}"/>
<token key="port" value="{server.port}"/>
</replacetokens>
</filterchain>
</copy>
<copy file="${templates.dir}/${services.config.template}"
tofile="${app.flex.dir}/${services.config.client.name}" >
<filterchain>
<replacetokens>
<token key="host" value="${client.host}"/>
<token key="port" value="${client.port}"/>
</replacetokens>
</filterchain>
</copy>
</target>

<target name="compile"
depends="compileJavaBasedServer, compileFlexBasedClients"
description="Compile Java and Flex code." />

<target name="compileJavaBasedServer"
description="Compile Java source code used for server side.">
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
classpathref="classpath" />
</target>

<target name="compileFlexBasedClients"
description="Compile the Flex clients"
depends="compileFlexBasedHelloWorldClient,
compileFlexBasedComplexObjectClient" />

<target name="compileFlexBasedHelloWorldClient"
description="Compile Flex Hello World client application into SWF file.">
<exec executable="mxmlc">
<arg line="-debug=${flex.debug}" />
<arg line="-context-root=${flex.context.root}" />
<arg line="-services=${flex.services}" />
<arg line="-warnings=${flex.warnings}" />
<arg line="-publisher=${flex.publisher}" />
<arg line="-title=${flex.title}" />
<arg line="-description=${flex.description}" />
<arg line="-verbose-stacktraces=${flex.verbose.stacktraces}" />
<arg line="-show-actionscript-warnings=${flex.actionscript.warnings}" />
<arg line="-output ${dist.dir}/${flex.app.helloworld.name.swf}" />
<arg line="-- ${flex.app.helloworld.name.source}" />
</exec>
</target>

<target name="compileFlexBasedComplexObjectClient"
description="Compile Flex Complex Object client application into SWF file.">
<exec executable="mxmlc">
<arg line="-debug=${flex.debug}" />
<arg line="-context-root=${flex.context.root}" />
<arg line="-services=${flex.services}" />
<arg line="-warnings=${flex.warnings}" />
<arg line="-publisher=${flex.publisher}" />
<arg line="-title=${flex.title}" />
<arg line="-description=${flex.description}" />
<arg line="-verbose-stacktraces=${flex.verbose.stacktraces}" />
<arg line="-show-actionscript-warnings=${flex.actionscript.warnings}" />
<arg line="-output ${dist.dir}/${flex.app.complexobject.name.swf}" />
<arg line="-- ${flex.app.complexobject.name.source}" />
</exec>
</target>

<target name="war"
description="Build server's web application archive (WAR)."
depends="-init,
-copy-respective-services-config-files,
compileJavaBasedServer">
<war webxml="${web.inf.dir}/web.xml"
destfile="${dist.dir}/${serverside.war}"
filesonly="true">
<zipfileset dir="${web.inf.dir}"
excludes="web.xml
flex/${services.config.client.name}"
prefix="WEB-INF" />
</war>
</target>

<target name="all" depends="war, compileFlexBasedClients"
description="Build it all; the default target." />

<target name="clean" description="Remove generated files."
depends="-clean-generated-blazeds-config-files">
<delete dir="${dist.dir}" />
<delete dir="${classes.dir}/dustin" />
</target>

<target name="-clean-generated-blazeds-config-files">
<delete file="${app.flex.dir}/${services.config.server.name}"
quiet="true" />
<delete file="${app.flex.dir}/${services.config.client.name}"
quiet="true"/>
</target>

<target name="javadoc" description="Generate Javadoc-based documentation">
<javadoc doctitle="Dustin's Spring BlazeDS Integration Example"
destdir="${javadoc.dir}"
sourcepath="${src.dir}"
classpathref="classpath"
private="true"
author="Dustin" />
</target>

</project>


In the above build.xml file, there are three properties defined near the top that would most likely need to be changed for a different deployment environment. They are client.host, client.port, and app.dir. The first two are used to appropriately build the client's version of services-config.xml to reference the host and port on which the server BlazeDS-powered application will be running. Because I am using GlassFish defaults for my server, I define these properties respectively to localhost and 8080. The third property, app.dir defines the directory into which I expanded the contents of the blazeds.war file for editing.

Targets of specific interest in the above build.xml file include the compileFlexBasedHelloWorldClient target for building the Flex client with the mxmlc compiler and the war target for building the WAR file to be deployed on the server.

When I build the WAR and the client SWF file (same name as its source MXML file but with an .swf extension rather than the .mxml extension), I can then deploy the WAR and run the SWF client. The deployment of the WAR file is done with whatever mechanism your web serve provides. The SWF client can easily be executed by clicking on it or typing its full name on the command line. The following two screen snapshots show the client running by first showing how it initially appears and then showing how it appears once a name has been entered into the name field.






Binding Java to ActionScript

The above example demonstrates the ability to remotely call Java on the server from MXML on the client via the RemoteObject and BlazeDS. However, the example up to this point has only used simple mappings (in this case Strings). It is more interesting to now move onto an example of binding a more complex objects. The good news is that it is all still pretty easy.

For this slightly more complex example, we will expand the HelloWorldRemotingServer class to feature a method that accepts a domain object that is neither a primitive, a boxed version of a primitive, or a String. The revised version of HelloWorldRemotingServer is shown next:

HelloWorldRemotingServer.java (Finished Version)

package dustin;

/**
* Simple "Hello World" example meant for use in BlazeDS remoting example.
* This is the server-side POJO that will be exposed to a Flex client via
* BlazeDS remoting.
*
* @author Dustin
*/
public class HelloWorldRemotingServer
{
/** No-arguments constructor. */
public HelloWorldRemotingServer() {}

/**
* Accept a String representing a name and return that name as part of a
* Hello message.
*
* @param nameToSayHelloTo Name to which Hello will be addressed.
* @return Hello address to provided name.
*/
public String processHello(final String nameToSayHelloTo)
{
return "Hello, " + nameToSayHelloTo + "!";
}

/**
* Accept a last name and a first name and return the concatendated name as
* part of a James Bond like introduction.
*
* @param firstName Provided first name of person.
* @param lastName Provided last name of person.
* @return The James Bond like introductory message.
*/
public String processName(final String firstName, final String lastName)
{
return "Your name is " + lastName + ". " + firstName + " " + lastName + ".";
}

/**
* Accept a Person and return that person's name as part of a James Bond like
* introduction.
*
* @param person Person whose name will be returned.
* @return The James Bond like introductory message.
*/
public String processName(final Person person)
{
return "Your name is " + person.getLastName() + ". "
+ person.getFirstName() + " " + person.getLastName() + "!";
}
}


One of the newly added methods is called processName(String,String). This wouldn't make a very exciting example because it still uses two separate Strings. The more interesting example will be binding a Flex client to the processName(Person) method that expects a Person instance. For our newly updated class to compile, we need to define a Person class in the same package. That is shown next:

Person.java

package dustin;

/**
* Encapsulates a person's information.
*
* @author Dustin
*/
public class Person
{
/** Last name of person. */
private String lastName;

/** First name of person. */
private String firstName;

/** No-arguments constructor. */
public Person() {}

/**
* Constructor accepting names for this person.
*
* @param newLastName Last name of the person.
* @param newFirstName First name of the person.
*/
public Person(final String newLastName,
final String newFirstName,
final int newAge)
{
this.lastName = newLastName;
this.firstName = newFirstName;
}

/**
* Provide person's first name.
*
* @return Person's first name.
*/
public String getFirstName()
{
return this.firstName;
}

/**
* Set/change the person's first name.
*
* @param newFirstName New first name for the person.
*/
public void setFirstName(final String newFirstName)
{
this.firstName = newFirstName;
}

/**
* Provide person's last name.
*
* @return Person's last name.
*/
public String getLastName()
{
return this.lastName;
}

/**
* Set/change person's last name.
*
* @param newLastName New last name for person.
*/
public void setLastName(final String newLastName)
{
this.lastName = newLastName;
}

/**
* String representation of me.
*
* @return My String representation.
*/
@Override
public String toString()
{
return this.firstName + " " + this.lastName;
}
}


There is nothing remarkable about the Person class and there is no sign of any association with ActionScript or Flex in this Java class. In fact, the Person class is downright boring.

For Strings, primitives, and object/boxed versions of primitives, BlazeDS can typically map these types to appropriate ActionScript types. Now that we've introduced the Person type, however, we need to instruct BlazeDS how to use this Java class on the Flex-based client side. This is easily done by simply creating a parallel Person class in ActionScript that explicitly associates itself with the Java version of Person. The code for the ActionScript version of Person is shown next.

Person.as

package dustin
{
[Bindable]
[RemoteClass(alias="dustin.Person")]
public class Person
{
/** First name of person. */
private var _firstName:String;

/** Last name of person. */
private var _lastName:String;

/**
* Constructor accepting names of this newly instantiated person.
* Because each parameter has a default of null, this can be used as a
* no-arguments constructor.
*
* @param newFirstName First name to be used for this person.
* @param newLastName Last name to be used for this person.
*/
public function Person(
newFirstName:String = null,
newLastName:String = null)
{
_firstName = newFirstName;
_lastName = newLastName;
}

/**
* WRITE (set/change) firstName property.
*
* @param newFirstName New first name.
*/
public function set firstName(newFirstName:String):void
{
_firstName = newFirstName;
}

/**
* READ firstName property.
*
* @return My first name.
*/
public function get firstName():String
{
return _firstName;
}

/**
* WRITE (set/change) lastName property.
*
* @param newLastName new last name.
*/
public function set lastName(newLastName:String):void
{
_lastName = newLastName;
}

/**
* READ lastName property.
*
* @return My last name.
*/
public function get lastName():String
{
return _lastName;
}
}
}


Arguably the most interesting point about the ActionScript version of Person is the use of RemoteClass metadata tag ([RemoteClass(alias="dustin.Person")]) to explicitly associate this ActionScript class with the Java class. By naming the property write/set and read/get methods to match those in the Java class, the remaining binding of ActionScript object to Java object is handled by BlazeDS.

The ActionScript Person.as file should be placed in a newly created subdirectory named dustin (for the "dustin" package) parallel to the WEB-INF directory. We now need to move onto building a new Flex client to use this new method on the server-side.

I want to make one final observation regarding the Person class in ActionScript. The code would have been even more concise if I had made the data members public. In that case, they could be accessed directly and the set and get methods would not be necessary. Many of the online examples take this approach.


A New Flex Client (Complex Object Client)

The code for ComplexObjectRemotingClient.mxml is shown next. It should be placed in the same location as our previous Flex client (in expanded directory and parallel to the WEB-INF subdirectory).

ComplexObjectRemotingClient.mxml

<?xml version="1.0" encoding="UTF-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="1100" height="700">

<mx:Script>
import dustin.Person;

/**
* Prepare provided name information and send to server.
*
* @param firstName First name of person.
* @param lastName Last name of person.
*/
public function processAndSubmitName(firstName:String, lastName:String):void
{
const person:Person = new Person(firstName, lastName);
remoteObject.processName(person);
}
</mx:Script>

<!-- Associate client with BlazeDS destination via RemoteObject. -->
<mx:RemoteObject id="remoteObject" destination="HelloWorld" />

<mx:Panel id="mainPanel" title="BlazeDS Remoting Example - Complex Object Mapping">
<mx:Form>
<mx:FormItem label="Your First Name">
<mx:TextInput id="firstNameInput" />
</mx:FormItem>
<mx:FormItem label="Your Last Name">
<mx:TextInput id="lastNameInput" />
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Submit Name!"
click="processAndSubmitName(firstNameInput.text, lastNameInput.text);" />
</mx:FormItem>
<mx:FormItem label="Server's Response">
<mx:Label text="{remoteObject.processName.lastResult}" />
</mx:FormItem>
</mx:Form>
</mx:Panel>
</mx:Application>


The new Flex client uses a little ActionScript to prepare the call to the server and instantiates one of our newly written Person objects in the process. The build.xml file shown previously already included the target for building this new client ("compileFlexBasedComplexObjectClient"). Note that the Java code also needs to be rebuilt and the WAR reassembled and redeployed to the web server because of the addition of methods to the server class.

The following two screen snapshots show the initial and final state of the new client when it is executed as the Hello World client was earlier.





As the above output shows, the method accepting a Person object (note the exclamation point in the returned message) has been contacted on the Java side from a remote Flex client.


Pictorial Overview of Directory/File Structure

One of the things that can be a little tricky about using BlazeDS or the mxmlc compiler if you have very little experience using them is the placement of files. Fortunately, most of this was already setup correctly by the blazeds.war file that we expanded. However, I did add some directories and files. In an effort to make it more clear where these directories and files are in relation to one another, I include the next screen snapshot of my final directory structure. I tried in the text above to describe where within these various directories each file should go.



The image above shows where to place MXML source files, ActionScript source files, and Java source files for the build.xml file described above to work properly. I did not provide descriptions of the nbproject subdirectory (used for NetBeans specifically), the images subdirectory (used to store the screen snapshots shown in this blog posting), or the WEB-INF subdirectory (it was provided by the expanding of blazeds.war). The only thing to note here is that I added the Java source code files (but not ActionScript source code files) into the src directory under WEB-INF.


Conclusion

This has turned out to be a rather lengthy blog entry. However, this is to be expected considering that I've intentionally tried to make this a complete and thorough introduction to BlazeDS/Java remoting and because I have included the code directly within the blog posting rather than as a separate attachment. Also, I've tried to avoid use of anything specific to FlexBuilder so that anyone with any text editor should be able to use my examples.

Despite the length of this blog entry, use of the BlazeDS remoting is pretty straightforward once you have tried it a couple times. It does get more complex when security constraints and other details are added back in, but one can choose how much one implements at each step and the examples above are nice for confirming that client-to-server communication is working properly.


Additional Resources

Saturday, December 27, 2008

Proposed New Java Language Features in ActionScript

In a recent blog post, I postulated that many individuals who are most vehemently in support of specific new features for the Java programming language probably owe at least a portion of this opinion to their experience with that feature in a different programming language. In this blog posting, I am going to look at some of the proposed new features and syntax for the Java programming language that are currently available in ActionScript 3.

I will not be looking at proposed features for Java SE 7 that are really beyond the language syntax itself, such as JMX 2 because these are often not as comparable between languages as is the syntax and language features.


The Proposed Java Language Features

There are several sources for identifying and learning more about the various proposed new features for Java SE 7. Alex Miller's comprehensive Java SE 7 coverage is not only a good place to start, but is also a good place to find details about the proposed features. He provides streamed Java 7 news via RSS at http://java7.tumblr.com/.

Other good sites for finding out about proposed Java SE 7 new language features include the Java.net poll results for the poll question about the most-missed features to be excluded from Java SE 7, results of whiteboard polling at Devoxx, Danny Coward's weblog, and Mark Reinhold's Blog.

From these sources, we can glean that some of the proposed new language features for future versions of Java (some have already been punted from SE 7) include:

* Closures
* JavaBean Property Support (AKA "first-class properties)
* Reified Generics
* Operator Overloading
* BigDecimal Operator
* Beans Binding (JSR 295)
* Switch Statement Support of Strings
* Enum Comparison Support
* Multiple Exception Capture
* Multi-line String Literals


ActionScript 3 and Java

It turns out that ActionScript 3 is significantly different than its ActionScript 2 predecessor. Besides gaining general Java-like concepts such as support for class-based objects, single-root object hierarchy, and static typing (ActionScript 2 was dynamically typed only), ActionScript 3 syntax appears to be largely based on Java syntax as well. This similarity can be easily seen in the article Comparing the Syntax of Java 5 and ActionScript 3. However, ActionScript has roots in ECMAScript and so has some similarities to JavaScript as well. We'll see how that ECMAScript heritage plays a role in providing some language features in ActionScript that are not in Java.

I will now look at some of the proposed changes to the Java programming language and compare those proposals to the current state of ActionScript.


Multi-line String Literals

Java currently does not support a single literal string overlapping multiple lines. A lengthy literal String in Java must either require a very long line or must be constructed with String concatenation or being built up through append calls on the StringBuilder or StringBuffer. ActionScript's String class supports similar operators and concatenation methods as the Java String class. However, ActionScript 3 also features a seemingly little-known approach for multi-line literal Strings. This is succinctly described in the blog posting Multi-line Strings in ActionScript 3. Note that the blog author (Doug McCune) associates this ActionScript feature with other ECMAScript implementations. An MXML example of a multi-line String is also shown in that blog posting, but I'm focusing on ActionScript features here (and the MXML case is more obvious anyway considering that MXML is an XML grammar).


/** Lengthy String that overlaps multiple lines. */
private const lengthyString:String = (
<![CDATA[
Here is a very long string that is so long that it cannot easily
be fit onto the same line in the source code. This example
demonstrates the ability to have a single String
overlap multiple lines in the source code. Note how even the white
space is retained.
]]> ).toString();


Just as some Java developers would like to see multi-line literal String support in a future version of Java, some ActionScript developers would like to see full-fledged multi-line literal String support in a future version of ActionScript as well. This is one of those features that would occasionally be convenient (especially for preparing logging messages), but which is not as big of a deal for me as it used to be as I have gotten used to acquiring large strings from external sources such as XML or the database.

Sven Efftinge demonstrates a hack in Java for supporting multi-line String literals. It is also worth noting that both Scala (since 2.1.7) and Groovy support multi-line String literals with three double quotes (Groovy also supports three single quotes for multi-line Strings with no GString expansion) and this is what has been proposed for ECMAScript 4 before the Harmony Effect.


Multiple Exception Capture

There are many times when the same exception handling should be done for two types of captured exceptions from the same try block, but that common exception handling is not appropriate for catching with a single high-level exception. In such cases, it would be nice to use some type of OR syntax to say handle an exception in a particular way for this exception type or for this exception type. It could look something like this (or could be comma-separated or even something totally different, but the concept is the same):


try
{
// some exceptional circumstance occurs
}
catch (FirstTypeException firstEx | SecondTypeException secondEx)
{
// handle these two exceptions in the same way
}


Neither Java nor ActionScript currently supports the multicatch concept. For additional details on ActionScript error handling, see Flex Error Handling: A Simple Example and Using try...catch...finally Statements.


Enum Comparison Support

Like Java before J2SE 5, ActionScript does not have an enum. Therefore, ActionScript obviously cannot have any type of enum comparison support. There have been several different recommendations for faux enums in ActionScript such as using ActionScript static initializers and using a static constructor, but I would like to see ActionScript get an enum as powerful as Java's. This is an example of where Java has something that I really, really like and would like to have in another language (ActionScript). Using the ActionScript fake enums reminds me of employing similar tactics (read hacks) in Java before J2SE 5.


Switch on Strings

This is popular in the Java development community and I admit that there are some times (especially with legacy code) in which it would be really nice to have. I generally don't need it as much as I used to thanks to the powerful Java enum just described, but I do find cases where it would be useful now and then. ActionScript switch statements do support switching on Strings as discussed in my previous blog entry Java and ActionScript: switch-case and Switching on Strings.


Beans Binding

Just as I miss Java's powerful enum in ActionScript, I similarly deeply miss ActionScript's data (beans) binding in Java. In fact, this was the feature that I was most disappointed to hear would not be in Java SE 7. There are alternatives (including the reference implementation for JSR 295) for Java provided by third-party products, but I really was hoping to see it as a standard part of the language. Data binding in Flex/ActionScript is extremely powerful and easy to use. It may be the most addicting feature of Flex and I know others familiar with Java and Flex have missed it in Java as well.


BigDecimal Operator and Operator Overloading

I must admit to missing the ability to overload operators in Java. When I was primarily a C++ developer, I did not use this feature often, but it occasionally was extremely helpful. I will admit to a certain guilty pleasure from wielding its power and can see how such power in the wrong hands can be dangerous. While several languages other than C++ support operator overloading, ActionScript is not one of them.

ActionScript does not have a BigDecimal equivalent, so the obviously useful BigDecimal-specific operator support is not something that can be found in ActionScript.


Reified Generics

Like enums, generics are another feature with Java since J2SE 5 that is not available in ActionScript. I would really like to see runtime generics support added to Java.


Property Support

ActionScript does support properties as a fundamental part of the language. The primary advantage of ActionScript's implementation of properties is that consumers of these can then reference them directly on the class reference as if they are data members of the class rather than as methods of the class. For example, code calling a property could like like this


outputTextArea.text = propertiesExample.someString;


instead of the more Java-like


outputTextArea.text = propertiesExample.getSomeString();


This is implemented in the class being called like this:

/** Variable must be defined with different name than set and get. */
private var _someString:String = "ActionScript properties!";

/**
* "get" method for readable portion of someString property.
*
* @return My String contents.
*/
public function get someString():String
{
return _someString;
}


This is very similar to just having normal Java-like get and set methods except that the calling code can be a little more concise and treat the property like its directly acting on an attribute/property rather than on a method. It feels like it is calling a public variable, but it is actually calling methods. The advantage is that the data member is still encapsulated and logic can be done as part of the get or set method invocation.

For additional details on proposals for Java to have similar support for properties, see Java Properties and Events, Property Support in Java, the Java Way, and Java 7 - Update on Properties. As other languages have proven, there are multiple ways to implement properties. The real question in the Java community seems to be whether properties should be added at all.


Closures

In the recent Java.net poll cited earlier regarding the feature already dropped from Java SE 7 that was most missed, the closures feature was the dominant first choice (nearly half of all votes). However, that is not the end of the story. Indeed, the Devoxx white board survey on closures showed an even split (41 votes in favor and 41 votes against) on whether closures are wanted or not. It seems that closures are by far the favorite feature of those wanting them, but that a significant portion of the Java community does not want them at all.

ActionScript 3 does have and uses closures and their benefits and downsides are recognized among ActionScript developers. The "Method Closures" section of the ActionScript 3 Overview demonstrates the code readability improvement from using closures.


Conclusion

It seems almost inevitable that if a developer uses more than one programming language, he or she will find facets of each language that he or she wishes was in the other language. That is certainly true of me when I was using C++ and Java at the same time, when I have used Java and Ruby at the same time, when I have used Java and ActionScript at the same time, and so forth. This observation also strengthens the argument for learning multiple programming languages.

Monday, December 22, 2008

View SWF Contents with Nemo 440

There are times when it is helpful to see the contents of a generated SWF to better understand what is being included in it. There are several tools that can help with this. I'll briefly summarize some of them here and provide a little additional detail on my preferred tool: Nemo 440.

In Disassembling a SWF with swfdump, Gordon Smith outlines how to use the open source Flex SDK download and its swfdump executable tool based on the provided swfutils.jar. Note that the swfdump executable is also available with the OpenLaszlo distribution in the same directory (WEB-INF/bin) as other Flex-specific commands such as mxmlc and asdoc.

The flash-decompiler project, hosted on Google Code, is intended to be used to decompile Flash files. There are also other Flash decompilers, including commercial products. These include Flash Decompiler, Flash Decompiler Gold, and Sothink SWF Decompiler. Other tools for determining SWF content include swfextract, abcdump, and Flare.

My favorite product for seeing how my Flash/SWF files have been put together is the easy-to-use Nemo 440. This AIR-based application is very simple and therefore very easy to use. It is relatively small in size and so downloads and installs quickly, especially if you already have Adobe AIR installed on your machine. Once the .air file is downloaded, it can be installed by clicking on it or by typing its name on the command-line. There are only a few choices once the application is loaded, so it is really easy to figure out how to use Nemo 440.

Looking at the code as available in the SWF can be useful in understanding better how the Flex-to-Flash process works and can be useful for ensuring that contents are being included in the SWF as you expect.

Even if you don't have a need to look at the contents of your SWF file for your own uses, it may be worth your time to look at what Nemo 440 and other similar tools do show from your files. You may find that there are some things in those files that you really don't want so easily observed. See Analyzing Malicious Flash Programs for additional details on such security implications.

For times when you need to peek into an SWF you have compiled, Nemo 440 is an extremely useful tool that makes viewing of this content easy and efficient.

Saturday, November 29, 2008

ActionScript toString() with Interfaces

ActionScript 3.0 includes many object-oriented features that have an obvious Java-like syntax, but there are some nuances of ActionScript's object-oriented support that are not so Java-like. In this blog entry, I'll look in some detail at the use of ActionScript's toString() method in conjunction with ActionScript interfaces.

The value of the toString() in Java development and debugging is generally accepted and widely understood (see Item #10 in Joshua Bloch's Effective Java, Second Edition, or Item #9 in the First Edition). Similarly, use of toString() in ActionScript can provide similar advantages as discussed in Debugging Basics in Flex 3 (beta).

While an ActionScript toString() looks similar to a Java toString and while use of the toString() can be highly beneficial in working with both Java and ActionScript, there are some important differences to understand. I have previously covered some nuances of the ActionScript toString() implementation in Flex: ObjectUtil.toString() Versus Inherited Object's toString() and When An ActionScript Object Does Not Provide toString(). I'll briefly summarize some points from those two previous blog entries before demonstrating use of ActionScript's toString specifically in conjunction with an ActionScript interface.


Characteristics of the ActionScript Object.toString()

* ActionScript classes that directly extead the Object class (don't extend any other ActionScript class) and provide a toString() implementation do not need to use the override keyword.

* Related to the first point above, ActionScript's Object.toString() is not inherited by descendant ActionScript classes.

* The static utility class mx.utils.ObjectUtil provides a toString() that is highly useful for displaying String representations of ActionScript class instances that do not have their own implemented toString() methods).


The observations summarized above play out when using ActionScript interfaces and implementing those ActionScript interfaces. The remainder of this blog entry will be focused on the relationship of toString() to the ActionScript interface.

ActionScript makes use of the interface concept throughout its standard library. The blog entry ActionScript 3 Class Interface Implementations lists the ActionScript classes that implement interfaces in the Flex 3 API, the AIR 1.0 API, and the Flash API.

The ActionScript interface syntax is pretty straightforward for anyone familiar with the Java interface. The next code listing demonstrates a simple interface in ActionScript and uses the commonly adopted Flex coding convention of having an interface's name begin with "I".

IState.as


package examples
{
/**
* Simple interface example demonstrating use of interfaces in Flex 3.
*
* Methods defined in a Flex interface cannot have any modifiers.
*/
public interface IState
{
/**
* Provide name of the state.
*
* @return Name of the state.
*/
function getStateName():String;

/**
* Provide the state's abbreviation.
*
* @return Abbreviation of the state.
*/
function getStateAbbreviation():String;

/**
* Provide the capital of the state.
*
* @return Capital of the state.
*/
function getStateCapital():String;
}
}



Most Java developers should have very little trouble reading this interface. Like a Java interface, the "public" modifier is not required for the methods defined as part of the interface. In fact, these method modifiers are not even allowed in the ActionScript interface. The interface keyword is the same as in Java and the concept of method signatures without implementations is the same for both languages' interfaces.

ActionScript uses the same implements keyword that Java uses to designate when a class implements an interface. This is demonstrated in the next code listing, for the State class which implements the IState interface.


State.as


package examples
{
/**
* Implementation of IState interface that is mainly intended to demonstrate
* use of interfaces in Flex.
*/
public class State implements IState
{
/** Name of the state. */
private var stateName:String;

/** State's abbreviation. */
private var stateAbbreviation:String;

/** Capital of the state. */
private var stateCapital:String;

/**
* Constructor.
*
* @param newStateName Name of the state.
* @param newStateAbbreviation State two-digit abbreviation.
* @param newStateCapital Capital of the state.
*/
public function State(
newStateName:String,
newStateAbbreviation:String,
newStateCapital:String)
{
this.stateName = newStateName;
this.stateAbbreviation = newStateAbbreviation;
this.stateCapital = newStateCapital;
}

/**
* Provide name of the state.
*
* @return Name of the state.
*/
public function getStateName():String
{
return this.stateName;
}

/**
* Provide the state's abbreviation.
*
* @return Abbreviation of the state.
*/
public function getStateAbbreviation():String
{
return this.stateAbbreviation;
}

/**
* Provide the capital of the state.
*
* @return Capital of the state.
*/
public function getStateCapital():String
{
return this.stateCapital;
}
}
}



To demonstrate behavior of ActionScript interfaces in conjunction with the toString() method, I will use a simple Flex application to invoke an object's toString() implementation directly and to also expose the object's contents via the ObjectUtil.toString() method. The simple Flex application is shown next.


Main.mxml (without use of interface)


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="900" height="900"
applicationComplete="runExample();">
<mx:Script>
<![CDATA[
import mx.utils.ObjectUtil;
import examples.IState;
import examples.State;

/** Set up a state for the interface example. */
public function setUpState():State
{
const state:State = new State("Colorado", "CO", "Denver");
return state;
}

/**
* Display state information.
*
* @param state State whose information is to be displayed.
*/
public function displayState(state:State):void
{
stateNameText.text = state.getStateName();
stateAbbreviationText.text = state.getStateAbbreviation();
stateCapitalText.text = state.getStateCapital();

stateToStringText.text = state.toString();
stateObjectUtilToStringText.text = ObjectUtil.toString(state);
}

/**
* Set descriptive information on this example type. This is a
* convenience function related to demonstration of principles in
* a blog entry, but is not significant to the functionlity of this
* class.
*/
private function setExampleType():void
{
exampleTypeText.text = "No toString() method implemented in class.";
}

/** Run the example of using Flex interfaces. */
public function runExample():void
{
setExampleType();
displayState( setUpState() );
}
]]>
</mx:Script>

<mx:Panel id="mainPanel" title="State Example: Using Flex Interfaces">
<mx:Form id="stateForm">
<mx:FormItem id="stateNameItem" label="State Name">
<mx:Text id="stateNameText" />
</mx:FormItem>
<mx:FormItem id="stateNameAbbreviation" label="State Abbreviation">
<mx:Text id="stateAbbreviationText" />
</mx:FormItem>
<mx:FormItem id="stateCapital" label="State Capital">
<mx:Text id="stateCapitalText" />
</mx:FormItem>
<mx:FormItem id="exampleType" label="Example Description">
<mx:TextArea id="exampleTypeText" height="75" />
</mx:FormItem>
<mx:FormItem id="objectToString" label="toString()">
<mx:TextArea id="stateToStringText" />
</mx:FormItem>
<mx:FormItem id="objectUtilToString" label="ObjectUtil.toString()">
<mx:TextArea id="stateObjectUtilToStringText" />
</mx:FormItem>
</mx:Form>
</mx:Panel>
</mx:Application>



The above MXML file displays the results of calling a toString() directly on the provided object (instance of State) as well as the results of calling ObjectUtil.toString() on the provided object (instance of State). The IState interface is not used in the code above.

The mxmlc complier does not allow this code to compile, but instead reports the error message "Error: Call to a possibly undefined method toString through a reference with static type examples:State." and specifically cites the following line of code: "stateToStringText.text = state.toString();" This is shown in the following screen snapshot:



This error message makes it fairly obvious that a toString() implementation needs to be added to the State class. This also is evidence of the point made earlier about a class extending Object not being able to explicitly access toString() without an implementation of it. In other words, the State class that extends Object without an explicit toString() implementation cannot have toString() called explicitly. This is definitely different than in Java where the toString() would call Java's root Object's toString() in this situation.

We'll add a toString() implementation to the State class and try it again. The method looks like this:


toString() added to State.as


/**
* Provide String representation of me.
*
* @return String representation of me.
*/
public function toString():String
{
return "The capital of " + this.stateName + " ("
+ this.stateAbbreviation + ") is " + this.stateCapital;
}



With this toString() implementation in place, the Flex application Main.mxml (which has been modified only to change the text returned from the setExampleType() method) compiles. When this compiled SWF is executed, it appears as shown in the next screen snapshot.




Based on the above, we see the need to implement the toString() on the class directly rather than relying on it to get that method from its Object parent class. Now it is time to look at what happens when we try to call toString on the interface this State class implements. Note that the interface does not define a toString() method. In Java, this does not matter and the appropriate toString() gets called on an object even if its interface doesn't specifically call out a toString(). To try this out the Main.mxml application is changed to use IState interface rather than the State class. The slightly modified Main.mxml is shown next.


Main.mxml (using interface)


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="900" height="900"
applicationComplete="runExample();">
<mx:Script>
<![CDATA[
import mx.utils.ObjectUtil;
import examples.IState;
import examples.State;

/** Set up a state for the interface example. */
public function setUpState():IState
{
const state:IState = new State("Colorado", "CO", "Denver");
return state;
}

/**
* Display state information.
*
* @param state State whose information is to be displayed.
*/
public function displayState(state:IState):void
{
stateNameText.text = state.getStateName();
stateAbbreviationText.text = state.getStateAbbreviation();
stateCapitalText.text = state.getStateCapital();

stateToStringText.text = state.toString();
stateObjectUtilToStringText.text = ObjectUtil.toString(state);
}

/**
* Set descriptive information on this example type. This is a
* convenience function related to demonstration of principles in
* a blog entry, but is not significant to the functionlity of this
* class.
*/
private function setExampleType():void
{
exampleTypeText.text =
"toString() method implemented in class but not interface; "
+ "call attempted via the interface.";
}

/** Run the example of using Flex interfaces. */
public function runExample():void
{
setExampleType();
displayState( setUpState() );
}
]]>
</mx:Script>

<mx:Panel id="mainPanel" title="State Example: Using Flex Interfaces">
<mx:Form id="stateForm">
<mx:FormItem id="stateNameItem" label="State Name">
<mx:Text id="stateNameText" />
</mx:FormItem>
<mx:FormItem id="stateNameAbbreviation" label="State Abbreviation">
<mx:Text id="stateAbbreviationText" />
</mx:FormItem>
<mx:FormItem id="stateCapital" label="State Capital">
<mx:Text id="stateCapitalText" />
</mx:FormItem>
<mx:FormItem id="exampleType" label="Example Description">
<mx:TextArea id="exampleTypeText" height="75" />
</mx:FormItem>
<mx:FormItem id="objectToString" label="toString()">
<mx:TextArea id="stateToStringText" />
</mx:FormItem>
<mx:FormItem id="objectUtilToString" label="ObjectUtil.toString()">
<mx:TextArea id="stateObjectUtilToStringText" />
</mx:FormItem>
</mx:Form>
</mx:Panel>
</mx:Application>



The mxmlc compiler will not allow this to compile for the same reason that it did not allow compilation before toString() was added to the State class. The reason for this is that the IState interface does not have toString() defined in it.




The following can be added to the IState interface:

toString() Definition to Add to IState Interface


/**
* Provide String representation of me.
*
* @return String representation of me.
*/
function toString():String;


With this toString() defined in this interface, the code compiles. The resultant SWF renders as shown in the next screen snapshot.




What can be gleaned from this is that the interface must spell out toString() as an available method if any class implementing that interface is expected to provide its String representation via the interface. Rather than adding a toString() method to every interface one might ever implement in ActionScript, a slightly cleaner approach is to define a base interface with toString() and have all interfaces extend it. For example, one could define the IBase.as as shown in the next code listing.


IBase.as


package examples
{
/**
* Base interface providing a toString definition for all child interfaces
* to use.
*/
public interface IBase
{
/**
* Provide String representation of me.
*
* @return String representation of me.
*/
function toString():String;
}
}



With IBase.as defined above, the IState.as code becomes slightly simpler because it no longer needs to define toString(). The new and improved IState that extends IBase is shown next:


package examples
{
/**
* Simple interface example demonstrating use of interfaces in Flex 3.
*
* Methods defined in a Flex interface cannot have any modifiers.
*/
public interface IState extends IBase
{
/**
* Provide name of the state.
*
* @return Name of the state.
*/
function getStateName():String;

/**
* Provide the state's abbreviation.
*
* @return Abbreviation of the state.
*/
function getStateAbbreviation():String;

/**
* Provide the capital of the state.
*
* @return Capital of the state.
*/
function getStateCapital():String;
}
}


As with Java, ActionScript classes can implement multiple interfaces, so this approach will often work very well, even if other interfaces need to be implemented.


Other Possibilities

Another possible approach for getting to a toString() implementation on a class is to cast the interface to the specific class that implements that interface. In this example, this would mean casting the IState interface to State and then calling toString() on that State instance.

The casting approach is demonstrated by the following modified version of the Main.mxml's displayState() method.

Main.mxml displayState() Method Using Casting


/**
* Display state information.
*
* @param stateInterface State whose information is to be displayed.
*/
public function displayState(stateInterface:IState):void
{
const state:State = stateInterface as State;
stateNameText.text = state.getStateName();
stateAbbreviationText.text = state.getStateAbbreviation();
stateCapitalText.text = state.getStateCapital();

stateToStringText.text = state.toString();
stateObjectUtilToStringText.text = ObjectUtil.toString(state);
}


This example demonstrates ActionScript's alternative casting syntax (use of 'as'). Note that I could have used "const state:State = State(stateInterface);" in place of "const state:State = stateInterface as State;".

Although casting obviously works, I am generally against casting because it is often a red flag of bad design. Perhaps even more significant is the possibility of the cast throwing a runtime error or returning a null. It is uglier and more dangerous to cast than to simply provide the toString() definition in the interface.

Another alternative would be to make the properties of State public. If this was done, then the ObjectUtil.toString() would print these public attributes of that class as shown in the next screen shapshot.



I don't like this approach because it requires the properties to all be public, which defeats some of the purpose of class-based encapsulation. So, I still prefer the approach of using a super interface with a toString() method defined.

A nice side effect of having toString() specified in an interface is that implementing classes must implement it. Perhaps the biggest drawback I've seen with Java toString() is that it is often not implemented by developers. Although Java does give Object's toString() representation in such cases, that is often not very useful.

Conclusion

The use of toString on an ActionScript class accessed by its interface is somewhat different from the same situation in Java. However, it is easy to address this issue by providing a basic super interface that provides for toString() and having all business interfaces extend that super interface. The side benefit of this is that classes which implement the super interface or any of its extended interfaces will be forced to implement a toString() method.