Integrating Adobe Flex with Struts 2 and JBOSS Using BlazeDS

The first step of this process will be to able to run BlazeDS on JBOSS without struts 2. If that is not working yet for you please do that first.
The main reason that Struts 2 and BlazeDS out of the box fail to work together is because most configurations of Struts 2 intercept all HTTP requests to a web application. This is because of this piece of configuration in web.xml:

<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
 <filter-name>struts2</filter-name>
 <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
 </filter>

This is a java filter that intercepts all requests and responses. This is a problem because most instructions on how to setup BlazeDS ask you declare the following servlet in your web.xml:

<!-- MessageBroker Servlet -->
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<display-name>MessageBrokerServlet</display-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>

However this will never work with Struts 2 unless you restrict the url pattern to something less than /*. Filters seem to be executed always before any servlet declared in web.xml.

To solve this problem, instead of declaring the MessageBrokerServlet as a servlet lets declare it as filter in the filter chain. Filters declared in a filter chain are always executed in the order that they are declared in web.xml. For that reason make sure that this filter is always called before the Struts 2 filter(FilterDispatcher). If our filter determines that there is an incoming request that needs to be handled by BlazeDS, we call the MessageBrokerServlet, and we prevent any more filters in the chain to be called. If the request is not for BlazeDS the filter will not be called.

package uk.co.spltech.web.filter;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.mock.web.MockServletConfig;

import flex.messaging.MessageBrokerServlet;

/**
* The MessageBrokerServlet has a lower priority than Filters, regardless of the url-pattern
* we use. For that reason I created a filter that directly extends the MessageBrokerServlet but
* behaves as a filter. This might be quite dodgy but that's the only way I found to integrate struts
* 2 and Adobe Flex/BlazeDS. Anyone who knows a simpler way please email me at info at spltech.co.uk
*
* @author Armindo Cachada
*
*/
public class FlexFilter  extends MessageBrokerServlet implements Filter{

public void destroy() {
super.destroy();
}

/**
* If this method is called the parent service method of the MessageBrokerServlet is called,
* which does whatever BlazeDS needs to do to communicate with the flex client. Note Here
* that any subsequent filter will not be called because I am not invoking filterChain.doFilter/
* That is on purpose, because if it does, the normal struts 2 action mapping mechanism will be called.
*
*/
public void doFilter(ServletRequest servletrequest,
ServletResponse servletresponse, FilterChain filterchain)
throws IOException, ServletException {
this.service((HttpServletRequest)servletrequest, (HttpServletResponse)servletresponse);
}

/** Note the use here of MockServletConfig. This utility class is available in the spring framework. It is  meant
to be used for testing but I am actually giving it a real purpose :)
*/
public void init(FilterConfig filterconfig) throws ServletException {
System.out.println("filter called");
MockServletConfig servletConfig = new MockServletConfig(filterconfig.getServletContext());
Enumeration filterParameters = filterconfig.getInitParameterNames();

while (filterParameters.hasMoreElements()){
String filterParameter = (String)filterParameters.nextElement();
System.out.println("Found parameter: " + filterParameter);
String value =filterconfig.getInitParameter(filterParameter);
servletConfig.addInitParameter(filterParameter, value);

}
super.init(servletConfig);
}

}




Add the following to your filter chain in web.xml:

<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>

<filter>
<filter-name>flex</filter-name>
<filter-class>uk.co.spltech.web.filter.FlexFilter</filter-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
</filter>

This solution did the job for me. Please let me know if it also works for you!

Adobe Flex with Struts 2 using HttpService

Using HttpService is the easiest way in which you can integrate Adobe Flex and Struts 2. As an example I will show you how you can create a registration form in Adobe Flex which calls a struts 2 action to create new users. Note that this example is very simple and for demonstration purposes. I am not adding any validation in flex and not even creating the users in a database. All I show you is how you can pass data between flex and struts.

First let me show you the steps you need to perform in Adobe Flex:

Step 1

Create the registration form in mxml:

<mx:Form label="Registration Form">
 <mx:FormHeading label="Registration Form">
 </mx:FormHeading>
 <mx:FormItem label="First Name">
  <mx:TextInput id="firstName">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem label="Last Name">
  <mx:TextInput id="lastName">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem label="Email">
   <mx:TextInput id="email">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem label="Username">
  <mx:TextInput id="username">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem label="Password">
  <mx:TextInput id="password" displayAsPassword="true">
  </mx:TextInput>
 </mx:FormItem>
 <mx:FormItem>
  <mx:Button label="Register" click="registerUser()"/>
 </mx:FormItem>
</mx:Form>

Step 2

Create the HttpService object:

<mx:HTTPService id="registerService" showBusyCursor="true" useProxy="false" url="register.action" resultFormat="e4x" method="POST" result="registerConfirmation(event)" fault="registerFailed(event)"/>
  • register.action is the Struts 2 action that registers the user
  • registerConfirmation(event) is the function that is called if the http service call is successful.
  • registerFailed(event) is the function that is called if an error occurs. For example if the server is down. The event object gives you access to any xml/html that might be returned upon the call of the action.
  • resultFormat specifies in which format you want to view the results. It can be text, xml or an object. There are 5 possible values for this field:
    • object – An XML object is returned and is parsed as a tree of ActionScript objects
    • xml – Returns literal XML in an XMLNode object
    • e4x – Returns literal XML that can be accessed using ECMAScript for XML(E4X) expressions.
    • flashvars – The result is text in flashvars format, value pairs separated by ampersands. Flex returns this result in an actionscript object
    • array – The result is XML that is returned in an Array even if there is only one top level node.

Step 3

Write the actionscript call to submit the form:

public function registerUser():void {
      var params:Object = { 'user.firstName': firstName.text,'user.lastName': lastName.text, 'user.email':email.text, 'user.password':password.text }; 
      this.registerService.send(params);
}

Step 4

Create registerConfirmation(). This method checks whether the registration was successful. We can know that by inspecting the XML that is returned.

private function registerConfirmation(event:ResultEvent):void {
        var xml:XML=XML(event.result); 
 if (xml != null && xml.item == true) {
  mx.controls.Alert.show("Registration Successful!");
 }
 else {
  mx.controls.Alert.show("The registration was not successful.");
 }
 }}

Step 5

If we can’t call the action on the server because of some networking issue we also should give some feedback to the user.

   
/**
 * Display a message to the user explaining what went wrong
 */
private function registerFailed(event:FaultEvent):void {
 mx.controls.Alert.show(event.fault.message);
}

Now let’s create our Struts 2 Action:

Step 1

Create RegisterAction.java

package uk.co.spltech.web.actions;

import uk.co.spltech.beans.User;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Creates a new user
 * 
 * @author Armindo Cachada
 *
 */
public class RegisterAction extends ActionSupport {

 private User user;
 
 private Boolean success;
 
 public Boolean getSuccess() {
  return success;
 }

 public void setSuccess(Boolean success) {
  this.success = success;
 }

 public User getUser() {
  return user;
 }

 public void setUser(User user) {
  this.user = user;
 }

 /**
  * Registers a user
  * 
  * @return
  */
 public String register() {
  System.out.println("Checking user");
  if ( this.getUser()!= null) {
   User u = this.getUser();
   if (u.getEmail()!= null && !u.getEmail().equals("") && u.getPassword()!= null 
     && !u.getPassword().equals("") ) {
    System.out.println("Successfully registered user with email=" + u.getEmail());
    this.success=true;
   }
   else {
    this.success=false;
    System.out.println("Error registering user");
   }
  }
  else {
   this.success=false;
   System.out.println("Error registering user");
  }
  
  return SUCCESS;

 }
 
}

Step 2

Add action name=’register’ to struts.xml

<package name="user" extends="struts-default">
        <action name="register" method="register" class="uk.co.spltech.web.actions.RegisterAction">
           <result type="xslt">   <param name="exposedValue">{success}</param></result>
        </action>
</package>

Source for “Adobe Flex with Struts 2 using HttpService” Example


To compile this example you will need to copy the following libraries to the lib folder:

commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging.jar
freemarker-2.3.13.jar
ognl-2.6.11.jar
struts2-core-2.1.6.jar
xwork-2.1.2.jar
struts2-convention-plugin-2.1.6.jar

Integrating Adobe Flex with Struts 2

I have noticed a lot of people on the internet are searching for information on how to integrate Struts 2 with Adobe Flex. It is not as complicated as most think and there’s more than one way of doing it.

Ways in which you can integrate Adobe Flex and Struts:

1. Use HttpService in Adobe Flex to call actions in Struts. In Struts, instead of returning a jsp file change the return type of your action to XSLT. In this way the contents of your action are automatically converted to XML. You can use the XML result in Adobe Flex to determine the result of the action that you invoked using HttpService.
This technique is by far the easiest of the three.
The main advantages are:

  • It is quite simple to use compared to the other techniques
  • You get complete control on the information that you transfer between Adobe Flex and Struts

There are disadvantages though:

  • The more information you transfer between Flex and Struts the more code you have to write for parsing/converting/… data

You can easily use HttpService to register a user using a struts action or to authenticate a user. Or you can use HttpService to retrieve information from the database through struts. In my next post I will give you an example on how to implement a registration system using HttpService.

2. Use Soap Web Services

Use Soap WebServices to connect to the Struts 2 backend. If you use an EJB 3 compliant application server you can easily create a web service by adding the @WebService annotation to an EJB 3 bean interface.
If you don’t have an EJB 3 compliant application server we recommend you to use Apache Axis 2 or Enunciate. The advantage of Enunciate is that you can publish your service not only for use with SOAP, it also creates endpoints for REST, AMF and JSON. We will exemplify this approach in a later post.

The advantages of using Web Services are:

  • Automatic class generation: With a few mouse clicks and the WSDL that describes your web service, Flex generates all the classes needed to access your web service.
    This is done via the ported version of Apache Axis.
  • The conversion of data from/to XML is done automatically for you. You only deal with objects and method calls.

The major disadvantage of using web services are circular references. An example of a circular reference is one where the child node of an object references its parent.

A —-> B
B —-> A

You need to deal with this by using annotations or by manually deleting the circular reference. Otherwise you will get a nasty marshaling/unmarshaling exception when calling the web service.

3. Using BlazeDS, from Flex it is possible to invoke methods on java objects deployed in an application server. This is done using AMF a message protocol developed by Adobe. This is a similar method to using SOAP. The only difference is that all the information is transferred in binary format. Performance wise this is probably the best option available to integrate Java/J2EE and Adobe Flex. Read my post on Integrate Adobe Flex and JBOSS using BlazeDS for more information.

Uploading a file with Flex 3 and Struts 2

In my previous post I taught you how to upload a file using Struts 2. In the example I gave you, both the presentation layer and the code that received the file were done in Struts 2.
But what if you want to upload a file via Adobe Flex? How can you do it?

The FileReference flex class allows you to upload files from a client to a server. In our case a server running Struts 2. FileReference shows a dialog box that allows the user to select which file to upload.
Enough of theory. Below is an example of the code that you can use to upload a file to a server running struts 2:

Step 1

First you need to create the dialog box that will allow the user to select a file to upload by clicking the button “Browse”.

<mx:VBox width="100%" left="10" top="10" >
   <mx:HBox>
       <mx:Label text="Image to upload:"/> <mx:TextInput disabledColor="black" enabled="false" id="fileName" width="200"  />
              <mx:Button width="80" label="Browse" click="browseFiles()" />
   </mx:HBox>
          <mx:Button id="uploadButton" label="Upload" enabled="false" click="upload()"/>
          <mx:ProgressBar labelPlacement="center"  source="{fileRef}" label="%3%%" minimum="0" maximum="100" color="blue" height="13" width="100" id="progressBar" visible="false" includeInLayout="false" />
    
        </mx:VBox>

Step 2

Create an object of type FileReference and add event listeners to track progress of the upload:

 fileRef = new FileReference();
 fileRef.addEventListener(Event.COMPLETE, completeEvent);
 fileRef.addEventListener(Event.SELECT, selectEvent);
 fileRef.addEventListener(IOErrorEvent.IO_ERROR, showError);
 fileRef.addEventListener(ProgressEvent.PROGRESS, progressEvent);
 fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, showError);  

Step 3

Write the actionscript code that opens a dialog that calls the native “Open File Dialog” in the user’s operating system.

private function browseFiles():void {
      var validFiles:Array= new Array(new FileFilter("Images (*.jpg,*.png,*.gif,*.jpeg)", "*.jpg;*.png;*.gif;*.jpeg"));
      this.fileRef.browse(validFiles);
   }

Step 4

Write the actionscript code that uploads the file to the server.
In our previous example the struts 2 action for the upload is “/fileUpload”.
Therefore we use the following url to upload the file: ${baseUrl}/fileUpload

/**
   * Uploads a file to the server 
   * 
   */ 
   private function upload():void {
     mx.controls.Alert.show("Starting upload.");
     var sendVars:URLVariables;
     var request:URLRequest;
     sendVars = new URLVariables();
     request = new URLRequest();
     request.data = sendVars;
     request.url =baseUrl + "/fileUpload";
     request.method = URLRequestMethod.POST;
     fileRef.upload(request,"file",false); 
   }  

You can find sample code to upload a file here. Note however that the example only works with a server to receive the file.

Uploading a file with Struts 2

Uploading a file with Struts 2 is really simple with the help of the FileUploadInterceptor. The steps required to upload a file using Struts 2 are:

Step 1

Create the Struts 2 Action class. In our example we have called it FileUploadAction.java

An example of an action to upload a file:

package uk.co.spltech.web.actions;

import com.opensymphony.xwork2.Action;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;


/**
 * Uploads a file using Struts 2
 * 
 * @author Armindo Cachada
 */
public class FileUploadAction extends ActionSupport {
     private static final long serialVersionUID = 12323232L;
     private File file;
     private String fileFileName;
     private String fileContentType;
    
    
 public String execute() {

  return Action.SUCCESS;
 }
   
 public File getFile() {
        return this.file;
     }
 
  public void setFile(File file) {
         this.file = file;
      }

  public void setUpload(File file) {
      this.file = file;
   }
 /**
       * This method is called by the action.
       * Here you can do whatever you want with the uploaded file
       */
 public String upload() throws IOException  {
        File uploadedFile =this.getFile();
        // you can do whatever you want with the file
        return Action.SUCCESS;
 }


 public String getFileFileName() {
  return fileFileName;
 }

 public void setFileFileName(String fileFileName) {
  this.fileFileName = fileFileName;
 }

 public String getFileContentType() {
  return fileContentType;
 }

 public void setFileContentType(String fileContentType) {
  this.fileContentType = fileContentType;
 }

 public String getFileUrl() {
  return fileUrl;
 }

 public void setFileUrl(String fileUrl) {
  this.fileUrl = fileUrl;
 }
}

Step 2

Create the action in struts.xml

<action name="fileUpload" class="uk.co.spltech.web.actions.FileUploadAction" method="upload">
 <result>/success.jsp </result>
 <result type="error">/upload.jsp</result>   
</action>

Step 3

Create the jsp. For example:

Please select the file you want to upload:

 <s:form action="fileUpload" method="post" enctype="multipart/form-data" theme="simple">  
  <s:actionerror cssClass="errorMessage"/>
  <s:fielderror cssClass="error"/>
      <s:file name="upload" label="File"/>
     <s:submit/>   
 </s:form>

Step 3

The default maximum file size that struts 2 allows you to upload is only 2 megabytes. You can increase this size by specifying in struts.properties the struts.multipart.maxSize. This value is in bytes.
For example:

struts.multipart.maxSize=20480

Finally you can download the code for this example here.

Note that I didn’t include the required struts2 library files with the example.
The files you need are:

commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging.jar
freemarker-2.3.13.jar
ognl-2.6.11.jar
struts2-core-2.1.6.jar
xwork-2.1.2.jar
struts2-convention-plugin-2.1.6.jar


Click here to learn how to upload a file with Adobe Flex

Creating hierarchical actions in struts 2.1

Struts 2.1 doesn’t directly support hierarchical actions. Nonetheless I still was able to implement them at visualmandarin.
Notice how the podcasts and exercises urls are all organized into folders and even the lesson title is present in the url: Lesson – Can I have a menu please?

If you are interested in SEO optimized urls for your website this kind of feature is a MUST. But how to do it struts 2.1? In short the steps you need to follow are:

Step 1

If you are running on struts 2, migrate to struts 2.1

Step 2

Make sure you are using the convention plugin

Step 3

Ensure that all your actions have an explicit namespace. If you don’t you might find that those actions are being called when they shouldn’t.

Step 4

Create a wildcard action in the namespace that you want to be hierarchical. In my case I have created one in namespace /lessons:

<action name="*" namespace="/lessons" method="execute">
<result name="lister"></result>
<result name="ResourceNotFoundException" type="chain">404</result>
</action>

Your lessons locator action should inspect the uri to determine which resource to display. In my case I was dealing with lessons but you could be dealing with categories and products or anything else. It shouldn’t make any difference.

Step 5

Extend org.struts2.impl.StrutsActionProxy and override the following prepare() method:

@Override
protected void prepare() {
String profileKey = "create DefaultActionProxy: ";
try {
UtilTimerStack.push(profileKey);
// we backtrack the namespace until we find an action that matches
this.findActionByBacktracking();

if (config == null && unknownHandler != null) {
config = unknownHandler.handleUnknownAction(namespace, actionName);
}
if (config == null) {
String message;

if ((namespace != null) && (namespace.trim().length() > 0)) {
message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_PACKAGE_ACTION_EXCEPTION, Locale.getDefault(), new String[]{
namespace, actionName
});
} else {
message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_ACTION_EXCEPTION, Locale.getDefault(), new String[]{
actionName
});
}
throw new ConfigurationException(message);
}

resolveMethod();

if (!config.isAllowedMethod(method)) {
throw new ConfigurationException("Invalid method: "+method+" for action "+actionName);
}

invocation.init(this);

} finally {
UtilTimerStack.pop(profileKey);
}
}

/**
* Returns a list of all possible namespaces. The one with biggest length
* is top of the list up to /xxx... bottom of a list. Note that we don't backtrack until
* the empty namespace
*
* @param namespace
* @return
*/
private String [] getAllPossibleNamespaces(String namespace) {
List<String> namespaces = new ArrayList<String>();
// e.g. /lessons/hsk/.../1 ->
String [] tokens = namespace.split("/");
tokens=removeEmptyStrings(tokens);
if (tokens != null && tokens.length >= 0) {
for (int i=0; i< tokens.length; i ++) {
String gnamespace="";
for (int j=0;j<tokens.length- i;j++) {
gnamespace += "/" + tokens[j] ;
}
namespaces.add(gnamespace);
}
}
return namespaces.toArray(new String[0]);
}

private String [] removeEmptyStrings(String [] tokens) {
List<String> result = new ArrayList();
for (String token: tokens) {
if (token == null || token.equals("")) {

}
else {
result.add(token);
}
}
return result.toArray(new String[0]);
}

/**
* Searches for the given action in one or more namespaces
*
*/
private void findActionByBacktracking() {
if (namespace != null && !namespace.equals("") && !namespace.equals("/")) {
String [] namespaces =this.getAllPossibleNamespaces(namespace);

for (String namespace: namespaces) {

config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);

if (config != null ) break;
}

}
else {
config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
}

}

Step 6

Extend org.apache.struts2.impl.StrutsActionProxyFactory.

My implementation as an example:

public class HierarchicalActionProxyFactory extends DefaultActionProxyFactory {
@Override
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {

HierarchicalActionProxy proxy = new HierarchicalActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
container.inject(proxy);
proxy.prepare();
return proxy;
}
}

Step 7

Finally the last step: override the property struts.actionProxyFactory
in struts.properties and point to your implementation of the proxy factory.

My implementation was:

struts.actionProxyFactory=com.visualmandarin.web.config.HierarchicalActionProxyFactory

If you have followed these steps correctly you should be able to have the same kind of urls that I have in visualmandarin.com website. If there’s something that you don’t understand don’t hesitate to ask!


You can find a working example here. This war file was tested in jboss 4.2.3 but should work in any compliant application server.

What you need to get this example working:

  • Add the struts 2 library files to the lib directory
  • Add the convention plugin jar file to the lib directory
  • Specify the correct path to your application server

Once you build the war file and deploy it, you can access the example at http://localhost/example3 or http://localhost:8080/example3