25 February 2008
Intermediate
BlazeDS is a set of data services that significantly simplify the development of data-driven Rich Internet Applications, and dramatically improve the performance of their data access operations. In addition, BlazeDS enables the creation of a new breed of real-time data push and collaborative applications. BlazeDS is deployed as a set of JAR files as part of your Web application running in your Java EE application server or servlet container.
BlazeDS consists of three key services:
In this Getting Started tutorial, you will learn how to:
The BlazeDS turnkey server is a ready-to-use version of Tomcat (currently version 6.0.14) in which the BlazeDS data services have already been deployed along with sample applications.
Note: The goal of the turnkey server is to give developers an easy way to run samples out-of-the-box. In your real-life development or production environment, you would typically integrate BlazeDS in your own web application on your own application server.
To install the BlazeDS turnkey server:
Note: The instructions in this tutorial assume that you expand blazeds-turnkey-<version>.zip in /blazeds. If you expand the zip file anywhere else make sure you adjust the path provided in these instructions accordingly.
catalina run
Note: You can skip this step if Eclipse and the Flex Builder 3 plug-in are already installed on your system.
Because you will work on both the client side and the server side of the applications built in this tutorial, you need to use Flex Builder plug-in configuration and install Flex Builder 3 on top of Eclipse. You cannot use the Flex Builder standalone configuration because it is built on top of a minimal version of Eclipse that does not include the Java development environment. As a result it does not allow you to work on the Java-based server side of your applications.
To install the Flex Builder 3 plug-in:
You will need a Java project to work on the server side of the applications built in this tutorial. There are several ways you can set up Eclipse to work on the Java classes of a Web application. You can use a simple Java project, work with the Web Tools Platform (WTP), or use other plug-ins such as MyEclipse. In this tutorial, to avoid dependencies on a specific plug-in, we use a simple Java project.
This project configuration allows you to store the source code for your Java classes in the WEB-INF/src directory. These classes will automatically be compiled in the WEB-INF/classes directory.
The BlazeDS Message Service provides a publish/subscribe infrastructure that allows your Flex application to publish messages and subscribe to a set of messaging destinations, enabling the development of real-time data push and collaborative applications.
Follow these steps to build a simple chat application that demonstrates the BlazeDS Message Service.
A messaging destination represents a topic of real time conversation that interested parties can subscribe (listen) to or contribute to by posting their own messages.
To define the simple chat destination for this application:
<destination id="tutorial-chat"/>
A key element of a destination is the channel used to exchange data between the client and the server. Using BlazeDS, a messaging destination typically uses either a streaming or a polling channel.
Notice that there is no need to explicitly define a channel for the tutorial-chat destination. When you do not specify channels at the destination level, the destination uses the default channels defined at the top of the messaging-config.xml file. In this case, the client will first try to connect to the message service using the “my-streaming-amf” channel. If a connection to the server cannot be established using that channel, the client will fall back to the “my-polling-amf” channel. Channels themselves are configured in services-config.xml.
Root Folder: C:\blazeds\tomcat\webapps\samples
Root URL: http://localhost:8400/samples/
Context Root: /samples
In the newly created tutorial-chat project, open the main.mxml file located in the src folder, and implement the application as follows:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="consumer.subscribe()">
<mx:Script>
<![CDATA[import mx.messaging.events.MessageEvent;
import mx.messaging.messages.AsyncMessage;
private function send():void
{
var message:AsyncMessage = new AsyncMessage();
message.body.chatMessage = msg.text;
producer.send(message);
msg.text = "";
}
private function messageHandler(event:MessageEvent):void
{
log.text += event.message.body.chatMessage + "\n";
}
]]>
</mx:Script>
<mx:Producer id="producer" destination="tutorial-chat"/>
<mx:Consumer id="consumer" destination="tutorial-chat" message="messageHandler(event)"/>
<mx:Panel title="Chat" width="100%" height="100%">
<mx:TextArea id="log" width="100%" height="100%"/>
<mx:ControlBar>
<mx:TextInput id="msg" width="100%" enter="send()"/>
<mx:Button label="Send" click="send()"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
In this example, messages are published by Flex clients. The BlazeDS Message Service also provides a Java API that allows a server-side component to publish messages to a BlazeDS destination. A third option for exchanging messages between Flex and Java applications is to map destinations to Java Message Service (JMS) topics, enabling Flex clients to publish and subscribe to JMS topics.
Using the Remoting Service, your application can directly invoke methods of Java objects deployed in your application server, and consume the return value. The method can return a value of a primitive data type, an object, a collection of objects, an object graph, and more. Java objects returned by server-side methods are deserialized into either dynamic or typed ActionScript objects.
This section describes how to build a simple inventory management application that demonstrates the BlazeDS Remoting Service.
Note: The turnkey server includes an HSQLDB database, which allows you to run the samples “out-of-the-box” without setting up a database. HSQLDB is a lightweight Java RDBMS that is particularly well-suited to run samples.
To start the sample database:
startdb
The server-side of this application uses the simple Data Access Object (DAO) and Value Object patterns:
package tutorial;
public class Product {
private int productId;
private String name;
private double price;
private int qty;
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getQty() {
return qty;
}
public void setQty(int qty) {
this.qty = qty;
}
}
package tutorial;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import flex.samples.ConnectionHelper;
import tutorial.Product;
public class ProductDAO {
public List getProducts() throws SQLException {
List list = new ArrayList();
Connection c = null;
try {
c = ConnectionHelper.getConnection();
Statement s = c.createStatement();
ResultSet rs = s.executeQuery("SELECT * FROM product ORDER BY name");
while (rs.next()) {
Product product = new Product();
product.setProductId(rs.getInt("product_id"));
product.setName(rs.getString("name"));
product.setPrice(rs.getDouble("price"));
product.setQty(rs.getInt("qty_in_stock"));
list.add(product);
}
} finally {
c.close();
}
return list;
}
public void update(Product product) throws SQLException {
Connection c = null;
try {
c = ConnectionHelper.getConnection();
PreparedStatement ps = c.prepareStatement(
"UPDATE product SET name=?, price=?, qty_in_stock=? WHERE product_id=?");
ps.setString(1, product.getName());
ps.setDouble(2, product.getPrice());
ps.setInt(3, product.getQty());
ps.setInt(4, product.getProductId());
} finally {
ConnectionHelper.close(c);
}
}
}
A Remoting destination exposes a Java class that your Flex application can invoke remotely. The destination id is a logical name that your Flex application uses to refer to the remote class, which eliminates the need to hardcode a reference to the fully qualified Java class name. This logical name is mapped to the Java class name as part of the destination configuration in remoting-config.xml.
To create a remoting destination for the ProductDAO class:
<destination id="tutorial-product">
<properties>
<source>tutorial.ProductDAO</source>
</properties>
</destination>
Root Folder: C:\blazeds\tomcat\webapps\samples
Root URL: http://localhost:8400/samples/
Context Root: /samples
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:RemoteObject id="ro" destination="tutorial-product"/>
<mx:DataGrid dataProvider="{ro.getProducts.lastResult}" width="100%" height="100%"/>
<mx:Button label="Get Data" click="ro.getProducts()"/>
</mx:Application>
RemoteObject calls are asynchronous. You use the result and fault events of the RemoteObject component to handle results and errors. To make the application more robust and better partitioned, modify the code as follows:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
[Bindable]
private var products:ArrayCollection;
private function resultHandler(event:ResultEvent):void
{
products = event.result as ArrayCollection;
}
private function faultHandler(event:FaultEvent):void
{
Alert.show(event.fault.faultString);
}
]]>
</mx:Script>
<mx:RemoteObject id="ro" destination="tutorial-product"
result="resultHandler(event)"
fault="faultHandler(event)"/>
<mx:DataGrid id="dg" dataProvider="{products}" width="100%" height="100%"/>
<mx:Button label="Get Data" click="ro.getProducts()"/>
</mx:Application>
In the application so far, the list of products returned by the getProducts() method is deserialized into dynamic objects. Sometimes, you may want to work with strongly typed objects. To work with typed objects in this application, first create the ActionScript version of the Product class created in step 1:
package
{
[Bindable]
[RemoteClass(alias="tutorial.Product")]
public class Product
{
public var productId:int;
public var name:String;
public var price:Number;
public var qty:int;
}
}
Notice that the code uses the [RemoteClass(alias="tutorial.Product")] annotation to map the ActionScript version of the Product class (Product.as) to the Java version (Product.java). As a result, Product objects returned by the getProducts() method of ProductDAO are deserialized into instances of the ActionScript Product class. Similarly, in the next step, the instance of the ActionScript Product class passed as an argument to the update method of the RemoteObject is deserialized into an instance of the Java version of the Product class at the server-side.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
private var product:Product;
private function update():void
{
product.name = productName.text;
product.price = Number(price.text);
product.qty = parseInt(qty.text);
ro.update(product);
}
]]>
</mx:Script>
<mx:RemoteObject id="ro" destination="tutorial-product"/>
<mx:DataGrid id="dg" dataProvider="{ro.getProducts.lastResult}" width="100%" height="100%"
change="product = Product(dg.selectedItem)"/>
<mx:Button label="Get Data" click="ro.getProducts()"/>
<mx:Form>
<mx:FormItem label="Name">
<mx:TextInput id="productName" text="{product.name}"/>
</mx:FormItem>
<mx:FormItem label="Price">
<mx:TextInput id="price" text="{product.price}"/>
</mx:FormItem>
<mx:FormItem label="Qty In Stock">
<mx:TextInput id="qty" text="{product.qty}"/>
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Update" click="update()"/>
</mx:FormItem>
</mx:Form>
</mx:Application>