Home > Rich Internet Applications > Flex, Spring and BlazeDS: the full stack! (Part 4) [updated]

Flex, Spring and BlazeDS: the full stack! (Part 4) [updated]

April 15th, 2008

Google Search Results

You arrived here after searching for the following phrase:

Click a phrase to jump to the first occurrence, or return to the search results.

[UPDATE] This article series has been reedited on the Adobe Developer Connection. For more information, see this post.

In the previous articles in this series, we did the boring stuff of setting up Spring, Hibernate and MySQL on a sample todo list server on one side, and we wrote a small useless Flex UI on the other side. In this article, we’re going to write the final UI and connect it with the Spring backend using BlazeDS. Let’s go!

Creating a module to share configuration files

[EDIT]
This section has been added to avoid duplicating configuration files.
[/EDIT]

The first thing we need to do is to create a new module under todolist. That module will contain our 2 configuration files and package them up in a resource zip that can be included later in both the web and ria modules.

So first go to todolist and run the following command:

mvn archetype:create -DgroupId=org.epseelon.samples -DartifactId=todolist-config

Then go to todolist-config and delete src/main/java and src/main/test directories. After that, create an src/main/resources directory and create services-config.xml in there with the following content:

<?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>

Next in the same directory, create a file named remoting-config.xml with the following content:

<?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>

Now you need to edit todolist-config/pom.xml 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 maven assembly plugin to package configuration according to the assembly descriptor in 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>

Now you can run ‘mvn install’ and you see a file called todolist-config-1.0-SNAPSHOT-resources.zip, with just our 2 configuration file inside.

Exposing Spring service via BlazeDS

The next thing to do is to add BlazeDS libraries to our web module dependencies. To do that, edit todolist-web/pom.xml and 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-core, blazeds-common and blazeds-remoting are only available from my personal repository for now, until Adobe publishes them to a public repository. Now that BlazeDS libraries have been added to the project, we’ll add a special Java class to the project. This class will act as a bridge between BlazeDS and Spring by retrieving Spring bean instances for BlazeDS FlexFactory interface. Here comes the org.epseelon.samples.todolist.controller.SpringFactory class (curtesy of Jeff Wroom):

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;
      }
    }
  }
}

Now we’ll configure the servlet that is responsible for handling remoting requests coming from the Flex UI and calling the Spring service accordingly. 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 the files that are in our shared configuration module.

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 need to add the following plugin 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, you can have a look at todolist-config/src/main/resources/services-config.xml. Here the most interesting part is the destination configuration: we define a todoService destination that uses our SpringFactory for handling and the source parameter corresponds to the bean id of the service.

There is one more thing to do in order to allow Maven to build todolist-web independently from other modules. flex-compiler-mojo defines a resource-bundle packaging that maven-war-plugin is not aware of. And you have to add the following plugin configuration to solve that issue:

<plugin>
    <groupId>info.rvin.mojo</groupId>
    <artifactId>flex-compiler-mojo</artifactId>
    <extensions>true</extensions>
</plugin>

Last but not least, run ‘mvn install’ to check that everything is working, and that both configuration files are present in WEB-INF/flex.

That’s it. Now our todo service is exposed for remoting via BlazeDS. Let’s move on to the client-side connection.

Writing the Flex UI

First off, delete todolist-ria/src/main/flex/Main.xml and create todolist-ria/src/main/flex/index.mxml with the following content:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                layout="vertical" verticalAlign="middle" horizontalAlign="center"
                xmlns:screen="org.epseelon.samples.todolist.view.screen.*">
    <screen:TodoForm/>
</mx:Application>

The main screen is a TodoForm class that you have to create in todolist-ria/src/main/flex/org/epseelon/samples/todolist/view/screen/TodoForm.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/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 = "";
        }
            //Ocorreu uma falha ao chamar algum servico servico.
        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 with two text fields to contain the ID and the title of the item being edited or created, and a list of all todo items.

Then you have a control bar with all the buttons necessary to create, update and delete items.

The third part is the remote object that is used to call methods on the server. As you can see, it references the todoService destination and defines the three methods to save, delete and list items, with their corresponding result and fault handlers.

Finally, we have the ActionScript part that implements methods to call the server, result and fault handlers. As you can see, all those methods use the org.epseelon.samples.todolist.view.entity.TodoItem class, that is really an exact ActionScript mapping for the org.epseelon.samples.todolist.domain.TodoItem class on the server-side. Here it goes:

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;
  }
}

Now what is really cool is that BlazeDS will automatically serialize into and deserialize from this class for us, so that we only have to manipulate this version in our client.

Compiling the Flex UI

Since our Flex UI defines a remote object that references our todoService destination, we 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 todoService destination is defined. You can add a <services/> element to the flex-compiler-mojo configuration section. But here, we are going to use the default setting, which causes the plugin to look for a file named services-config in the module resources. And to avoid duplicating those files, we will just add our shared configuration module as a dependency of todolist-ria:

<dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>todolist-config</artifactId>
    <version>1.0-SNAPSHOT</version>
    <classifier>resources</classifier>
    <type>zip</type>
    <scope>provided</scope>
</dependency>

And in order to unpack the files in resources, add the following plugin 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>

Now we need to add target/generated-resources to default resources by adding the following section in build:

<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 same port than the one on which the SWF is running. 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 we have to do is to modify the configuration for the flex-compiler-mojo in todolist-ria/pom.xml:

<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 can notice that the value for the context root corresponds to the build/finalName parameter defined in todolist-web/pom.xml. Now you can run ‘mvn install’ on the whole project.

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 several mxml files there, it will take any file called main.as or main.mxml as the main application. And because Velo really understood that Maven is all about “convention over configuration”, you can also specify your own path for this main file using the ’sourceFile’ configuration parameter. Ain’t that awesome?

Ready for the final steps?

Bundling the Flex UI into the web application

Now we need to include our Flex UI as a dependency in our web module. To do so, add the following dependency to todolist-web/pom.xml:

<dependency>
  <groupId>org.epseelon.samples</groupId>
  <artifactId>todolist-ria</artifactId>
  <version>1.0-SNAPSHOT</version>
  <type>swf</type>
</dependency>

Now if we want this dependency to be copied over to our web archive, we need to configure maven-dependency-plugin to do so. So you can 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>

There are still a few things we need to do, just to make it easier to access our application. First, edit todolist-web/src/main/webapp/index.jsp and put 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", "http://www.adobe.com/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", "http://www.adobe.com/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=http://www.adobe.com/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="http://www.adobe.com/go/getflashplayer">
</embed>
</object>
</noscript>
</body>
</html>

This code comes from the standard HTML template generated by Flex Builder and uses javascript to embed the SWF object into a traditional web page. Of course, we need to add the files this pages depends on. First download AC_OETags.js and copy it to todolist-web/src/main/webapp/AC_OETags.js.

Then download playerProductInstall.swf and put it in the same directory.

Lastly, download history.zip and unzip it in the same directory.

That’s it, now everything is ready. You can now 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…
And it should work!

And because I’m a good boy, I’m even going to give you the final version of this project with everything inside. Enjoy!

Conclusion

This project setup certainly requires a bit of work, but in the end, set aside this small issue with configuration file duplication that should be fixed soon, it’s pretty clean, and flex-compiler-mojo works really great.

Now of course, we can add many things like flexunit tests, asdoc generation and so on, but for now, I just wanted to focus on the configuration needed for BlazeDS to work.

Now I’m waiting for your feedback, questions, suggestions, and I’ll probably edit those articles as it comes until it’s good enough to be published… somewhere else.

In the meantime, I’d like to thank Velo for providing this great plugin as well as the support I needed from him to write this tutorial.

Ready for comments? Shoot!

1 Total TweetBacks: (Tweet this post)
  • en: Flex, Spring and BlazeDS: the full stack! (Part 4) [updated] http://tinyurl.com/88fsdx from: @sarbogast 04/13/09 08:49am
Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Slashdot
  • e-mail
  • Facebook
  • Google Bookmarks
  • blogmarks
  • Furl
  • StumbleUpon
  • Technorati
  • TwitThis
  • Reddit

Rich Internet Applications , , , , , ,

  1. 3.29 am at 3.29 am | #1

    Hi Sebastien,

    When we start talking about this article I had a bad opinion about spring. But, now I begin like it.

    Congratulations for the article, thanks for using flex-mojos and if you need any help on next ;)

    VELO

  2. 11.14 pm at 11.14 pm | #2

    Hi Sebastien,

    great tutorial just wondering if you can help me.

    I get

    [INFO] Loading configuration file C:\todolist\todolist\todolist-ria\target\config.xml
    [ERROR] C:\todolist\todolist\todolist-ria\src\main\flex\index.mxml:[4,-1] Could not resolve to a component implementation.

    when doing mvn install just before “Bundling the Flex UI into the web application”

    Im sure ive followed all the steps..any ideas…

  3. flexlover
    8.33 am at 8.33 am | #3

    One of the best explain article, nice work !!! keep it up.

  4. flexlover
    8.47 am at 8.47 am | #4

    But i am getting this error while building it.
    258K downloaded
    [WARNING] *** CHECKSUM FAILED – Error retrieving checksum file for com/adobe/flex/sdk/rpc/3.0.0.3.0.0.477/
    rpc-3.0.0.3.0.0.477.swc – IGNORING
    [INFO] ————————————————————————
    [ERROR] BUILD ERROR
    [INFO] ————————————————————————
    [INFO] Failed to resolve artifact.

    Missing:
    ———-
    1) com.adobe.flex.sdk:rpc:resource-bundle:en_US:3.0.0.3.0.0.477

    Try downloading the file manually from the project website.

    Then, install it using the command:
    mvn install:install-file -DgroupId=com.adobe.flex.sdk -DartifactId=rpc -Dversion=3.0.0.3.0.0.477 -Dc
    lassifier=en_US -Dpackaging=resource-bundle -Dfile=/path/to/file

    Alternatively, if you host your own repository you can deploy the file there:
    mvn deploy:deploy-file -DgroupId=com.adobe.flex.sdk -DartifactId=rpc -Dversion=3.0.0.3.0.0.477 -Dcla
    ssifier=en_US -Dpackaging=resource-bundle -Dfile=/path/to/file -Durl=[url] -DrepositoryId=[id]

    Path to dependency:
    1) com.yomari.econvision:econvision-webapp:war:1.0-SNAPSHOT
    2) com.adobe.flex.sdk:rpc:resource-bundle:en_US:3.0.0.3.0.0.477

  5. 11.46 am at 11.46 am | #5

    > flexlover

    Are you sure you added both repositories in Part 2? Because the dependency you’re missing is there: http://flex-mojos.googlecode.com/svn/trunk/repository/com/adobe/flex/sdk/rpc/3.0.0.3.0.0.477/

    > Dave

    There might be an error in the syntax of you MXML file. Did you copy it from the blog post? If yes, try to see if you get the same error with the index.mxml file you can find in the final project archive.

  6. 5.24 pm at 5.24 pm | #6

    Sebastian,

    Thanks for your help.

    yes I coped it from the final project archive and I still get the same error. I have checked the other files and all seem to match so Im not sure why I get a namespace error. Any suggestions?

    Dave

  7. 11.22 pm at 11.22 pm | #7

    Can you send me your project and I’ll have a look.

  8. 12.26 am at 12.26 am | #8

    sure …whats your email? Here is the error again. I get the same error when I take your source and use mvn install

    [INFO] Flex compiler configurations:
    -compiler.accessible=false
    -compiler.allow-source-path-overlap=false
    -compiler.as3=true
    -compiler.context-root todolist-web
    -compiler.debug=false
    -compiler.es=false
    -compiler.external-library-path=
    -compiler.fonts.local-fonts-snapshot C:\Maven\todolist\todolist-ria\target\fonts.ser
    -compiler.include-libraries=
    -compiler.keep-generated-actionscript=false
    -compiler.library-path=
    -compiler.locale en_US
    -compiler.optimize=true
    -compiler.services C:\Maven\todolist\todolist-ria\src\main\resources\services-config.xml
    -compiler.source-path C:\Maven\todolist\todolist-ria\src\main\flex C:\Maven\todolist\todolist-ria\src\main\resources
    -compiler.strict=true
    -compiler.theme=
    -compiler.verbose-stacktraces=false
    -load-config C:\Maven\todolist\todolist-ria\target\config.xml
    -runtime-shared-libraries=
    -use-network=true
    [INFO] Loading configuration file C:\Maven\todolist\todolist-ria\target\config.xml
    [ERROR] C:\Maven\todolist\todolist-ria\src\main\flex\index.mxml:[5,-1] Could not resolve to a component implementation.

  9. 7.55 am at 7.55 am | #9

    I guess there must be a problem with your Maven configuration then. My email is sebastien.arbogast@gmail.com

  10. Lorenzo
    11.24 am at 11.24 am | #10

    Hi Sebastien,

    This series is great! It allowed me to get up and running with Maven/Java/Flex in such a short time.

    But I’m getting the same error as flexlover, where it cannot find the resource bundle (en_US). I can successfully do ‘mvn install’ from the parent folder. But if I do ‘mvn install’ under the todolist-web module, I get an error where it cannot find the resource bundle. Any suggestions?

    Thanks

  11. Lorenzo
    11.35 am at 11.35 am | #11

    Here’s the result when I build from the todolist-web folder.

    Path to dependency:
    1) org.epseelon.samples:todolist-web:war:1.0-SNAPSHOT
    2) org.epseelon.samples:todolist-ria:swf:1.0-SNAPSHOT
    3) com.adobe.flex.sdk:rpc:resource-bundle:en_US:3.0.0.3.0.0.477

    ———-
    2 required artifacts are missing.

    for artifact:
    org.epseelon.samples:todolist-web:war:1.0-SNAPSHOT

    from the specified remote repositories:
    central (http://repo1.maven.org/maven2),
    flex-mojos-repository (http://flex-mojos.googlecode.com/svn/trunk/repository/),
    epseelon-repository (http://m2repo.epseelon.org/)

  12. 9.07 pm at 9.07 pm | #12

    Lorenzo, flexlover,

    I managed to reproduce your problem. I still don’t understand why building from todolist works and building from todolist-web fails, but it is not that important: when you build it from todolist, you get todolist-web.war in todolist-web/target, which is all you need. Because Maven builds in cascade. So don’t worry about the build failing in todolist-web. I’ll investigate, but don’t worry about it.

  13. 10.55 pm at 10.55 pm | #13

    Hi,

    To solve this problem, just need to add this on war:

    <build>
    <plugins>
    <plugin>
    <groupId>info.rvin.mojo</groupId>
    <artifactId>flex-compiler-mojo</artifactId>
    <extensions>true</extensions>
    </plugin>
    </plugins>
    </build>

    ok?

    VELO

  14. 11.03 pm at 11.03 pm | #14

    I just updated the article and the project accordingly and now it should work fine.

  15. Magnus
    10.15 am at 10.15 am | #15

    I get faultCode: InvokedFailed
    faultString: [MessagingError message="Destination 'todoService' either does
    not exists or the destination has no channels
    defined (and the destination does not define any default channels.)']‘
    faultDetail: ‘Couldn’t establish a connection to ‘todoService”

    Any ideas, I deploy the full solution as downloaded

  16. 1.40 pm at 1.40 pm | #16

    Are you using JBoss or Tomcat as a server?

  17. Magnus
    2.38 pm at 2.38 pm | #17

    Im using JBoss

  18. Magnus
    3.21 pm at 3.21 pm | #18

    By the way!

    What is the best way to autogenerate Value objects for both Java and ActionScript? Is there a Maven plugin to autogenerate that? Even better would be to have the full lifecycle from Hibernate XML files all the way so the VO for both Java and ActionScript.

    Cheers

  19. 8.07 pm at 8.07 pm | #19

    Right now there is no maven plugin (at least I never see).

    Is one issue on flex-mojos make this plugin.

    VELO

  20. 1.24 pm at 1.24 pm | #20

    Magnus, are you certain that your application is started and loaded correctly? I’ll test the project attached to this article again and see if I can reproduce your problem.

  21. 1.26 pm at 1.26 pm | #21

    Magnus, Velo,
    About generation of value objects, there’s nothing in BlazeDS for it but if my memory is correct, there is a code generator in GraniteDS (an alternative to BlazeDS) to do just that. As far as I’m concerned, I’m using a more integrated approach with AndroMDA (http://www.andromda.org)

  22. 1.45 pm at 1.45 pm | #22

    I talk with Franck (from GraniteDS project).
    I will adapt gas3 (ant-taks) into a mojo.

    VELO

  23. Magnus
    1.56 pm at 1.56 pm | #23

    Great stuff VELO I think it would be a good contribution.

    Sébastien Im gonna try again to see what Im doing wrong so I’ll get back to you on that.

    By the way has anyone tried the Hessian Flex port to compare speed compared to AMF?

  24. Magnus
    2.02 pm at 2.02 pm | #24

    Sébastien I just tried with the download and I get the same error when I run it on JBoss default installation…hmmm something obvious Im missing?

    Magnus

  25. 9.15 pm at 9.15 pm | #25

    You’re right Magnus, there’s definitely something wrong with my project: it doesn’t work anymore. Thanks for the feedback. I’ll fix it and let you know.

  26. 10.19 pm at 10.19 pm | #26

    I got it!!! It seems that Maven and its plugins have a hard time keeping properties consistent over time. You see the resource specified in ${project.target.directory}/generated-resources? By default it’s supposed to resolve to target/generated-resources, where both services-config.xml and remoting-config.xml are copied from configuration module. Well, suddenly, it doesn’t resolve to that anymore, so services-config.xml is not found by the compiler and you get the error saying that the client cannot find todoService.

    Here is the solution. All you have to do is to replace the resources section in todolist-ria/pom.xml with the following one:

    
           <resources>
                <resource>
                    <directory>${basedir}/src/main/resources</directory>
                </resource>
                <resource>
                    <directory>${basedir}/target/generated-resources</directory>
                    <filtering>true</filtering>
                </resource>
            </resources>
    

    I’ll correct the article and the end project right away.

  27. Frederick N. Brier
    11.21 am at 11.21 am | #27

    Sebastien, great article. This is the technology stack I want to use, but what drove you to do this with 3 maven sub-projects? I went through your tutorial. It builds, deploys and works fine. I tried to collapse it into a single war project and keep running into one issue or another with the .swf or java classes not building. No errors, but not doing what I want. You *should* be able to be able to do the flex-compiler-mojo plugin along with other plugins. I have put together maven scripts with XDoclet and other code generating steps that all get incorporated into a war in a single pom.xml. Why is this different? Thank you.

    Fred

  28. 11.35 am at 11.35 am | #28

    I agree that it may seem a little bit too complicated for the task at hand but it all comes down to what Maven encourages in terms of best practices. One of the main principles of Maven is “one module, one package”. Here we have one module to compile and package an SWF, and a second to build and package a WAR. And since both of them use common configuration files, I’ve factored out those files in a third module. If we wanted to be even closer to best practices, we would have to split the web module into one to build java classes in a JAR, and another one to bundle everything in a WAR.

    Now why this principle, that’s another story. I think the idea is reusability, even though it would be hard to reuse our SWF in another context as the one of this web app.

    By the way, I tried the single module approach first but I ran into problems because Maven can only have one source directory by configuration, and having Java classes and Flex files to compile meant two different source directories with two compilers running.

    Anyways, it seems more cumbersome with three modules, but I think it’s a good compromise between modularity, maintainability and elegance. The only thing that is really annoying is the amount of configuration still necessary to import configuration files.

  29. 1.48 pm at 1.48 pm | #29

    AFIK, I can’t attach another compiler-plugin to war module.

    So, war module compile src/main/java with java-compiler, and, there is no way to say it to compile src/main/flex with flex-mojos.

    May be I’m wrong and there is a way. But I don’t know how.

    If anyone knows, let me know.

    VELO

  30. Frederick N. Brier
    5.36 pm at 5.36 pm | #30

    Sebastien, I have gone the route before of splitting classes into different .jar(s) and .war(s) inside an .ear. But rather than belabor the point, let us say I want to write an XDoclet2 plugin that generates Actionscript POFOs from Hibernate POJOs. There is already an XDoclet2 plugin that does this [http://xdoclet.codehaus.org/Actionscript+plugin], but it would probably have to be modified/copied to meet the requirements of BlazeDS. In the case of your tutorial, that would be the TodoItem class. Try to keep these mirrored classes in sync in a large project is an unnecessary pain in the butt. So now we have to run the XDoclet2 Maven plugin [http://xdoclet.codehaus.org/Maven2+plugin] within a new Java Maven module that has the .java source files, pack/copy/unpack the generated Actionscript files over to the todolist-ria module, build the .swf and then continue with the current pack/copy/unpack(s) of the .swf and configuration files. We also either need to .jar up the original .class files or install them in the local repo, or pack/copy/unpack them over to the .war. What if like yourself, I like .hbm.xml files instead of EJB3 annotations, but like to generate them with XDoclet? I have to either move those over as well, or .jar them up with the POJOs and hope my class loader du jour can find them in the .jar when the SessionFactory is constructed in the Spring context in a .war.

    It is a professional decision when KISS trumps the potential savings of reuse. I find having the entire source in one module, where my IDE (IDEA) can easily find usages and perform refactorings across Java, XML and Actionscript, and a flat single level deployment archive, simplifies development and eliminates class loader problems and overhead. If in the future, there are some set of classes which might be shared with another project or within modules, then I will refactor them out at that point.

    The problems I am running into getting this to run as a single module seem to be related to the need to specify a element. I am delving into mojo compiler code to try and determine out why it cannot figure out that the source directory for flex is src/main/flex given the standard Maven layout. It should not need the sourceDirectory element. I also want it to pickup a second source tree, say target/generated-sources-flex, in the event that I write an XDoclet2/BlazeDS plugin. Thank you again.

    Fred

  31. 5.54 pm at 5.54 pm | #31

    This project is by no means a production-ready project. It is merely a tutorial project to give some hints about how to glue things together. There is especially one thing that is not good in this project: TodoItem !

    IMHO, Hibernate entities should never ever be exposed to the presentation layer. There should always be a layer of abstraction (call it Value-Objects or Data-Transfer Objects) between business layer and presentation layer, such that Spring services manipulate Hibernate entities but only accept DTO’s as input or output. There are several excellent reasons for that approach, and if you want to know more, I suggest reading this.

    By the way, the reason why I wrote this article series was because I needed this stack in a much more advanced real-life project I’m working on. This project uses AndroMDA to generate most of the boilerplate code, including Hibernate entities and DTO’s from a UML model. And right now, I’m trying to write a new cartridge for AndroMDA to generate ActionScript mapped DTO’s based on the same model.

  32. 6.59 pm at 6.59 pm | #32

    Hi Frederick,

    Maven standard directory structure doesn’t preview any non java project.

    So, I adapt it to flex-mojos.

    Even if I look for sourcePath in other place, how to run flex-mojos on a project where packaging is war?

    VELO

  33. 8.20 pm at 8.20 pm | #33

    Hi,

    First of all thanks so much for such a nice article..

    I am able to build everything and i am able to generate the war File.

    Now when i deploy the war File i only see one .swf File which is generated by the xxx-ria sub project..

    How ever most of our applications will have multiple .swf files (one for eahc module) and we usually expect the user to type the URI ending in mxml. (Which are handled by the Flx

    As we configure the tomcat for this web app using the servlet mapping config info:

    FlexMxmlServlet
    *.mxml

    Now the ria app won’t copy any .mxml files into the war file..!!

    Do i need to add this capability in the web app to copy all the Flex Related files (.mxml and .as ) to the appropriate folder in the webapp ??

    Thanks
    sateesh

  34. 8.27 pm at 8.27 pm | #34

    As I said before, this project is by no means a production-ready project. Now if you want to customize it to handle MXML files via the Flex compiling servlet, it is perfectly feasible. But the layout of this project is probably not adapted for that. So if you manage to get it working, I’ll be happy to have a look.

  35. JR
    9.23 pm at 9.23 pm | #35

    I’ve noticed that you filed:

    http://bugs.adobe.com/jira/browse/BLZ-169

    It would appear that you’d like to use BlazeDS in an OSGi environment. I’d be interested in sharing ideas with you, as I’m attempting the same thing. Have you had any success? Also, any luck with GraniteDS either?

  36. alexandre
    8.21 pm at 8.21 pm | #36

    What an amazing initiative!

    I am speechless with the quality of your material.

  37. deadagain
    8.37 pm at 8.37 pm | #37

    Not only AMAZING content IMO, but also so well written… A two year-old step-by-step guide about a quite complicated topic… You gotta become a teacher, seriously
    Thanks man, you’ve helped me to learn even maven and a little flex… =D
    Hope everything keeps going well with Lorelai… =P

    To VELO belo trabalho cara!

  38. 3.04 pm at 3.04 pm | #38

    Hey, valeu brody =D

    VELO

  39. loic
    11.32 am at 11.32 am | #39

    Hi Sebastien,

    Thanks very much for this great tutorial.

    I just have a problem :
    I download your projet and when i run the mvn install, i’ve got this error :

    [INFO] ————————————————————————
    [ERROR] BUILD ERROR
    [INFO] ————————————————————————
    [INFO] Failed to resolve artifact.

    Missing:
    ———-
    1) com.adobe.blazeds:blazeds-common:jar:1.0

    ….

    2) com.adobe.blazeds:blazeds-core:jar:1.0

    ….

    3) com.adobe.blazeds:blazeds-remoting:jar:1.0

    ….

    ———-
    3 required artifacts are missing.

    for artifact:
    org.epseelon.samples:todolist-web:war:1.0-SNAPSHOT

    from the specified remote repositories:
    flex-mojos-repository (http://flex-mojos.googlecode.com/svn/trunk/repository/),
    epseelon-repository (http://m2repo.epseelon.org/),
    central (http://repo1.maven.org/maven2)

    thanks for your help :-)

  40. 11.39 am at 11.39 am | #40

    Yep, sorry for that, I forgot to fix the final project, but there is a mistake in my todolist-ria/pom.xml. I won’t be able to fix it right now but you can do it: just replace 1.0 by 3.0.0.544 as the version number for blazeds-common, blazeds-core and blazeds-remoting dependencies.

  41. loic
    1.46 pm at 1.46 pm | #41

    Thank you very much !! It works :-)
    Very, nice job

  42. anand
    3.09 pm at 3.09 pm | #42

    Hi,
    Thanks for a great write up.
    However, I downloaded the todolist2.zip to see the blaze libraries were missing in them. Could you tell me if it is possible to get them.

  43. 3.40 pm at 3.40 pm | #43

    See comment 42 above.

  44. anand
    1.28 pm at 1.28 pm | #44

    I think the versions of the 3 blaze libraries are 3.0. Because the maven library site does not have the libraries of version 3.0.0.544.
    check http://repo1.maven.org/maven2/com/adobe/blazeds/

  45. 2.13 pm at 2.13 pm | #45

    Great detail. Great Post.

  46. 2.28 pm at 2.28 pm | #46

    Good point. Actually, I had deployed version 3.0.0.544 to my personal repository before because Adobe had only release 1.0-beta1 to the official repository. I didn’t know that 3.0 had been released there since. So I guess you can use those versions instead.

  47. Peter Delaney
    8.56 pm at 8.56 pm | #47

    Sebastien;
    Thanks so much for publishing this article. I’ve been looking for something like this for a while. I’ve even paid for such tutorials that have not even worked. This is the exact application stack I’ve been looking for. Thanks

    In you instructions where you delete Main.xml to index.xml I had a problem where the source was not recognized. It took me a while to find it. I change the name to index.mxml and my problem when away. Just wanted to give you some feedback.

  48. 9.14 pm at 9.14 pm | #48

    Thanks Peter. I just fixed the article accordingly.

  49. dialloma
    10.03 am at 10.03 am | #49