Thinking in Java And Flex

Michael Niu’s Technical Weblog

Mate-Tag Base Framework

Mate will become another hot pot in Flex area . It use configuration to call the service and handle the event. It also uses configuration to update the Bindable object. Somehow The Mate is The “springframework” in Flex Area.

There are two architecture level diagrams of Mate. One is from Yakov Fain of Farata Systems, Another is from Mate funder company ASFusion. I like the later one:

Lets use mate to develop the buddyList application

1.Create the core component of mate —-EventMap.

The EventMap is the heart of mate it glues all the other component and control the process:

BuddyListEventMap.mxml:

<?xml version=”1.0″ encoding=”utf-8″?>

 

<EventMap

 

xmlns=”http://mate.asfusion.com/

xmlns:mx=http://www.adobe.com/2006/mxml>

</EventMap>

 

 

I will add the related code later. Now we need tell the main app that initialize the EventMap:

Flex_Mate.mxml:

<?xml version=”1.0″ encoding=”utf-8″?>

 

<mx:Application

 

xmlns:map=”com.ny.flex.mate.map.*” xmlns:views=”com.ny.flex.mate.views.*” xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute>

<mx:Script>

<![CDATA[

[

 

Bindable

]

 

public var

viewStackSelectedIndex :int = 0;

]]>

 

</mx:Script>

<map:BuddyListEventMap/>

 

 

<mx:HBox horizontalAlign=”center” verticalAlign=”top” width=”100%” height=”100%” y=”0” x=”0>

 

 

<mx:ViewStack id=”viewStack” resizeToContent=”true” selectedIndex=”{viewStackSelectedIndex}>

 

 

<views:LoginView />

 

 

<views:BuddyListView/>

 

 

</mx:ViewStack>

 

 

</mx:HBox>

</mx:Application>

 

 

2. Create the LoginView :

<?xml version=”1.0″ encoding=”utf-8″?>

 

<mx:Panel

 

xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”300” height=”200” horizontalAlign=”center” verticalAlign=”middle” title=”Flex Cirngorm Login>

 

<mx:Script>

<![CDATA[

 

import

com.ny.flex.mate.event.LoginEvent; 

 

import

com.ny.flex.mate.vo.User; 

 

import

mx.validators.Validator; 

 

private function login():void

{ 

 

if

(Validator.validateAll(validators).length == 0){ 

 

var loginUser:User = new

User();

loginUser.userName=username.text;

loginUser.password=password.text;

 

var loginEvent:LoginEvent = new

LoginEvent(LoginEvent.LOGIN);

loginEvent.loginUser = loginUser;

dispatchEvent(loginEvent);

}

}

]]>

 

</mx:Script>

 

<!– Validators–>

 

 

<mx:Array id=”validators>

 

 

<mx:StringValidator id=”userNameValidator” source=”{username}” property=”text” required=”true/>

 

 

<mx:StringValidator id=”passwordValidator” source=”{password}” property=”text” required=”true/>

 

 

</mx:Array>

 

 

<mx:Form

 

id=”loginForm” x=”0” y=”0>

 

 

<mx:FormItem label=”Username:>

 

 

<mx:TextInput id=”username/>

 

 

</mx:FormItem>

 

 

<mx:FormItem label=”Password:>

 

 

<mx:TextInput id=”password” displayAsPassword=”true/>

 

 

</mx:FormItem>

 

 

<mx:FormItem direction=”horizontal” verticalGap=”15” paddingTop=”5” width=”170>

 

 

<mx:Button id=”loginBtn” label=”Login” click=”login()”/>

 

 

</mx:FormItem>

 

 

</mx:Form>

</mx:Panel>

 

 

See the action method above the LoginEvent is dispatched. Now create the LoginEvent:

 

package

 

import

com.ny.flex.mate.vo.User;

 

import

flash.events.Event;

 

public class LoginEvent extends

Event

{

 

public static const LOGIN:String = “login”

; 

 

public var

loginUser:User; 

 

public function LoginEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false

)

{

 

super

(type, bubbles, cancelable);

}

}

}

 

com.ny.flex.mate.event

{

 

The “bubbles” has to be “true”, it makes EventMap can handle it. The magic of mate comes in the EventMap . The LoginEvent can handle by tag :

…..

 

<EventHandlers type=”{LoginEvent.LOGIN}>

 

 

<RemoteObjectInvoker destination=”flexmvcRO” method=”authenticate

arguments=”{event.loginUser}>

 

 

<resultHandlers>

 

 

<MethodInvoker generator=”{LoginService}

method=”onResult_Authenticate

arguments=”{resultObject}/>

 

 

</resultHandlers>

 

 

</RemoteObjectInvoker>

 

 

</EventHandlers>

……

 

In the EvevntHandler , You can define the service call function(RemoteObjectInvoker here) . and It also define the result handler specify the service class ,method and arguments.

Lets see the LoginService object:

package

 

com.ny.flex.mate.service

{

 

import

com.ny.flex.mate.vo.User;

 

public class

LoginService

{

[

 

Bindable

] 

 

public var

authUserName:String; 

[

 

Bindable

] 

 

public var

viewStackSelectedIndex:int ;

 

public function onResult_Authenticate(user:User):void

{

authUserName = user.userName;

viewStackSelectedIndex = 1;

}

}

}

 

 

 

 

The service class handle the result and make the return object bindable. now we need update target views. Another shine point of mate is Injecting the bindable object to the target view! you just need add another tag in EventMap class . (This is damn cool):

 

<Injectors target=”{BuddyListView}>

 

 

<PropertyInjector targetKey=”authUserName” source=”{LoginService}

sourceKey=”authUserName/>

 

 

</Injectors>

 

<Injectors target=”{Flex_Mate}>

 

 

<PropertyInjector targetKey=”viewStackSelectedIndex” source=”{LoginService}

sourceKey=”viewStackSelectedIndex/>

 

 

</Injectors>

 

 

 

You just need specify the target view, taget key , source service object and source key!

Finally lets define the target view :BuddyListView:

<?xml version=”1.0″ encoding=”utf-8″?>

 

<mx:Panel

 

xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Buddy List of {authUserName} creationComplete=”getBuddyList()” width=”500” height=”320>

<mx:Script>

<![CDATA[

 

import

mx.collections.ArrayCollection; 

 

import

com.ny.flex.mate.event.GetBuddyListEvent; 

[

 

Bindable

] 

 

public var

authUserName:String; 

[

 

Bindable

] 

 

public var

buddyCollection:ArrayCollection;

 

private function getBuddyList():void

{ 

 

var getBuddyListEvent:GetBuddyListEvent = new

GetBuddyListEvent(GetBuddyListEvent.GET_BUDDY_LIST);

getBuddyListEvent.authUserName = authUserName;

dispatchEvent(getBuddyListEvent);

}

]]>

 

</mx:Script>

 

<mx:DataGrid id=”buddyList” dataProvider=”{buddyCollection}” borderStyle=”none” width=”100%” height=”100%>

 

 

<mx:columns>

 

 

<mx:DataGridColumn dataField=”firstName” headerText=”First Name/>

 

 

<mx:DataGridColumn dataField=”lastName” headerText=”Last Name/>

 

 

</mx:columns>

 

</mx:DataGrid>

</mx:Panel>

 

Checkout the “{autherUsername}”, it comes from the LoginService.

You probably wonder about “buddyListCollection”. It means another around development:

Action–>Dispatch Event–>Config Handler–>create service–>Inject Bindable Object –>another Action….

All the other code is blow:

GetBuddyListEvent.as:

 

package

 

import

flash.events.Event;

 

public class GetBuddyListEvent extends

Event

{

 

public static const GET_BUDDY_LIST =“getAllFriends”

; 

 

public var

authUserName:String;

 

public function GetBuddyListEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false

)

{

 

super

(type, bubbles, cancelable);

}

}

}

 

com.ny.flex.mate.event

{

 

BuddyService.as:

 

package

 

import

mx.collections.ArrayCollection;

 

public class

BuddyService

{

[

 

Bindable

] 

 

public var

buddyList:ArrayCollection; 

 

public function

onResult_GetBuddyList(buddies:ArrayCollection){

buddyList = buddies;

}

}

}

 

com.ny.flex.mate.service

{

 

The completely EventMap file: BuddyListEventMap.mxml

<?xml version=”1.0″ encoding=”utf-8″?>

 

<EventMap

 

xmlns=”http://mate.asfusion.com/” xmlns:mx=”http://www.adobe.com/2006/mxml>

 

<mx:Script>

<![CDATA[

 

import

com.ny.flex.mate.event.BDbbdEvent; 

 

import

com.ny.flex.mate.service.BuddyService; 

 

import

com.ny.flex.mate.event.GetBuddyListEvent; 

 

import

com.ny.flex.mate.views.BuddyListView; 

 

import

com.ny.flex.mate.service.LoginService; 

 

import

com.ny.flex.mate.event.LoginEvent;

]]>

 

</mx:Script>

 

 

<EventHandlers type=”{LoginEvent.LOGIN}>

 

 

<RemoteObjectInvoker destination=”flexmvcRO” method=”authenticate” arguments=”{event.loginUser}>

 

 

<resultHandlers>

 

 

<MethodInvoker generator=”{LoginService}” method=”onResult_Authenticate” arguments=”{resultObject}/>

 

 

</resultHandlers>

 

 

</RemoteObjectInvoker>

 

 

</EventHandlers>

 

 

<EventHandlers type=”{GetBuddyListEvent.GET_BUDDY_LIST}>

 

 

<RemoteObjectInvoker destination=”flexmvcRO” method=”{GetBuddyListEvent.GET_BUDDY_LIST}” arguments=”{event.authUserName}>

 

 

<resultHandlers>

 

 

<MethodInvoker generator=”{BuddyService}” method=”onResult_GetBuddyList” arguments=”{resultObject}/>

 

 

</resultHandlers>

 

 

</RemoteObjectInvoker>

 

 

</EventHandlers>

 

<Injectors target=”{BuddyListView}>

 

 

<PropertyInjector targetKey=”authUserName” source=”{LoginService}” sourceKey=”authUserName/>

 

 

<PropertyInjector targetKey=”buddyCollection” source=”{BuddyService}” sourceKey=”buddyList/>

 

 

</Injectors>

 

<Injectors target=”{Flex_Mate}>

 

 

<PropertyInjector targetKey=”viewStackSelectedIndex” source=”{LoginService}” sourceKey=”viewStackSelectedIndex/>

 

 

</Injectors>

</EventMap>

 

 

Summary

I discussed  flex development  in  last 5 blogs. What is  the best choice  ?

I think  the central management  will be  NO1. choice for the small project. Because  you dont need   learn any  new framework  and  you still  get clear architecture .

For the  mate and Cairngorm,   Mate seems  win the conpetition  now. Because

For Cairngorm:

1.   Cairngorm  is too complicate  and  study cost is higher.

2.  Cairngorm has some Junk code for my  opionion, ie.frontcontrollerI.

For  Mate,

1 . It is  simpler than  Cairngorm, and easier to study.

2. You get benefit from  EventMap, Because  you dont need  write ther glue code between  event and the  service.

3.You get pain from EventMap.  Just imagine  you have  50 actions and 100 objects to bind. you have to put  lots of   configration  stuff in your EventMap, Then EventMap become the disastor!

So  if you have to use  cairngorm   use  my solution  Cairngorm without FrontContoller.

For the mate,   if they can add  the Meta Tag instead of  the EventMap. i.e.:

[EventHadler ={name ="myHandler", serviceclass="myservice" result , taget ...}]

MateDispatch(myevent).

So  you dont need   config anything,  I am waiting for  this.

 

 

 

 

July 29, 2008 Posted by | Flex | Leave a Comment

Cut It Off-Cairngorm without front controller

As we know when you use Cairngorm, Every action of view need dispatch an event. Every dispatched event need to create command to handle the event. And you have to add the mapping them in the frontcontroller. For example:

Loginvew.xml —> action login() —>dispatch LoginEvent —>Handled by LoginCommand —>mapping LoginEvent and LoginCommand in FrontController.

When you create any new action , you have to create 2 classes and modify front controller, assume the business logic code is same .

So How to simplify Cairngorm? My solution is cut of frontcontroller. The architecture is :

As above diagram show, The view will not dispatch any event. It will service façade directly. The façade will call business delegate to communicate with the server side solution(i.e. remote object). Then the delegate will handle the result and update Model locator. Finally Model locator will update the result view by Binding.

Lets see the code ;

ServiceFacade.as:

package com.ny.flex.cairngorm.no_fc.service

{

import com.ny.flex.cairngorm.no_fc.vo.User;

public class ServiceFacade

{

private static var _serviceFacade:ServiceFacade = null;

public function ServiceFacade(privateClass:PrivateClass)

{

if(ServiceFacade._serviceFacade == null){

ServiceFacade._serviceFacade = this;

}

}

public static function getInstance():ServiceFacade {

if(_serviceFacade == null){

_serviceFacade =

new ServiceFacade(new PrivateClass);

}

return _serviceFacade;

}

public function authenticate(user:User):void{

LoginDelegate.getInstance().authenticate(user);

}

public function getBuddyList():void{

BuddyListDelegate.getInstance().getBuddyList();

}

}

}

class PrivateClass{}

 

ServiceFacade provides unify interface of all business logic. And the action of view will only call the façade . for example login action in Loginvew.xml :

private function login():void{

if(Validator.validateAll(validators).length == 0){

var loginUser:User = new User();

loginUser.userName=username.text;

loginUser.password=password.text;

serviceFacade.authenticate(loginUser);

}

}

 

 

Lets see the function serviceFacade.authenticate(loginUser):

public function authenticate(user:User):void{

LoginDelegate.getInstance().authenticate(user);

}

 

 

It calls the function “authenticate(user)” in LoginDelegate:

public function authenticate(user:User):void{

var responder:IResponder =
new Responder(onResult_Authenticate,fault);

var call:Object = service.authenticate(user);

call.addResponder(responder);

}

private function onResult_Authenticate(event:ResultEvent)

:void{

var authUser:User = event.result as User;

model.loginUser = authUser;

model.viewStackSelectedIndex = 1;

}

 

 

As above code shows the delegate will call the data source , handle the result and update model locator.

Lets see the completely code from LoginView to LoginDelegate:

BaseView.mxml:

package com.ny.flex.cairngorm.no_fc.service

{

import com.ny.flex.cairngorm.no_fc.vo.User;

public class ServiceFacade

{

private static var _serviceFacade:ServiceFacade = null;

public function ServiceFacade(privateClass:PrivateClass)

{

if(ServiceFacade._serviceFacade == null){

ServiceFacade._serviceFacade = this;

}

}

public static function getInstance():ServiceFacade {

if(_serviceFacade == null){

_serviceFacade =

new ServiceFacade(new PrivateClass);

}

return _serviceFacade;

}

public function authenticate(user:User):void{

LoginDelegate.getInstance().authenticate(user);

}

public function getBuddyList():void{

BuddyListDelegate.getInstance().getBuddyList();

}

}

}

class PrivateClass{}

The façade will locate the concrete Business delegate method :

LoginDelegate.as:

package com.ny.flex.cairngorm.no_fc.service

{

import com.ny.flex.cairngorm.no_fc.*;

import com.ny.flex.cairngorm.no_fc.vo.User;

import mx.rpc.IResponder;

import mx.rpc.Responder;

import mx.rpc.events.ResultEvent;

public class LoginDelegate extends BaseDelegate

{

private static var _loginDelegate:LoginDelegate = null;

public function LoginDelegate(privateClass:PrivateClass){

if(LoginDelegate._loginDelegate == null ){

LoginDelegate._loginDelegate = this;

}

}

public static function getInstance():LoginDelegate{

if(_loginDelegate == null){

_loginDelegate = new LoginDelegate(new PrivateClass);

}

return _loginDelegate;

}

public function authenticate(user:User):void{

var responder:IResponder = new Responder(onResult_Authenticate,fault);

var call:Object = service.authenticate(user);

call.addResponder(responder);

}

private function onResult_Authenticate(event:ResultEvent):void{

var authUser:User = event.result as User;

model.loginUser = authUser;

model.viewStackSelectedIndex = 1;

}

}

}

class PrivateClass{}

 

 

This solution simplify Cairngorm framework. And It makes everthing understandable. But the shortage is obviously. It broke some design patten rules Decouple the application as much as possible. It couple the action to the Service Façade.

Lets move the next part:Mate-Tag Base Framework,

  

July 28, 2008 Posted by | Flex | 1 Comment

Flex MVC-Cairngorm

Do you ever hear a weird word “Cairngorm”? if your answer is no. Please look around if you live in a cageJ

A MVC Framework in Flex area which named by a mountain of Scotland. (Damn it, If I create my own Flex framework, I will call it “TianAnMen”).

There are tons of articles and diagram to discuss about . This is my architecture diagram:

The first step of using Cairngorm should be creating the backbone of frame work. It includes 3 objects:

Model Locater.

Service Locator;

Front Controller.

Model Locator carries all the transport information between the components It is a Bindable object;

Service Locator defines the interface to communicate with datasource(Httpservice, Webservice,Remoteobject ) .

Front Controller builds the mapping between the Dispatched event and Command ;

Lets see the related code of BuddyList app:

BuddyAppModelLocator.as:

package com.ny.flex.cairngorm.model

{

import com.ny.flex.cairngorm.vo.User;

import mx.collections.ArrayCollection;

[Bindable]

public class BuddyAppModelLocator

{

public var buddyList:ArrayCollection=new ArrayCollection();

public var loginUser:User=new User();

public var viewStackSelectedIndex :int = 0;

static private var __instance:BuddyAppModelLocator=null;

static public function getInstance():BuddyAppModelLocator

{

if(__instance == null)

{

__instance=new BuddyAppModelLocator();

}

return __instance;

}

}

}

Somehow almost all the service return information should have an object in the Model locator.

BuddyServiceLocator.mxml:

<?xml version=”1.0″ encoding=”utf-8″?>

<cairngorm:ServiceLocator xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:cairngorm=”http://www.adobe.com/2006/cairngorm>

<mx:RemoteObject id=”buddyRo destination=”flexmvcRO>

</mx:RemoteObject>

</cairngorm:ServiceLocator>

It defines remoteobject in this example . it should match the destination in remote_config.xml;

BuddyListController.as:

package com.ny.flex.cairngorm.control

{

import com.adobe.cairngorm.control.FrontController;

import com.ny.flex.cairngorm.command.GetBuddyListCommand;

import com.ny.flex.cairngorm.command.LoginCommand;

import com.ny.flex.cairngorm.event.GetBuddyListEvent;

import com.ny.flex.cairngorm.event.LoginEvent;

public class BuddyListController extends FrontController

{

public function BuddyListController()

{

super();

addCommand(LoginEvent.LOGIN_EVENT,LoginCommand);

addCommand(GetBuddyListEvent.GET_BUDDY_LIST_EVENT,

GetBuddyListCommand);

}

}

}

Obviously it is a mapping house of event and command.

How to glue this junk together? The magic is in Main application view :

BuddList_Main_Cairngorm.mxml:

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml xmlns:service=”com.ny.flex.cairngorm.service.* xmlns:controller=”com.ny.flex.cairngorm.control.*” xmlns:views=”com.ny.flex.cairngorm.views.*” layout=”absolute width=”100%” height=”100%>

<mx:Script>

<![CDATA[

import com.ny.flex.cairngorm.model.BuddyAppModelLocator;

[Bindable]

public var myModel:BuddyAppModelLocator = BuddyAppModelLocator.getInstance();

]]>

</mx:Script>

<service:BuddyServiceLocator id=”myservice/>

<controller:BuddyListController id=”myController/>

<mx:HBox horizontalAlign=”center” verticalAlign=”top width=”100%” height=”100%” y=”0” x=”0>

<mx:ViewStack id=”viewStack resizeToContent=”true” selectedIndex=”{myModel.viewStackSelectedIndex}>

<views:LoginView />

<views:BuddyListView/>

</mx:ViewStack>

</mx:HBox>

</mx:Application>

Now you can create the view component and dispatch the event from it.

LoginView.xml

<![CDATA[

import com.ny.flex.cairngorm.event.LoginEvent;

import com.ny.flex.cairngorm.vo.User;

import mx.validators.Validator;

private function login():void{

if(Validator.validateAll(validators).length == 0){

var loginUser:User = new User();

loginUser.userName=username.text;

loginUser.password=password.text;

var loginEvent:LoginEvent = new LoginEvent();

loginEvent.loginUser = loginUser;

loginEvent.dispatch();

}

}

]]>

</mx:Script>

<!– Validators–>

<mx:Array id=”validators>

<mx:StringValidator id=”userNameValidator” source=”{username} property=”text required=”true/>

<mx:StringValidator id=”passwordValidator” source=”{password} property=”text” required=”true/>

</mx:Array>

<mx:Form id=”loginForm” x=”0” y=”0>

<mx:FormItem label=”Username:>

<mx:TextInput id=”username/>

</mx:FormItem>

<mx:FormItem label=”Password:>

<mx:TextInput id=”password” displayAsPassword=”true/>

</mx:FormItem>

<mx:FormItem direction=”horizontal” verticalGap=”15” paddingTop=”5” width=”170>

<mx:Button id=”loginBtn” label=”Loginclick=”login()”/>

</mx:FormItem>

</mx:Form>

</mx:Panel>

Typically every action of view will need create a event:

LoginEvent.as:

package com.ny.flex.cairngorm.event

{

import com.adobe.cairngorm.control.CairngormEvent;

import com.ny.flex.cairngorm.vo.User;

import flash.events.Event;

public class LoginEvent extends CairngormEvent

{

public static var LOGIN_EVENT:String = “loginEvent”

public var loginUser:User ;

public function LoginEvent()

{

super(LOGIN_EVENT);

}

override public function clone() : Event

{

return new LoginEvent();

}

}

}

And Every Event has to be mapped to a Command :

LoginCommand.as:

package com.ny.flex.cairngorm.command

{

import com.adobe.cairngorm.commands.ICommand;

import com.adobe.cairngorm.control.CairngormEvent;

import com.ny.flex.cairngorm.event.LoginEvent;

import com.ny.flex.cairngorm.model.BuddyAppModelLocator;

import com.ny.flex.cairngorm.service.LoginDelegate;

import com.ny.flex.cairngorm.vo.User;

import mx.controls.Alert;

import mx.rpc.IResponder;

public class LoginCommand implements ICommand, IResponder

{

public function LoginCommand()

{

}

public function execute(event:CairngormEvent):void

{

var loginEvent:LoginEvent = LoginEvent(event);

var user:User = loginEvent.loginUser;

var lgoinService :LoginDelegate

= new LoginDelegate(this);

lgoinService.authenticate(user);

}

public function result(event:Object):void

{

var authUser:User = User(event.result);

BuddyAppModelLocator.getInstance().loginUser = authUser;

BuddyAppModelLocator.getInstance().viewStackSelectedIndex=1;

}

public function fault(info:Object):void

{

Alert.show(“Login Fail Error “);

}

}

}

Then Do the mapping thing in the front controller.

addCommand(LoginEvent.LOGIN_EVENT,LoginCommand);

The command has to do the business: you have to add your business logic code in execute method :

var lgoinService :LoginDelegate

= new LoginDelegate(this);

lgoinService.authenticate(user);

The delegate is used to call the data source through the service locator :

LoginDelegate.as

package com.ny.flex.cairngorm.service

{

import com.adobe.cairngorm.business.ServiceLocator;

import com.ny.flex.cairngorm.vo.User;

import mx.rpc.IResponder;

public class LoginDelegate

{

private var responder:IResponder;

private var service:Object;

public function LoginDelegate(responder :IResponder){

this.service =

ServiceLocator.getInstance().getRemoteObject(“buddyRo”);

this.responder = responder;

}

public function authenticate(user:User):void{

var call:Object = service.authenticate(user);

call.addResponder(responder);

}

}

}

The result will be responded in the result method of The command, the model will be updated and binded to the result view:

public function result(event:Object):void

{

var authUser:User = User(event.result);

BuddyAppModelLocator.getInstance().loginUser

= authUser;

BuddyAppModelLocator.getInstance().viewStackSelectedIndex=1;

}

The other view work flow is similar to the above… The FB Project structure like :

Cairngorm makes the application development layable, testable. And make the developer more professional Geek J.

But it is complicate and hard to study and maintain. Is there any way simplify it?

Lets move to : Cut It Off-Cairngorm without front controller.

July 25, 2008 Posted by | Flex | | 2 Comments

Flex Central Management

As I discussed  in the  Flex Chaos-All-in-one.  Put  everything  together is not  good solution  for  big project. That means  we need to take the business logic away from  the UI.

The Central Management  use  a remote object manager to control the  communication between the  Flex and Back-end . The  architechture  is :

 

As the above graph shows, Every UI component will call  a service first, and the service class will call the Central manager class, The central manager class will call the Server side solution .Then  Global Object manager will be used to transfer data  between UIs.

Lets  see the  simple buddy list application on this way.

LoginView.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”300″ height=”200″ horizontalAlign=”center” verticalAlign=”middle” title=”Flex Central Manager Login”>
<mx:Script>
 <![CDATA[
  import com.ny.flex.centralManagement.service.LoginService;
  import mx.validators.Validator;
  import mx.containers.ViewStack;
  import mx.rpc.events.ResultEvent;
  private function login():void{
   if(Validator.validateAll(validators).length == 0){
    LoginService.getInstance().login(username.text,password.text);
   }  
  }
 ]]>
</mx:Script>

 <!–  Validators–>
 <mx:Array id=”validators”>
    <mx:StringValidator  id=”userNameValidator” source=”{username}”  property=”text”  required=”true”/>
    <mx:StringValidator  id=”passwordValidator” source=”{password}”  property=”text” required=”true” />
 </mx:Array>   
<mx:Form id=”loginForm” x=”0″ y=”0″>
          <mx:FormItem label=”Username:” >
               <mx:TextInput id=”username” />
           </mx:FormItem>
           <mx:FormItem label=”Password:” >
               <mx:TextInput id=”password” displayAsPassword=”true” />
           </mx:FormItem>
           <mx:FormItem direction=”horizontal” verticalGap=”15″ paddingTop=”5″ width=”170″>
               <mx:Button id=”loginBtn” label=”Login” click=”login()”/>
           </mx:FormItem>
    </mx:Form>
    
</mx:Panel>

The core  function  above is :
LoginService.getInstance().login(username.text,password.text); 

It separate the business logic  from the view component.  The service class  is always the  singleton, since we don’t use it to carry any status.

Lets see  the class LoginService:

import com.ny.flex.centralManagement.event.DataManagerResultEvent;
 import com.ny.flex.centralManagement.manager.GlobalObjectManager;
 import com.ny.flex.centralManagement.manager.RemoteObjectManager;
 
 public class LoginService
 {
  public var roManager:RemoteObjectManager = null; 
  public var gom:GlobalObjectManager = GlobalObjectManager.getInstance();

  private static var _instance:LoginService =null;
  
  public static function getInstance():LoginService{
   if(_instance == null){
    _instance =  new LoginService(new PrivateClass)
   }
   return _instance;
   
  }
  public function LoginService(privateclass:PrivateClass)
  {
   if(LoginService._instance == null){
    LoginService._instance =  this;
   }
  }
  public function login(userName:String,password:String):void{
 roManager = RemoteObjectManager.getRemoteObjectManager(“flexmvcRO”);
   roManager.addEventListener(“getLoginUser”,loginHandler);
   var params:Array = new Array(userName,password);
   
   roManager.makeRemoteCall(“getLoginUserName”,”getLoginUser”,params);
  }

  private function loginHandler(event:DataManagerResultEvent):void {
   var userName:String = event.result as String;
   if(userName){
    gom.loginUserName = userName;
    gom.viewStackSelectedIndex=1;
   }
  }

 }

 

There are 2 special objects :

    RemoteObjectManager

   GlobalObjectManager

RemoteObjectManager is  a singleton class to implement central manage all the  Remote Object communication. The original code is from Jeff Tapper’s blog :

Creating a Remote Object DataManager in ActionScript 3.0 for Flex 2.0

The “GlobalObjectManager”  is used to  keep the information  between  UIs ,  for example   we use  viewstack selectedIndex to  decide which view will show  on the sample  application , so the global object viewStackSelectedIndex is used  in the application view :

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml“  xmlns:views=”com.ny.flex.centralManagement.views.*” layout=”absolute”  width=”100%” height=”100%”>
<mx:Script>
 <![CDATA[
  import mx.binding.utils.BindingUtils;
  import com.ny.flex.centralManagement.manager.GlobalObjectManager;
  [Bindable]
  public  var gom:GlobalObjectManager=GlobalObjectManager.getInstance();
 ]]>
</mx:Script>
    <mx:HBox  horizontalAlign=”center” verticalAlign=”top”  width=”100%” height=”100%” y=”0″ x=”0″>
    <mx:ViewStack id=”viewStack”  resizeToContent=”true” selectedIndex=”{gom.viewStackSelectedIndex}” >
        <views:LoginView  />
        <views:BuddyListView/>
    </mx:ViewStack>
    </mx:HBox>

</mx:Application>

 Just  look back the loginHandler method in LoginService , the  global object  is updated:

  private function loginHandler(event:DataManagerResultEvent):void {
   var userName:String = event.result as String;
   if(userName){
    gom.loginUserName = userName;
    gom.viewStackSelectedIndex=1;
   }
  }

The [Bindable] makes the change  affect immediately.

The list show is BuddyList.mxml:

 <?xml version=”1.0″ encoding=”utf-8″?>
<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Buddy List of {gom.loginUserName}” creationComplete=”init()” width=”500″ height=”320″>
<mx:Script>
 <![CDATA[
  import com.ny.flex.centralManagement.service.BuddyService;
  import com.ny.flex.centralManagement.manager.GlobalObjectManager;
  import mx.collections.ArrayCollection;
  import mx.rpc.events.ResultEvent;
  [Bindable]
  public var gom:GlobalObjectManager = GlobalObjectManager.getInstance();
  
  private function init():void{
   BuddyService.getInstance().getBuddyList();
  }
 ]]>
</mx:Script>

 <mx:DataGrid id=”buddyList”  dataProvider=”{gom.mybuddyList}”  borderStyle=”none” width=”100%” height=”100%” >
       <mx:columns>
        <mx:DataGridColumn dataField=”firstName” headerText=”First Name”/>
        <mx:DataGridColumn dataField=”lastName” headerText=”Last Name”/>
    </mx:columns>

 </mx:DataGrid>
</mx:Panel>

BuddyService  is another  service class to communicate the Remote Object.

This is  my favorate  architecture to develop flex webapps. It is very clean, There is  no junk dispatch and listener.  and it is  very easy to maintain. But I don’t have enough theory supports  it.

Next  lets move to MVC: Flex MVC-Cairngorm

July 23, 2008 Posted by | Flex | 2 Comments

Flex Chaos:All-in-one

Flex is born with event-driven. That means  Flex can do  anything by  its own tag. So The simplest way to develop Flex application naturally  is  using  Flex Tags. Lets shot  the code

In Loginview, the display part as usual:

 <mx:Form id=”loginForm” x=”0″ y=”0″>
          <mx:FormItem label=”Username:” >
               <mx:TextInput id=”username” />
           </mx:FormItem>
           <mx:FormItem label=”Password:” >
               <mx:TextInput id=”password” displayAsPassword=”true” />
           </mx:FormItem>
           <mx:FormItem direction=”horizontal” verticalGap=”15″ paddingTop=”5″ width=”170″>
               <mx:Button id=”loginBtn” label=”Login” click=”login()”/>
           </mx:FormItem>
 </mx:Form>

And use  tag <mx:RemoteObject> to call the service  :

<mx:RemoteObject  id=”loginReq” destination=”flexmvcRO”>
    <mx:method name=”login” result=”loginHandler(event)” fault=”mx.controls.Alert.show(event.fault.faultString)”>
     <mx:arguments>
      <userName>{username.text}</userName>
      <password>{password.text}</password>
     </mx:arguments>
    </mx:method>
</mx:RemoteObject>

Now, we need  send the  request in  login method:

   if(Validator.validateAll(validators).length == 0){
     loginReq.login.send();
   }  

After sending the request  A Result handler should be created:

  private function  loginHandler(event:ResultEvent):void{
    var isLogin:Boolean = event.result as Boolean;
   if(isLogin){
    this.parentApplication.viewStack.selectedIndex=1;
    dispatchEvent(new LoginUserEvent(username.text));
   }
  }

Finally A connection between the pages  should be  built. I use dispatch event :

dispatchEvent(new LoginUserEvent(username.text));

you want to dispatch event  you have to  add Meta tag in you page :

 <mx:Metadata>
     [Event(name="loginUser", type="flash.events.Event")]
</mx:Metadata>

It is time to create the  custom event LoginUserEvent.as:

import flash.events.Event;

 public class LoginUserEvent extends Event
 {
  public static const LOGINUSEREVENT:String =”loginUser”;
  public var loginUserName:String = “”;
  
  public function LoginUserEvent(userName:String)
  {
   super(LOGINUSEREVENT, true, true);
   this.loginUserName = userName;
  }
  override public function clone():Event {
     
      return new LoginUserEvent(loginUserName);
     
   }

The  Buddy list page(BuddyListVew.mxml) will show up after successful login:

First of first,  the  list page should listen the  LoginUserEvent,  so  a preinitialize mehtod will be called:

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Buddy List of {loginUserName}”     preinitialize=”init()” width=”500″ height=”320″>

   private function init():void{
   this.parentApplication.addEventListener(LoginUserEvent.LOGINUSEREVENT, getUserName);
 }

It is time to handle the event listener:

private function getUserName(event:LoginUserEvent):void{

loginUserName = event.loginUserName;

loginReq.getAllFriends.send();

}

then send request to get all Buddy and  handle it:

   private function getAllFriendsHandler(event:ResultEvent):void{
   mybuddyList = event.result as ArrayCollection;
   
  }

Don’t forget the remote object:

<mx:RemoteObject  id=”loginReq” destination=”flexmvcRO”>
    <mx:method name=”getAllFriends” result=”getAllFriendsHandler(event)” fault=”mx.controls.Alert.show(event.fault.faultString)”>
     <mx:arguments>
      <userName>{loginUserName}</userName>
     </mx:arguments>
    </mx:method>
</mx:RemoteObject>

That is  all-in-one style: You only need us  flex tag. nothing else. It is simple. and It is powerful if your system is simple  and the business logic is not  complicate.  you can do it by this  way. But  there  is no  real project as simple as this example.  Typically  there are lots of  remote object calls  in the real project.

So  It is time to move on : Flex Central Management.

 

 

July 21, 2008 Posted by | Flex | 2 Comments

Flex Development Evolvement

Flex is the No.1 choice  to develop RIA  application. And Java is  No.1 choice for Enterprise application. So the migration solution  should be that   use  Flex to  develop the  front-end, and  Java  for back-end. The Architecture is :

 

There are many different ways to  implement above architecture.  This series blogs will show  my experience and  evolvement of Flex Application development:

1. Flex Chaos: All-in-one

2. Flex Central Managerment

3.Flex MVC-Cairngorm

4. Cut It Off-Cairngorm without front controller

5. Tag Based-Mate

 

Sample application

I use a very simple  application to show the dvelop process. I call it  buddyList.  There are  two views :

Login View:

 input username and  password  click login  the buddyList view will show:

This  application  use  Remote Object to  implement server side data communication by BlazeDS,  I create some  simple  java code to get  some dummy data  from server side  The realated  classes and  files  are :

1. add the  java remote  destination in Remote-config.xml :

 <?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>
    <destination id=”flexmvcRO”>
     <properties>
      <source>com.ny.blog.flex.mvc.accessor.DummyAccessor</source>
      <scope>session</scope>
      </properties>
     <adapter ref=”java-object” />
    </destination>

</service>

2. DummyAccessor.java:

package com.ny.blog.flex.mvc.accessor;

import java.util.ArrayList;
import java.util.List;

import com.ny.blog.flex.mvc.pojo.Friend;

public class DummyAccessor {

 public DummyAccessor() {
 }
 
 public boolean login(String userName,String password){
  return true;
 }
 
 public  List<Friend> getAllFriends(String userName){
  List<Friend> myBuddy = new ArrayList<Friend>();
  Friend dummy1 = new Friend();
  dummy1.setFirstName(“John”);
  dummy1.setLastName(“Smith”);
  myBuddy.add(dummy1);
  
  Friend dummy2 = new Friend();
  dummy2.setFirstName(“Andy”);
  dummy2.setLastName(“Jones”);
  myBuddy.add(dummy2);
  
  Friend dummy3 = new Friend();
  dummy3.setFirstName(“Michael”);
  dummy3.setLastName(“Niu”);
  myBuddy.add(dummy3);
  return myBuddy;
 }
 

3. The pojo Friend.java

public class Friend {
 private String firstName;
 private String lastName;
 private String nickName;

public Friend() {
 }

//getter and setters

}

Lets  start it  from  : Flex Chaos: All-in-one

July 17, 2008 Posted by | Flex | | Leave a Comment

   

Follow

Get every new post delivered to your Inbox.