Tuesday, August 4, 2009

Adobe Flex & Struts 2.0 Integration

Flex presentation layer has been gaining popularity over the years, mostly because Adobe made the Flex SKD open source. The SDK focussed mostly on the presentation layer and leaves the door open for integrating with various server side technologies. With a vast majority of applications developed in J2EE platform utilizing struts, it becomes the obvious choice for providing the server side functionality.

In this article, I am going to explain how to integrate Flex presentation layer with Struts (2.0) server side. To make the process of creating new projects using this methodology, I have created a Maven archetype, that when installed, would create a sample project with the example explained in this article.

This article assumes understanding of Struts and Flex programming and not going into the details of the corresponding frameworks.

Submitting Flex Form Data to Struts Form



In this section we are going to discuss the submission of Flex form data to a struts action. The form data consists of product details, which when sent to the struts action, would be converted to java object and saved in the action.

Step 1

Create a Flex Form that contains the data fields 'productName', 'productDescription', 'productPrice', and 'productQuantity'.


<mx:Form id="addProductsForm" label="Add Product Form" width="100%" height="60%" >
<mx:FormHeading label="Add Product Form">
</mx:FormHeading>

<mx:FormItem label="Product Name:">
<mx:TextInput id="productName">
</mx:TextInput>
</mx:FormItem>
<mx:FormItem label="Product Description">
<mx:TextInput id="productDescription">
</mx:TextInput>
</mx:FormItem>
<mx:FormItem label="Product Price">
<mx:TextInput id="productPrice">
</mx:TextInput>
</mx:FormItem>
<mx:FormItem label="Quantity in Stock">
<mx:TextInput id="productQuantity">
</mx:TextInput>
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Add" click="addProduct()"/>
</mx:FormItem>
</mx:Form>

Step 2

Create the service that invokes a Struts action. This service defines the methods to be invoked for the successful response and on failure.



<mx:HTTPService id="addProductService" showBusyCursor="true" useProxy="false"
url="addProduct.action" resultFormat="e4x" method="POST"
result="addProductResult(event)" fault="addProductFault(event)"/>


Step 3

Create the method that gets called when the button was clicked. This method creates a dynamic object with the attributes of the form data and submits the 'addProduct Service'.



public function addProduct():void {
var params:Object = { 'product.name': productName.text,
'product.description': productDescription.text,
'product.price':parseFloat(productPrice.text),
'product.quantity':parseInt(productQuantity.text)
};
this.addProductService.send(params);
}



Step 4

Process the response from the server. In case of error, display the error message. In this example, we have not applied any filters on the output result of the action. So, a complete XML representation of the action class would be sent back to the Flex as a response. Look for the 'status' variable and see if it is true and if so, the product was added successfully, else the details of the errors are stored in the 'actionErrros' variable of the Struts action object.



private function addProductResult(event:ResultEvent):void {
var xml:XML=XML(event.result);
var status:String = xml..status;
if (status != null && status.toString() == "true") {
mx.controls.Alert.show("Product was added Successfully!");
var messages:XMLList = xml..actionMessages.item;
actionMessages.text = messages.toString();
}
else {
var messages:XMLList = xml..actionErrors.item;
actionMessages.text = messages.toString();
mx.controls.Alert.show("Product was NOT added. See messages below");
}
}


The complete Flex application can be seen here.


Step 5

Create Struts configuration file, and define the action mapping with the result type as XSLT. This configuration would marshall the complete action class into XML stream and send it to the requester, in this case the Flex application.

<action name="addProduct" method="addProduct" class="com.company.flex.action.ProductAction">
<result type="xslt"></result>
</action>



Step 6

Define the POJO for the Product. And Create the Action class that extends the action support. The 'addProduct' method checks for the validity of input, and if validated, adds the product to the static product list and sets the status as true, else sets teh status as false.


public String addProduct() {
this.status = true;
if (this.getProduct() != null) {
Product p = this.getProduct();
if ( p.getName() == null p.getName().equals( "" ) ) {
addActionError ("Product Name can not be blank" );
this.status = false;
}
if ( p.getPrice() <= 0 ) {
addActionError( "Product Price has to be a positive number" );
this.status = false;
}

if ( p.getQuantity() <= 0) {
addActionError( "Product Quantity has to be a positive number" );
this.status = false;
}

if (status) {
getProducts().add( getProduct() );
addActionMessage( "Product " + p.getName() + "[" + p.getPrice() + ", " + p.getQuantity() + "] was added" );
System.out.println( "Product " + p.getName() + "[" + p.getPrice() + ", " + p.getQuantity() + "] was added" );
}

} else {
System.out.println ( "Product Object was not set for the action" );
addActionError( "Product Object was not set for the action" );
this.status = false;
}

return SUCCESS;

}



A complete example project can be downloaded here. This project is a maven project and you need maven version 2.2.0 to execute this project. I am using the maven flex-mojos for compiling and building the mxml and actionscript files, so you need to update your corresponding settings.xml file in your home directory.


Fetching Data from Struts Action



In this section we are going to discuss how to fetch data from a Struts action and display it in Flex application. For keeping this article simple, we are going to fetch the static data from the action class, but this could be easily extended to fetch the data from a server side file or database. This involves using the "xslt" result type of struts action (available in 2.0).

Step 1

Define a Flex form that has a button (We are not sending any data to server, however that change can easily be made) and a display grid for displaying the results.


<mx:Form id="detailsForm" label="Get Details Form" width="100%" height="100">
<mx:FormItem>
<mx:Button label="Get Product Details" click="getProductDetails()"/>
</mx:FormItem>
</mx:Form>

<mx:DataGrid id="dg" color="0x323232" width="100%" height="90%" rowCount="3" dataProvider="{products}">
<mx:columns>
<mx:DataGridColumn dataField="name" headerText="Product Name"/>
<mx:DataGridColumn dataField="description" headerText="Description"/>
<mx:DataGridColumn dataField="price" headerText="Price"/>
<mx:DataGridColumn dataField="quantity" headerText="Quantity in Stock"/>
</mx:columns>
</mx:DataGrid>



Step 2

Create the service that invokes a Struts action. This service defines the methods to be invoked for the successful response and on failure.


<mx:HTTPService id="getDetailsService" showBusyCursor="true" useProxy="false" url="getProductDetails.action" resultFormat="e4x" method="POST" result="getDetailsResult(event)" fault="getDetailsFault(event)"/>


Step 3

Create the method to submit the form and get the results from the struts action. In this case, this is a dummy action method and we are fetching the data from the action itself.


public function getProductDetails():void {
var params:Object = {};
this.getDetailsService.send(params);
}


Step 4

Define the Struts configuration for this action in struts.xml file


<action name="getProductDetails" method="dummy" class="com.company.flex.action.ProductAction">
<result type="xslt"> <param name="exposedValue">products</param></result>
</action>


Step 5

Define the action method and a property 'products' in the action class.


public ArrayList<Product> getProducts() {
if ( products == null ) {
products = new ArrayList<Product>();
Product prod1 = new Product();
prod1.setName( "Canon Rebel Xi" );
prod1.setDescription( "Digital SLR Camera from Canon." );
prod1.setPrice( 456.50f );
prod1.setQuantity( 2 );
products.add( prod1 );
Product prod2 = new Product();
prod2.setName( "IPhone 3G" );
prod2.setDescription( "3rd Generation Smart Phone from Apple" );
prod2.setPrice( 199.99f );
prod2.setQuantity( 10 );
products.add( prod2 );
}
return products;
}

public String dummy() {
return SUCCESS;
}


For complete application example, please see the resources section below.

Maven Archetype for Flex-Struts Project Creation



To make the creation of Flex Struts projects easier, I have created a Maven Archetype (using other open source components available on-line like flex-mojos etc).

Step 1

Download the maven archetype zip file from the resources below.

Step 2

Extract the contents into a folder, and run maven install command. This would install the archetype into your local repository


mvn clean install


Step 3

Now you can create a new project from the archetype specifying the local catalog. All the local archetypes are listed, and selecte the FlexStruts-archetype option. Then the installer would ask input for the parameters like 'groupId', 'artifactId', 'version' and package. Provide the requested information and a new project would be created for you.

mvn archetype:generate -DarchetypeCatalog=local


See the following screen-shot for how the output looks.



Step 4

Go to the newly created project and run Maven install command and a War file would be created in the target folder for the web module of the project. Deploy this war in an application server and you can test the Flex Struts in action.


Resources



  1. Example Project FlexStruts.zip
  2. Maven User settings.xml
  3. Maven Archetype Project.

No comments: