Technical blog

September 11, 2009

Flex: mxml vs ActionScript

Filed under: flex — Tags: , — paawak @ 01:40

This might look rather silly, but still had to give in to temptation :). Since I had worked on Swings as well, had to see if I could code a create a popup window with ActionScript. Well, of course you can, but I am rather new to Flex and most of the tutorial, for some unknown reason, choose not to mention this topic. So I decided to give it a shot.

Below is a popup window coded in mxml. Its called ItemSelectDialog.mxml.

<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"  
    showCloseButton="true" close="close();">
 
    <mx:Script>
        <![CDATA[
            import mx.managers.PopUpManager;
 
            private function close():void {
                // Put any clean-up code here.
                PopUpManager.removePopUp(this);
            }
 
        ]]>
    </mx:Script>
 
 
	<mx:Label x="39" y="77" text="ItemName:"/>
	<mx:Label x="0" y="10" text="Item Search" width="400" textAlign="center" fontSize="18" fontWeight="bold"/>
	<mx:ComboBox x="140" y="75" id="itemNames">
	    <mx:ArrayCollection id="itemArray">
	    <mx:Object data="0" label="Item-0"/>
	    <mx:Object data="1" label="Item-1"/>
	    <mx:Object data="2" label="Item-2"/>
	    </mx:ArrayCollection>
	</mx:ComboBox>
	<mx:Button x="103" y="214" label="Cancel" id="cancel" click="close();"/>
	<mx:Button x="230" y="214" label="Add" id="add" click="close();"/>
 
</mx:TitleWindow>

This is how you invoke it from another mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" >
 
    <mx:Script>
        <![CDATA[
            import mx.managers.PopUpManager;
            import mx.core.IFlexDisplayObject;
 
            private function showAddItemDialog():void {
                // Create a modal TitleWindow container.
                var itemWindow:IFlexDisplayObject = PopUpManager.createPopUp(this, ItemSelectDialog, true);
            }
 
        ]]>
    </mx:Script>
 
	<mx:Button x="556" y="249" label="Add Item" id="addItem" click="showAddItemDialog();"/>
 
</mx:Application>

And this is how its ActionScript counterpart looks like. Its called ItemSelectionDialog.as.

package com.swayam.flex.exp {
 
    import  mx.containers.TitleWindow;
    import  mx.controls.Label;
    import  mx.controls.ComboBox;
    import  mx.controls.Button;
    import mx.managers.PopUpManager;
    import mx.events.CloseEvent;
    import flash.events.IEventDispatcher;
    import flash.events.MouseEvent;
 
    public class ItemSelectionDialog extends TitleWindow {
 
        [Bindable]
        private var itemsArray:Array = new Array();
 
        private var cbItemList:ComboBox;
 
        public function ItemSelectionDialog() {
 
            initComponents();
 
            showCloseButton=true;
 
            layout="absolute";
 
            width = 500;
            height = 300;
 
            addEventListener(CloseEvent.CLOSE, closeWindow);
 
        }
 
        private function initComponents():void {
 
            var lbTitle:Label = new Label();
            lbTitle.text = "Item Search";
            lbTitle.x = 0;
            lbTitle.y = 10;
            /*lbTitle.textAlign="center";
            lbTitle.fontSize=18;
            lbTitle.fontWeight="bold";*/
            addChild(lbTitle);
 
            var lbItemName:Label = new Label();
            lbItemName.text = "ItemName:";
            lbItemName.x = 39;
            lbItemName.y = 75;
            addChild(lbItemName);
 
            //put some elements in the combo
            var count:int = 0;
 
            for (; count < 10; count++) {
                var content:Object = new Object();
                content["label"] = "Item-" + count;
                content["data"] = count;
                itemsArray[count] = content;
            } 
 
            cbItemList = new ComboBox();
            cbItemList.x = 140;
            cbItemList.y = 75;
            cbItemList.dataProvider = itemsArray;
            addChild(cbItemList);
 
            var btCancel:Button = new Button();
            btCancel.label = "Cancel";
            btCancel.x = 103;
            btCancel.y = 214;
            btCancel.addEventListener(MouseEvent.CLICK, closeOnClick);
            addChild(btCancel);
 
            var btAdd:Button = new Button();
            btAdd.label = "Add";
            btAdd.x = 230;
            btAdd.y = 214;
            btAdd.addEventListener(MouseEvent.CLICK, addAndClose);
            addChild(btAdd);
 
        }
 
        private function closeWindow(event:CloseEvent):void {
            close();
        }
 
        private function closeOnClick(event:MouseEvent):void {
            close();
        }
 
        private function addAndClose(event:MouseEvent):void {
 
        	if (cbItemList.selectedItem != null) {
                close();
        	}
 
        }
 
        private function close():void {
            PopUpManager.removePopUp(this);
        }
 
    }
 
}

You can use it just like its mxml counterpart, just make sure that you import the class.

...
            private function showAddItemDialog():void {
                // Create a modal TitleWindow container.
                var itemWindow:IFlexDisplayObject = PopUpManager.createPopUp(this, ItemSelectionDialog, true);
            }
...

This is how it looks like:
popup-actionscript

September 10, 2009

Accessing Java objects with Flex remoting

Filed under: flex, java — Tags: , , , — paawak @ 20:23

This post talks about how we:

  1. Query the database through Flex by invoking a server side Java object (the ItemDao) by Flex remoting with Blaze DS.
  2. Displays the result, which is a java.util.List<Item> in a DataGrid and a ComboBox.

Now that we have successfully configured Spring with Flex, we would like to access server side Java objects, which we expose through Spring.

We will query Items from the database using Spring/Hibernate through the ItemDao.

This is how my Item.java looks like:

 
/**
 * 
 * @author paawak
 */
@Entity
public class Item implements Serializable {
 
    private static final long serialVersionUID = 340952899861L;
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    @Column(length = 20, unique = true)
    private String code;
 
    @Column(length = 15, unique = true)
    private String name;
 
    @Column(length = 50)
    private String description;
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getCode() {
        return code;
    }
 
    public void setCode(String code) {
        this.code = code;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
}

And this is how my ItemDao.java looks like:

/**
 * 
 * @author paawak
 */
public class ItemDao extends HibernateDaoSupport {
 
    @SuppressWarnings("unchecked")
    public List<Item> getAll() {
        return super.getHibernateTemplate().loadAll(Item.class);
    }
 
}

As is obvious, I configure the ItemDao through Spring and then expose it as below:

 
...
<flex:remoting-destination ref="itemDao"/>
...

The one thing to remember here is that, Flex handles remote access through call back mechanism. That is, the call to the Java object, is asynchronous. The call returns immediately. However, the result (or the error) is got asynchronously by registering suitable listeners.

The following code snippet illustrates this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    initialize="initData();">
 
    <mx:Script>
        <![CDATA[           
            import mx.controls.Alert;
            import mx.rpc.remoting.RemoteObject;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.events.FaultEvent;
            import mx.collections.ArrayCollection;
 
            [Bindable]
            private var itemsMultiArray:ArrayCollection;
 
            private function initData():void {
 
                //call the getAll() method on the itemDao remote object, which has been exposed
                var itemRO:RemoteObject = new RemoteObject();
                itemRO.destination = "itemDao";
                itemRO.addEventListener(ResultEvent.RESULT, getItemListResultHandler);
                itemRO.addEventListener(FaultEvent.FAULT, faultHandler);
                itemRO.getOperation("getAll").send();
 
            }
 
            private function getItemListResultHandler(event:ResultEvent):void {
 
                itemsMultiArray = (ArrayCollection)(event.result);
 
            }
 
 
            private function faultHandler (event:FaultEvent):void {
             // Deal with event.fault.faultString, etc.
                Alert.show(event.fault.faultString, 'Error');
            }
 
        ]]>
    </mx:Script>
 
	<mx:DataGrid id="itemDetails" width="511" dataProvider="{itemsMultiArray}">
		<mx:columns>
			<mx:DataGridColumn headerText="ID" dataField="id"/>
			<mx:DataGridColumn headerText="Name" dataField="name"/>
			<mx:DataGridColumn headerText="Code" dataField="code"/>
		</mx:columns>
	</mx:DataGrid>
 
 
</mx:Application>

This is how it looks like:
flex_remote_object-1

The most important thing to note here is that the result that is got back in the call back function, should be cast with the proper data type in Flex. This is the link which has the details under the head Converting data from Java to ActionScript. From here we get to know the correct casting. In this case, we are casting the result into mx.collections.ArrayCollection since it is of java.util.List type.

Also note that though each element in the array is of the type Item, it can be accessed by its bean attribute name. That is how we can access the id, name and code in the DataGrid. Note that you cannot invoke the getter method like getId() on the object, it will not work.

So far, we have used a DataGrid which can extract data based on the attribute names. What if we use a ComboBox which needs a two dimensional array with data and label attributes? This is the code snippet of the modified getItemListResultHandler():

...
           [Bindable]
            private var itemComboArray:Array = new Array();
...
            private function getItemListResultHandler(event:ResultEvent):void {
 
                itemsMultiArray = (ArrayCollection)(event.result);
 
                var count:int = 0;
 
                for each (var item:Object in itemsMultiArray) {
                    var content:Object = new Object();
                    content["label"] = item.name;
                    content["data"] = item.id;
                    itemComboArray[count++] = content;
                } 
 
            }

And this is the code to add the ComboBox:

...
<mx:ComboBox id="itemCombo"  dataProvider="{itemComboArray}"></mx:ComboBox>
...

The screen looks like this now:
flex_remote_object-2

August 24, 2009

Integrating Spring (MVC) and BlazeDS (flex)

Filed under: flex, java, spring — Tags: , , , — paawak @ 22:33

Disclaimer:

I am not an expert in either Spring MVC or Flex and this article is not about fundamentals of Flex/BlazeDS. Its just how to get it working. Quickly.

Prologue:

I was playing around with Flex for the past couple of days. Being a Java developer, I was naturally inclined towards trying out BlazeDS so that I can use Flex as a front-end to my Java backend. The thing that I suffered from most was a huge information deluge. I was flooded by loads of blogs, articles and tutorials which made my life miserable. Finally, I saw a saviour in: Bare Bones BlazeDS Object Remoting, which explains how to create a BlazeDS and Java integration. Of course this does not include spring integration. Then I downloaded Spring BlazeDS, and went through the samples. Then I kind of combined these two and figured out how to make it work. The challenge here is that I have existing screens which are pure Spring MVC stuff. I am trying to embed flex in some of them, and have BlazeDS integration with my existing spring beans. Read on…

First things first: directory structure

My project directory looks like:

directory-structure1

Details:

  1. src/main/java contains all my java files
  2. src/main/resources has all config files like hibernate.cfg.xml, property files, etc.
  3. src/main/webapp has the WEB-INF folder and the jsps
  4. src/main/webapp/flash has all the compiled .swf files to be embedded in the jsps
  5. src/main/flex has all the .mxml and action script files

Config files required for BlazeDS

You would need these three config files under the WEB-INF/flex directory:

services-config.xml

This is the most important file. This defines the various channels that would be used for client/server communication by the BlazeDS

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
	<services>
		<service-include file-path="remoting-config.xml" />
		<default-channels>
			<channel ref="my-amf" />
		</default-channels>
	</services>
 
	<channels>
		<channel-definition id="my-amf"
			class="mx.messaging.channels.AMFChannel">
			<endpoint
				url="http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf"
				class="flex.messaging.endpoints.AMFEndpoint" />
			<properties>
				<polling-enabled>false</polling-enabled>
			</properties>
		</channel-definition>
	</channels>
 
	<logging>
		<!-- You may also use flex.messaging.log.ServletLogTarget -->
		<target class="flex.messaging.log.ConsoleTarget" level="info">
			<properties>
				<prefix>[BlazeDS] </prefix>
				<includeDate>false</includeDate>
				<includeTime>false</includeTime>
				<includeLevel>true</includeLevel>
				<includeCategory>false</includeCategory>
			</properties>
			<filters>
				<pattern>Endpoint.*</pattern>
				<pattern>Service.*</pattern>
				<pattern>Configuration</pattern>
			</filters>
		</target>
	</logging>
 
</services-config>

remoting-config.xml

This defines the remoting adapter used by the flex client.

<?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="my-amf"/>
    </default-channels>
 
  </service>

BlazeDS/Spring integration

To integrate Spring with BalzeDS, you have to touch upon these existing files:

web.xml

1. Adding the flex listener

Add the flex.messaging.HttpFlexSession listener.

2. URL mapping

Note that in the services-config.xml, the url of the default channel my-amf is http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf. You have to pass any url with this pattern to the Spring front end handler servlet, the org.springframework.web.servlet.DispatcherServlet. I assume that you already have a similar servlet defined for your Spring MVC. You should use the same servlet (so that you can re-use the same beans in flex) to map these urls. I am assuming the name of the existing DispatcherServlet is dispatcher.

This is how the modified web.xml looks like:

...
    <!-- *******************     START flex       **************************** -->
 
    <!-- Http Flex Session attribute and binding listener support -->
    <listener>
        <listener-class>flex.messaging.HttpFlexSession</listener-class>
    </listener>
 
    <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
 
    <!-- Map all *.spring requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/spring/messagebroker/*</url-pattern>
    </servlet-mapping>    
 
    <!-- *******************     END flex       **************************** -->
...

dispatcher-servlet.xml

This xml already has the existing Spring MVC beans. This has to be modified to expose existing beans to the BlazeDS remoting as services. The xml header has to be modified as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:flex="http://www.springframework.org/schema/flex"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/flex 
           http://www.springframework.org/schema/flex/spring-flex-1.0.xsd"
           default-lazy-init="true">
 
...

Then, the body has to be modified by adding:

...
    <!-- The default id of this bean is *_messageBroker*. It takes all config files by default.-->
    <flex:message-broker/>
    <bean class="org.springframework.flex.servlet.MessageBrokerHandlerAdapter"/>
 
    <!-- if this is not present, it gives a big exception -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!-- Maps all flex requests from blaze-ds to the flex message broker. You can re-use an existing bean instance -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <value>                                
                /messagebroker/*=_messageBroker
            </value>
        </property>
        <property name="order" value="0"/>
    </bean>
...

Finally, this is how you expose a bean as a service for remoting:

...
    <bean id="accountGroupManager" class="com.swayam.exp.AccountGroupManager"/>       
    <!-- Expose spring bean to blazeds for remoting. This will be available as a remote object with an id *account* -->
    <flex:remoting-destination ref="accountGroupManager" destination-id="account" />
...

This is how the AccountGroupManager looks like:

public class AccountGroupManager {
 
 
    public AccountGroupManager() {
 
        System.out
                .println("*****************AccountGroupManager.AccountGroupManager()");
    }
 
    public String save(String name, String desc) {        
// dummy code, returning static string
        return "40";
    }
 
 
}

Coding the mxml

I am using eclipse to code the NewAccountGroup.mxml. This is how it looks like:

<?xml version="1.0" encoding="UTF-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
 
<!-- Associate client with BlazeDS destination via RemoteObject. -->
<mx:RemoteObject id="accountGroupRemote" destination="account" />
 
    <mx:Form>
        <mx:FormItem label="Group Name:">
            <mx:TextInput id="groupName"/>
        </mx:FormItem>
 
        <mx:FormItem label="Description:">
            <mx:TextInput id="description"/>
        </mx:FormItem>
 
        <mx:FormItem>
            <mx:Button label="Submit" click="accountGroupRemote.save(groupName.text, description.text);" />
        </mx:FormItem>
 
        <mx:FormItem label="Server's Response">
            <mx:Label text="{accountGroupRemote.save.lastResult}" />
        </mx:FormItem>
 
    </mx:Form>
</mx:Application>

Compiling mxmls to swf

The one thing you have to keep in mind is that, you have to compile the mxmls along with the services-config.xml. Note that you have something like url=”http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf” in there. While the server.name and server.port will be taken by default by the client, you have to specify the context.root during the compile time, as an argument. This is the context of your webapp. I am using the mxmlc that comes with the Flex SDK from the command prompt (you can add it to the PATH). First you cd to the src/main/flex directory. Here you go:

mxmlc -services ../webapp/WEB-INF/flex/services-config.xml -strict=true -debug=true -context-root=/ -show-actionscript-warnings=true -output ../webapp/flash/NewAccountGroup.swf NewAccountGroup.mxml

Note how you specify the services-config.xml.

So far so good. Now we will have to embed the generated flash into our jsp. This how it is done:

...
<p>
 
    <object id="NewAccountGroup" align="middle"
    codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" 
    classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
    <param name="allowScriptAccess" value="sameDomain" />
    <param name="quality" value="high" />
    <param name="bgcolor" value="#ffffff" />
    <embed src="/flash/NewAccountGroup.swf" width="1000" height="400"
    quality="high" bgcolor="#ffffff" name="NewAccountGroup" align="middle" 
    allowScriptAccess="sameDomain" type="application/x-shockwave-flash" 
    pluginspage="http://www.macromedia.com/go/getflashplayer" />
    </object>
 
</p>
 
...

Finally, just a word for Maven users, these are the arifacts you have to use:

...
    <dependencies>
...
        <!-- *******************     START flex       **************************** -->
 
        <!-- blaze ds -->
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-common</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-core</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-opt</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-proxy</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-remoting</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
 
        <!-- spring-flex -->
        <dependency>
            <groupId>org.springframework.flex</groupId>
            <artifactId>spring-flex</artifactId>
            <version>${spring.flex.version}</version>
        </dependency>
 
        <dependency>
			<groupId>xalan</groupId>
			<artifactId>xalan</artifactId>
			<version>${xalan.version}</version>
        </dependency>
 
        <dependency>
            <groupId>edu.oswego.util</groupId>
            <artifactId>concurrent</artifactId>
            <version>${concurrent.version}</version>
        </dependency>
 
        <!-- *******************     END flex       **************************** -->
...        
    </dependencies>
...
    <properties>
        <blazeds.version>3.2.0.3978</blazeds.version>
        <spring.flex.version>1.0.0.RELEASE</spring.flex.version>
        <xalan.version>2.7.0</xalan.version>
        <concurrent.version>1.3.3</concurrent.version>
    </properties>
...

For others, you can always expand the blazeds.war and put all the jars inside the WEB-INF/lib.

The only pain point for this approach is that every time you change your context root, you have to recompile all your flex against the services-config.xml along with a new context.root argument.

This is how the end screen looks like (in the browser):

newaccountgroup

Powered by WordPress