Requirements

Prerequisite knowledge
Prior experience working with Flex Builder to create applications is useful, but not required. Some knowledge of working with BlazeDS is also helpful.
Be sure to download the sample files—and if you haven't already, check out Part 1 of this series before following the instructions provided for this last section.
 
User level
Intermediate
Required products
Flash Builder 3 (Download trial)
BlazeDS (Download trial)
Sample files
todolist3.zip (8 KB)

 
Additional Requirements

 
Spring
In Part 1 and Part 2 of this series, I described the process of setting up Spring, Hibernate, and MySQL on a sample to-do list server to create the back-end of the sample application. I also covered the steps to create a small Flex UI on the client side. In this article, we're going to bring all of the pieces together by writing the final user interface for Flex and connect it with the Spring back-end using BlazeDS.
 

 
Creating a module to share configuration files

The goal of this section is to avoid duplicating configuration files. First, we need to create a new module under todolist, our sample application. The new module will contain the two configuration files. Later on, the module can be packaged in a resource zip that will be included in both the web and desktop modules.
 
Open a command line in todolist and run the following command:
 
mvn archetype:create -DgroupId=org.epseelon.samples -DartifactId=todolist-config
Then navigate to todolist-config and delete the src/main/java and src/test directories. After that, create an src/main/resources directory and create a services-config.xml document inside it that contains the following code:
 
<?xml version="1.0" encoding="UTF-8"?> <services-config> <services> <service-include file-path="remoting-config.xml" /> </services> <!-- Spring factory registration --> <factories> <factory id="spring" class="org.epseelon.samples.todolist.controller.SpringFactory" /> </factories> <channels> <channel-definition id="channel-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint" /> <properties> <polling-enabled>false</polling-enabled> </properties> </channel-definition> </channels> <logging> <target class="flex.messaging.log.ConsoleTarget" level="Error"> <properties> <prefix>[BlazeDS]</prefix> <includeDate>true</includeDate> <includeTime>false</includeTime> <includeLevel>true</includeLevel> <includeCategory>true</includeCategory> </properties> <filters> <pattern>Endpoint.*</pattern> <pattern>Service.*</pattern> <pattern>Message.*</pattern> <pattern>DataService.*</pattern> <pattern>Configuration</pattern> </filters> </target> </logging> <system> <redeploy> <enabled>true</enabled> <watch-interval>20</watch-interval> <watch-file> {context.root}/WEB-INF/flex/services-config.xml </watch-file> <watch-file> {context.root}/WEB-INF/flex/remoting-config.xml </watch-file> <touch-file>{context.root}/WEB-INF/web.xml</touch-file> </redeploy> </system> </services-config>
While still in the same directory, create a file named remoting-config.xml that contains the following code:
 
<?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="channel-amf" /> </default-channels> <destination id="todoService"> <properties> <factory>spring</factory> <source>todoService</source> </properties> </destination> </service>
Edit the todolist-config/pom.xml file so that it looks like this:
 
<?xml version="1.0"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <parent> <artifactId>todolist</artifactId> <groupId>org.epseelon.samples</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>org.epseelon.samples</groupId> <artifactId>todolist-config</artifactId> <name>todolist-config</name> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <id>make shared resources</id> <goals> <goal>single</goal> </goals> <phase>package</phase> <configuration> <descriptors> <descriptor>src/main/assembly/resources.xml</descriptor> </descriptors> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Notice the pom packaging and configuration of the maven assembly plug-in that references the assembly descriptor to package the shared configuration. Create the src/main/assembly/resources.xml file, as follows:
 
<assembly> <id>resources</id> <formats> <format>zip</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <fileSets> <fileSet> <directory>src/main/resources</directory> <outputDirectory></outputDirectory> </fileSet> </fileSets> </assembly>
Run 'mvn install' and you'll see a file called todolist-config-1.0-SNAPSHOT-resources.zip, containing the two configuration files. This is exactly what we wanted, because now we can include these configuration files in the various modules of the to do list sample application.
 
In the next part of this article, I'll describe the process required to allow BlazeDS to consume our to do list service.
 

 
Exposing the Spring service via BlazeDS

In the previous section we set up the configuration files in their own module, in order to make it easy to distribute (and not duplicate) them later. In this section, we'll take a look at how to expose the to do list service via BlazeDS so that our Flex client can consume it.
 
First, we need to add the BlazeDS libraries to the web module dependencies. This part involves editing the todolist-web/pom.xml file to add the following dependencies:
 
<dependency> <groupId>com.adobe.blazeds</groupId> <artifactId>blazeds-common</artifactId> <version>3.0.0.544</version> </dependency> <dependency> <groupId>com.adobe.blazeds</groupId> <artifactId>blazeds-core</artifactId> <version>3.0.0.544</version> </dependency> <dependency> <groupId>com.adobe.blazeds</groupId> <artifactId>blazeds-remoting</artifactId> <version>3.0.0.544</version> </dependency> <dependency> <groupId>backport-util-concurrent</groupId> <artifactId>backport-util-concurrent</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency>
BlazeDS libraries (blazeds-core, blazeds-common and blazeds-remoting) are currently only available from my personal repository—until they are published to a public repository. The code above adds the BlazeDS libraries to the project, and now we'll add a special Java class to the project. This Java class acts as a bridge between BlazeDS and Spring by retrieving Spring bean instances for the BlazeDS FlexFactory interface. The entire org.epseelon.samples.todolist.controller.SpringFactory class is presented below (courtesy of Jeff Vroom):
 
package org.epseelon.samples.todolist.controller; import flex.messaging.FactoryInstance; import flex.messaging.FlexFactory; import flex.messaging.config.ConfigMap; import flex.messaging.services.ServiceException; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; public class SpringFactory implements FlexFactory { private static final String SOURCE = "source"; public void initialize(String id, ConfigMap configMap) { } public FactoryInstance createFactoryInstance(String id, ConfigMap properties) { SpringFactoryInstance instance = new SpringFactoryInstance(this, id, properties); instance.setSource(properties.getPropertyAsString(SOURCE, instance.getId())); return instance; } // end method createFactoryInstance() public Object lookup(FactoryInstance inst) { SpringFactoryInstance factoryInstance = (SpringFactoryInstance) inst; return factoryInstance.lookup(); } static class SpringFactoryInstance extends FactoryInstance { SpringFactoryInstance(SpringFactory factory, String id, ConfigMap properties) { super(factory, id, properties); } public String toString() { return "SpringFactory instance for id=" + getId() + " source=" + getSource() + " scope=" + getScope(); } public Object lookup() { ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(flex.messaging.FlexContext.getServletConfig().getServletContext()); String beanName = getSource(); try { return appContext.getBean(beanName); } catch (NoSuchBeanDefinitionException nexc) { ServiceException e = new ServiceException(); String msg = "Spring service named '" + beanName + "' does not exist."; e.setMessage(msg); e.setRootCause(nexc); e.setDetails(msg); e.setCode("Server.Processing"); throw e; } catch (BeansException bexc) { ServiceException e = new ServiceException(); String msg = "Unable to create Spring service named '" + beanName + "' "; e.setMessage(msg); e.setRootCause(bexc); e.setDetails(msg); e.setCode("Server.Processing"); throw e; } } } }
The next step involves configuring the servlet that is responsible for handling remoting requests coming from the Flex UI. This servlet is responsible for calling the Spring service when needed. Add the following configuration to todolist-web/src/main/webapp/WEB-INF/web.xml, just after listener configuration:
 
<context-param> <param-name>flex.class.path</param-name> <param-value>/WEB-INF/flex/hotfixes</param-value> </context-param> <!-- 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> <init-param> <param-name>flex.write.path</param-name> <param-value>/WEB-INF/flex</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>
As you can see, this servlet needs some configuration files to work. More specifically, it requires the files that are inside the shared configuration module that we created in the first section of this article.
 
In order to import these files, we need to include our shared configuration module as a dependency of the web module. To do this, add the following dependency to todolist-web/pom.xml:
 
<dependency> <groupId>${project.groupId}</groupId> <artifactId>todolist-config</artifactId> <version>1.0-SNAPSHOT</version> <classifier>resources</classifier> <type>zip</type> <scope>provided</scope> </dependency>
Then, in order to unpack those configuration files and embed them in your web application, you'll need to add the following plug-in configuration to the build section in todolist-web/pom.xml:
 
<plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack-config</id> <goals> <goal>unpack-dependencies</goal> </goals> <phase>generate-resources</phase> <configuration> <outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/flex</outputDirectory> <includeArtifacIds>todolist-config</includeArtifacIds> <includeGroupIds>${project.groupId}</includeGroupIds> <includeClassifiers>resources</includeClassifiers> <excludeTransitive>true</excludeTransitive> <excludeTypes>jar,swf</excludeTypes> </configuration> </execution> </executions> </plugin>
Now, review the code in todolist-config/src/main/resources/services-config.xml. The most interesting part is the destination configuration. Be sure to check out the section where the todoService destination is defined that uses the BlazeDS SpringFactory for handling. The key is that the source parameter corresponds to the bean ID of the service.
 
There is one more thing left to do in order to allow Maven to build todolist-web independently from the other modules. The Maven plug-in flex-mojos defines a resource-bundle packaging that maven-war-plugin is not aware of. To solve that issue, add the following plug-in configuration:
 
<plugin> <groupId>info.rvin.mojo</groupId> <artifactId>flex-compiler-mojo</artifactId> <extensions>true</extensions> </plugin>
Finally, run 'mvn install' to check that everything is working. Double-check that both configuration files are present in WEB-INF/flex.
 
At this point the to do service is exposed for remoting via BlazeDS. That's a big piece of the back end puzzle. In the next section, I'll describe the process for setting up the client-side connection.
 

 
Writing the Flex UI

In this section I'll describe how to create the front end of the application using Flex. To begin, delete the todolist-ria/src/main/flex/Main.xml file. Then, create todolist-ria/src/main/flex/index.mxml with the following content:
 
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="/2006/mxml" layout="vertical" verticalAlign="middle" horizontalAlign="center" xmlns:screen="org.epseelon.samples.todolist.view.screen.*"> <screen:TodoForm/> </mx:Application>
The main screen of the user interface is a TodoForm class. Create this new file in todolist-ria/src/main/flex/org/epseelon/samples/todolist/view/screen/TodoForm.mxml, using the following code:
 
<?xml version="1.0" encoding="utf-8"?> <mx:TitleWindow xmlns:mx="/2006/mxml" layout="vertical" width="488" height="384" creationComplete="getList()"> <mx:Form width="100%" height="100%" defaultButton="{saveButton}"> <mx:FormHeading label="Todo List" width="100%"/> <mx:FormItem label="ID:" width="127"> <mx:TextInput width="100%" id="idText" text="{TodoItem(todoDatagrid.selectedItem).id}" editable="false" enabled="false"/> </mx:FormItem> <mx:FormItem label="Title:" width="345"> <mx:TextInput width="100%" id="titleText" text="{TodoItem(todoDatagrid.selectedItem).title}"/> </mx:FormItem> <mx:DataGrid id="todoDatagrid" width="100%" height="100%" dataProvider="{todoItems}"> <mx:columns> <mx:DataGridColumn headerText="ID" dataField="id" width="30"/> <mx:DataGridColumn headerText="Title" dataField="title"/> </mx:columns> </mx:DataGrid> </mx:Form> <mx:ControlBar horizontalAlign="center"> <mx:Button label="New" click="setDefault()"/> <mx:Button label="Save" id="saveButton" click="save()" textAlign="center"/> <mx:Button label="Delete" click="remove()"/> </mx:ControlBar> <mx:RemoteObject id="todoService" showBusyCursor="true" fault="onFault(event)" destination="todoService"> <mx:method name="save" result="onResultSave(event)" fault="onFault(event)"/> <mx:method name="remove" result="onResultRemove(event)" fault="onFault(event)"/> <mx:method name="getList" result="onResultGetList(event)" fault="onFault(event)"/> </mx:RemoteObject> <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; import org.epseelon.samples.todolist.view.entity.TodoItem; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import mx.controls.Alert; [Bindable] private var todoItems:ArrayCollection; private var todoItem:TodoItem; public function save():void { this.todoItem = new TodoItem(); this.todoItem.id = new Number(idText.text); this.todoItem.title = titleText.text; todoService.save(todoItem); } public function onResultSave(event:ResultEvent):void { status = "Item was successfully saved with ID: " + TodoItem(event.result).id; getList(); } public function remove():void { if (todoDatagrid.selectedItem != null) { todoItem = todoDatagrid.selectedItem as TodoItem; todoService.remove(todoItem); } else { Alert.show("Select an item to delete"); } } public function onResultRemove(event:ResultEvent):void { status = "Deletion succeeded!"; getList(); } public function getList():void { todoService.getList(); } public function onResultGetList(event:ResultEvent):void { todoItems = event.result as ArrayCollection; } public function setDefault():void { idText.text = ""; titleText.text = ""; } public function onFault(event:FaultEvent):void { Alert.show(event.fault.message); } ]]> </mx:Script> </mx:TitleWindow>
The first part of this view is a simple form that contains two text fields. One text field contains the ID and the other one contains the title of the item being edited or created Then comes a DataGrid that holds a list of all the to do items.
 
The interface also includes a control bar with all of the buttons necessary to create, update and delete items in the to do list.
 
The third part is the remote object that is used to call methods on the server. As you can see, the remote object references the todoService destination and defines the three methods to save, delete and list items, along with their corresponding result and fault handlers.
 
Finally, we have the ActionScript code that implements methods to call the server, result and fault handlers. As you can see, all of these methods use the org.epseelon.samples.todolist.view.entity.TodoItem class, and it is really an exact ActionScript mapping for the org.epseelon.samples.todolist.domain.TodoItem class on the server-side of the application. Here's the code:
 
package org.epseelon.samples.todolist.view.entity { [RemoteClass(alias="org.epseelon.samples.todolist.domain.TodoItem")] [Bindable] public class TodoItem { public var id:Number; public var title:String; } }
BlazeDS is extremely helpful here, because it will automatically serialize into and deserialize from this class for us. This means that we only have to manipulate this version in our client, which simplifies the entire process.
 
By the way, note that writing those Data Transfer Objects manually can become tedious. Fortunately for us, Velo, flex-mojos' developer, is preparer a generator mojo using GAS3 generator to generate those ActionScript DTO's automatically. I didn't integrate them here because it's not quite ready for prime time yet, but we're working on it.
 
In the upcoming section I'll cover the steps to compile the Flex UI and add some additional parameters that we'll need to hook everything all together.
 

 
Compiling the Flex UI

The goal of this part of the development process is to compile the Flex interface for the client side. Since our Flex UI defines a remote object that references our todoService destination, we'll need to compile index.mxml with a few additional parameters.
 
First, the compiler needs to know where it can find the services-config.xml file where the todoService destination is defined. It's important to note that you could add a <services/> element to the flex-compiler-mojo configuration section. But in this situation, we are going to use the default setting, which means the plug-in will look for a file named services-config.xml in the module resources. In order to avoid duplicating those files, we'll just add our shared configuration module as a dependency of todolist-ria, like this:
 
<dependency> <groupId>${project.groupId}</groupId> <artifactId>todolist-config</artifactId> <version>1.0-SNAPSHOT</version> <classifier>resources</classifier> <type>zip</type> <scope>provided</scope> </dependency>
Next, we need to unpack the files in resources. Add the following plug-in configuration to todolist-ria/pom.xml:
 
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack-config</id> <goals> <goal>unpack-dependencies</goal> </goals> <phase>generate-resources</phase> <configuration> <outputDirectory>${project.build.directory}/generated-resources</outputDirectory> <includeArtifacIds>todolist-config</includeArtifacIds> <includeGroupIds>${project.groupId}</includeGroupIds> <excludeTransitive>true</excludeTransitive> </configuration> </execution> </executions> </plugin>
We need to add target/generated-resources to the default resources by adding the following code in the build section:
 
<resources> <resource> <directory>${basedir}/src/main/resources</directory> </resource> <resource> <directory>${basedir}/target/generated-resources</directory> <filtering>true</filtering> </resource> </resources>
Last but not least, our remote object will send its AMF request messages to the same host and the same port where the SWF is running. That's the desired behavior, but we still need to specify the context root where the MessageBrokerServlet will receive those messages. This context root value will be used as a placeholder in the channel definition in services-config.xml. To specify a value for this parameter, all that's required is to modify the configuration for the flex-compiler-mojo in todolist-ria/pom.xml, like this:
 
<plugin> <groupId>info.rvin.mojo</groupId> <artifactId>flex-compiler-mojo</artifactId> <version>1.0-alpha7</version> <extensions>true</extensions> <configuration> <locales> <param>en_US</param> </locales> <contextRoot>todolist-web</contextRoot> </configuration> </plugin>
By the way, you may have noticed that the value for the context root corresponds to the build/finalName parameter that is defined in todolist-web/pom.xml.
 
Now you can run 'mvn install' on the whole project.
 
It's important to note that by default, flex-compiler-mojo will look for the one MXML file in src/main/flex as the main application. If there are multiple MXML files located in that directory, the plug-in will use any file called main.as or main.mxml as the main application. And because Velo (the creator of flex-mojos) really understands that Maven is all about "convention over configuration," you can also choose to specify your own path for this main file using the 'sourceFile' configuration parameter.
 
In the final section of this article series we'll bring all of the pieces of this sample application together by bundling the Flex UI into the web application.
 

 
Bundling the Flex UI into the web application

In this final section of the article series, we'll bring all the pieces of the puzzle together. At this point, we need to include the Flex UI as a dependency in the web module. To do so, add the following dependency to todolist-web/pom.xml:
 
<dependency> <groupId>${project.groupId}</groupId> <artifactId>todolist-ria</artifactId> <version>${project.version}</version> <type>swf</type> </dependency>
It's important that this dependency is copied over to the web archive, so we need to configure the maven-dependency-plugin to do so. Add this execution to the maven-dependency-plugin configuration in todolist-web/pom.xml:
 
<execution> <id>copy-swf</id> <phase>process-classes</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory> <includeTypes>swf</includeTypes> </configuration> </execution>
At this point there are only a few things we need to do. This next part will make it easier to access our application. Edit todolist-web/src/main/webapp/index.jsp and add the following content:
 
<html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <!-- BEGIN Browser History required section --> <link rel="stylesheet" type="text/css" href="history/history.css" /> <!-- END Browser History required section --> <title>Todo List</title> <script src="AC_OETags.js" language="javascript"></script> <!-- BEGIN Browser History required section --> <script src="history/history.js" language="javascript"></script> <!-- END Browser History required section --> <style> body { margin: 0px; overflow:hidden } </style> <script language="JavaScript" type="text/javascript"> <!-- // ----------------------------------------------------------------------------- // Globals // Major version of Flash required var requiredMajorVersion = 9; // Minor version of Flash required var requiredMinorVersion = 0; // Minor version of Flash required var requiredRevision = 28; // ----------------------------------------------------------------------------- // --> </script> </head> <body scroll="no"> <script language="JavaScript" type="text/javascript"> <!-- // Version check for the Flash Player that has the ability to start Player Product Install (6.0r65) var hasProductInstall = DetectFlashVer(6, 0, 65); // Version check based upon the values defined in globals var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision); if ( hasProductInstall && !hasRequestedVersion ) { // DO NOT MODIFY THE FOLLOWING FOUR LINES // Location visited after installation is complete if installation is required var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn"; var MMredirectURL = window.location; document.title = document.title.slice(0, 47) + " - Flash Player Installation"; var MMdoctitle = document.title; AC_FL_RunContent( "src", "playerProductInstall", "FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"", "width", "100%", "height", "100%", "align", "middle", "id", "index", "quality", "high", "bgcolor", "#869ca7", "name", "index", "allowScriptAccess","sameDomain", "type", "application/x-shockwave-flash", "pluginspage", "/go/getflashplayer" ); } else if (hasRequestedVersion) { // if we've detected an acceptable version // embed the Flash Content SWF when all tests are passed AC_FL_RunContent( "src", "todolist-ria-1.0-SNAPSHOT", "width", "100%", "height", "100%", "align", "middle", "id", "todolist-ria-1.0-SNAPSHOT", "quality", "high", "bgcolor", "#869ca7", "name", "todolist-ria-1.0-SNAPSHOT", "allowScriptAccess","sameDomain", "type", "application/x-shockwave-flash", "pluginspage", "/go/getflashplayer" ); } else { // flash is too old or we can't detect the plugin var alternateContent = 'Alternate HTML content should be placed here. ' + 'This content requires the Adobe Flash Player. ' + '<a href=/go/getflash/>Get Flash</a>'; document.write(alternateContent); // insert non-flash content } // --> </script> <noscript> <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="index" width="100%" height="100%" codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab"> <param name="movie" value="index.swf" /> <param name="quality" value="high" /> <param name="bgcolor" value="#869ca7" /> <param name="allowScriptAccess" value="sameDomain" /> <embed src="todolist-ria-1.0-SNAPSHOT.swf" quality="high" bgcolor="#869ca7" width="100%" height="100%" name="index" align="middle" play="true" loop="false" quality="high" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="/go/getflashplayer"> </embed> </object> </noscript> </body> </html>
The code above comes from the standard HTML template generated by Flex Builder. It uses JavaScript to embed the SWF object into a traditional web page. Next we need to add the dependent files for this page. The AC_OETags.js file is included in the downloadable sample files folder. Copy it to this location:
 
todolist-web/src/main/webapp/AC_OETags.js
Then copy playerProductInstall.swf (also available in the sample files) and paste it into the same directory.
 
Finally, copy the 'history' directory over to the same directory described above.
 
Now everything is ready. This is the exciting part. Run 'mvn install' for the last time, deploy todolist-web.war to JBoss or Tomcat, start your server and go to http://localhost:8080/todolist-web
 
The to do application should work as expected. Try it out and make up some action items.
 
If you'd like to compare your version of the sample application with mine, I've included the final working version of this project in the sample files folder. Enjoy!
 

 
Where to go from here

This project setup certainly requires a bit of work, but it uses clean code and the flex-mojos plug-ins works really well.
 
If you'd like to take this sample application a bit further, there are many things you could add, such as FlexUnit tests, ASDoc generation and so on. Try customizing the provided files to see how you can extend them to add new functionality.
 
I'd like to thank Velo for providing the excellent flex-mojos plug-in that makes this project possible, as well as offering the support I needed from him to write this article series.
 
To learn more about the concepts covered in these articles, see the following online resources:
 
And be sure to visit the Flex Developer Center to find the newest information, sample projects, and tutorials.