This page is an archive of my old blog. Please visit DavidTucker.net for my current blog.
This site is no longer being maintained and commenting is disabled.

Getting Started with Cairngorm – Part 4

The basic Cairngorm Event Flow that is handled in Part 3 is essential to any Cairngorm application, but most applications interact with a server. The Service to Worker pattern that was discussed in the previous tutorial is essential to this process. To learn the expanded Cairngorm Flow, you will need to learn a few new Cairngorm elements.

The Server Interaction Elements

There are three new types of classes that you will need to be familiar with to understand the full Service to Worker Pattern.

  • ServiceLocator [Reference] - The ServiceLocator is a singleton that contains references to all of the services that your application will use. It usually includes HTTPService, WebService, or RemoteObject calls.
  • Business Delegate - A Business Delegate has three functions: to locate the service that is needed, to call a method on the service, and to route the result to the specified responder. There is usually one Business Delegate for each service.
  • Value Object - Value Objects are classes that by definition do not require any methods, only properties. They allow for the application to transfer strongly-typed complex data objects to and from the server.

Organizing Your Cairngorm Project (With Server Interaction)

In the last tutorial you saw the standard Cairngorm structure for a project. To complete this structure, you will need to add an additional two folders:

  • /business - This folder typically contains two types of files: the ServiceLocator (named Services.mxml) and Business Delegates.
  • /vo - This folder contains the value objects that are passed to and from the server.

Understanding the Process

Before you begin crafting the server interaction for your project, you need to understand the full Service to Worker pattern. Figure 1 illustrates the entire process.

Full Cairngorm Diagram Final

Figure 1 - Cairngorm Event Flow with Server Interaction

Phase I - Execution Phase

  1. The user is viewing a component that has a login button. When the button is clicked, that component would dispatch an event named LoginEvent that extends CairngormEvent.
  2. The FrontController would receive the LoginEvent and execute the command that is mapped to that event. For this example, it will be named LoginCommand and will implement ICommand.
  3. The Command is executed and received the LoginEvent as an argument. The LoginCommand creates an instance of the Delegate while passing a reference to the Responder. The Command then calls the specified method on the Delegate.
  4. The Delegate gets the service from the ServiceLocator and calls the method on the specific service.
  5. The service defined in the ServiceLocator receives the information that is passed to it from the call in the Delegate.

Phase II - Application Tier Processing

Phase III - Response Phase

  1. The service that is defined in the ServiceLocator returns the result of the server-side processing.
  2. The Delegate receives the result and passes it to the specified responder.
  3. The Responder receives the result indicating a successful login.
  4. The ModelLocator will have a predefined constant for the "Logged in View". The Responder will change workflowState to this value.
  5. Since the view is bound to workflowState, it will automatically update itself to the new view.

Note: Traditionally the Command and Responder were the same class in a Cairngorm application. If you are working with other developers, the applications will probably be coded in this manner. However, most developers (including Adobe Consulting) are now separating these classes into two different classes. For a less complex project, it might not make sense to separate these items, but in a large application it could prove advantageous in organization and practice to have two different classes.

The ServiceLocator

The ServiceLocator is a singleton that contains references to all of the services that the application will be using. These services can be RemoteObjects, HTTPServices, WebServices, or custom services. Like the FrontController, this class is usually instantiated in your main application file. Unlike many of the other Cairngorm project assets that you have created, this class can be defined in MXML (as well as ActionScript). To properly define this in MXML, there must be a namespace that points to the business folder of the Cairngorm package. In this case the namespace cairngorm is typically used. The ServiceLocator in Code Example 1 has one service defined, loginService.

Actionscript:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <cairngorm:ServiceLocator
  3.     xmlns:cairngorm="com.adobe.cairngorm.business.*"
  4.     xmlns:mx="http://www.adobe.com/2006/mxml">
  5.    
  6.     <!-- Login Service -->
  7.     <mx:RemoteObject id="loginService"
  8.         showBusyCursor="true"
  9.         destination="ColdFusion"
  10.         source="CairngormTest.CairngormLogin">
  11.        
  12.         <mx:method name="login" /> 
  13.        
  14.     </mx:RemoteObject>
  15.    
  16. </cairngorm:ServiceLocator>

Code Example 1 - Sample ServiceLocator

The ServiceLocator is typically named Services.mxml and resides in the business folder of your Cairngorm project.

Commands with Server Interaction

In the last tutorial you hard-coded some values into the LoginCommand to check for a specific username and password. Here was the execute method:

Actionscript:
  1. public function execute(event:CairngormEvent):void {
  2.     var loginEvent:LoginEvent = event as LoginEvent;
  3.     if( (loginEvent.username == "david") && (loginEvent.password == "password")) {
  4.         modelLocator.workflowState = ViewModelLocator.WELCOME_SCREEN;
  5.     }
  6. }

Code Example 2 - Command Without Delegate

This methodology will hold true for any Command that doesn't have server interaction, but if you do have server interaction, it will need to be modified to include the delegate. First, you will need to make a decision. As stated earlier, you can have separate Commands and Responders, or they can be the same class. For this example, they will be the same class. The Command will now also need to implement the mx.rpc.IResponder class (please note that com.adobe.cairngorm.business.Responder is deprecated and should no longer be used).

The other initial change is that the execute method now instantiates a class named LoginDelegate (which will be created shortly). The LoginDelegate class required one argument in its constructor, the Responder of the service. In this case, the Command will function both as the command and the responder, so you simply need to insert the this keyword inside the parenthesis. If you chose to have a separate responder, you would insert the reference to it here (instead of the this keyword).

Actionscript:
  1. package net.davidtucker.CairngormSample.commands {
  2.    
  3.     import com.adobe.cairngorm.commands.ICommand;
  4.     import com.adobe.cairngorm.control.CairngormEvent;
  5.     import mx.controls.Alert;
  6.     import mx.rpc.IResponder;
  7.     import net.davidtucker.CairngormSample.business.LoginDelegate;
  8.     import net.davidtucker.CairngormSample.events.LoginEvent;
  9.     import net.davidtucker.CairngormSample.model.ViewModelLocator;
  10.    
  11.     public class LoginCommand implements ICommand,IResponder {
  12.    
  13.         public var modelLocator:ViewModelLocator = ViewModelLocator.getInstance();
  14.        
  15.         public function LoginCommand() {
  16.         }
  17.        
  18.         public function execute(event:CairngormEvent):void {
  19.             var loginEvent:LoginEvent = event as LoginEvent;
  20.             var delegate:LoginDelegate = new LoginDelegate( this );
  21.             delegate.login(loginEvent.loginAttempt);
  22.         }
  23.        
  24.         public function result( event:Object ):void {
  25.             if(event.result == true) {
  26.                 modelLocator.workflowState = ViewModelLocator.WELCOME_SCREEN;
  27.             } else {
  28.                 mx.controls.Alert.show("Password Incorrect","ERROR");
  29.             }
  30.         }
  31.        
  32.         public function fault( event:Object ):void {
  33.             trace("Service Error");
  34.         }
  35.        
  36.     }
  37. }

Code Example 3 - Command with Server Interaction Through a Delegate

Value Objects

A Value Object does not extend or implement any Cairngorm class. As stated earlier, it simply is a class that is only required to have properties, but not methods. For example, if you created a Value Object for a Login - it would have a property for username and a property for password.

Actionscript:
  1. package net.davidtucker.CairngormSample.vo {
  2.    
  3.     [RemoteClass(alias="CairngormTest.LoginVO")]
  4.     public class LoginVO {
  5.        
  6.         public var username:String;
  7.         public var password:String;
  8.        
  9.         public function LoginVO(username:String,password:String) {
  10.             this.username = username;
  11.             this.password = password;
  12.         }
  13.        
  14.     }
  15. }

Code Example 4 - Value Object for Login

The RemoteClass metatag is important to note. This will allow the corresponding server-side object (ColdFusion Component, PHP Class, Java Class, etc...) to be mapped to this Value Object. In this case, it is mapped to a ColdFusion component named LoginVO in the CairngormTest folder.

Business Delegates

The final design pattern in the Cairngorm Micro-Architecture is the Business Delegate. A Business Delegate essentially is the abstraction layer between your services and the rest of your application. As stated earlier, it has three functions. First, the Business Delegate will locate the service that is needed in the ServiceLocator. Second, it will call a method on that service. Finally, it will route the response back to the specified responder (usually either a command or separate responder).

A Delegate class doesn't extend or implement and Cairngorm classes, but it generally follows the following guidelines.

  • The Delegate has at least two properties: one named service which is a reference to a service in the ServiceLocator and one named responder which is the responder for the service calls.
  • There is a method for each server-side method that you will be calling.
  • Both the responder and the service variables are set in the constructor.
Actionscript:
  1. package net.davidtucker.CairngormSample.business {
  2.    
  3.     import mx.rpc.IResponder;
  4.     import net.davidtucker.CairngormSample.vo.LoginVO;
  5.     import com.adobe.cairngorm.business.ServiceLocator;
  6.    
  7.     public class LoginDelegate {
  8.         private var responder : IResponder;
  9.         private var service : Object;   
  10.        
  11.         public function LoginDelegate( responder:IResponder ) {
  12.             this.responder = responder;
  13.             this.service = ServiceLocator.getInstance().getRemoteObject("loginService");
  14.         }
  15.        
  16.         public function login(login:LoginVO):void {
  17.             var call:Object = service.login( login );
  18.             call.addResponder( responder );
  19.         }
  20.        
  21.     }
  22. }

Code Example 5 - Service Delegate

There are many benefits to having this layer. If coded correctly, you should be able to change out the server interaction (going from PHP to ColdFusion for example) and only have to change the code in your ServiceLocator and your Delegate. You also can easily insert "stub code" to simulate the actual server interaction during the early stages of development.

The Application Tier

In this example, the Application Tier will be handled by a ColdFusion 8 installation. It will contain two Coldfusion components. These components are purposefully simple.

  1. LoginVO.cfc - This component will correspond to the ActionScript class LoginVO that you created earlier.
  2. CairngormLogin.cfc - This component will perform the actual login processing.

In this example, the Flex application will pass a LoginVO ActionScript object to the CairngormLogin.cfc's login method through a RemoteObject call. This will be mapped to a LoginVO.cfc object. If this LoginVO.cfc object has the username "david" and the password "password" the method will return true. If not, it will return false.

Coldfusion:
  1. <cfcomponent displayname="LoginVO" hint="Login VO For CairngormTest" output="false">
  2.  
  3.     <cfset this.username = "" />
  4.     <cfset this.password = "" />
  5.    
  6. </cfcomponent>

Code Example 6 - LoginVO.cfc

Coldfusion:
  1. <cfcomponent displayname="CairngormLogin" hint="CFC to Test Cairngorm Service Interaction" output="false">
  2.  
  3.     <cffunction name="login" displayname="login" access="remote" output="false" returntype="boolean">
  4.         <cfargument name="loginAttempt" type="LoginVO" required="true" />
  5.            
  6.             <cfif (loginAttempt.username EQ "david") AND (loginAttempt.password EQ "password")>
  7.                 <cfreturn true />
  8.             <cfelse>
  9.                 <cfreturn false />
  10.             </cfif>
  11.        
  12.     </cffunction>
  13.    
  14. </cfcomponent>

Code Example 7 - CairngormLogin.cfc

Application Code
Download (11 Kb)

Looking Ahead

There are only two tutorials remaining in the Cairngorm series. In the next tutorials you will learn about code generation options for Cairngorm, and you will also build an actual application using Cairngorm.

Reference

The following resouces should assist you in getting Flex connected to your application server.




71 Responses to “Getting Started with Cairngorm – Part 4”

  1. Jorsh says:

    Thanks a bunch for the tutorials David, they make learning this stuff SO much easier. If anyone else runs into problems implementing the ServiceLocator mxml component described above, make sure you have the component added to your Application mxml like you did with the FrontController. Took me a while to figure that one out.

  2. David Tucker says:

    @Jorsh – Thanks for this note! I apologize to all if I didn’t make this clear in the tutorial.

  3. [...] Part 4 you saw the full Service to Worker pattern demonstrated. However, the method discussed in the last [...]

  4. daslicht says:

    why can’t access ServiceLocator in the ERROR file?
    Any idea?

  5. daslicht says:

    GOT IT :D

  6. Harry says:

    Here is the error I get when running example. I have declared a remoteObject service and given id=”unclaimService”.

    RemoteObject not found for unclaimService
    at RemoteObjects/getService()[C:\dev\swat\projects\ac_emea\Cairngorm\com\adobe\cairngorm\business\RemoteObjects.as:80]
    at com.adobe.cairngorm.business::ServiceLocator/getRemoteObject()[C:\dev\swat\projects\ac_emea\Cairngorm\com\adobe\cairngorm\business\ServiceLocator.as:137]

  7. Harry says:

    I am getting error at this line in the delegate class constructor.
    When the command class tries to get instance of delegate, its throwing an error.
    this.service = ServiceLocator.getInstance().getRemoteObject(“unclaimService”);

    Also can somebody explain the logic behind the above line. We have created a services.mxml file and its rootTag is . I am kind of confused!!!, where are we using that services.mxml file… please explain!!

  8. Pranav says:

    Hi David,
    Great series of vedios….

    I implement all these stuff but i got an error
    ArgumentError: Error #1063: Argument count

    Could you help me to resolve this ?

    ThanX in advance………

  9. Vikram says:

    Hello David

    Thanks a lot for the wonderful series of tutorials. This has got me going with Cairngorm.

    I am setting up the Services using Zend_AMF. The service works fine when I use a simple RemoteObject without Cairngorm. But using this tutorial the fault event displays the following message:
    [RPC Fault faultString="Channel disconnected" faultCode="Client.Error.DeliveryInDoubt" faultDetail="Channel disconnected before an acknowledgement was received"]

    What could be going in this case? Can you guide me appropriately?

    With Kind Regards
    Vikram

  10. Vikram says:

    Hello there David

    I just noticed that the service class you made in ColdFusion takes in the argument loginAttempt of type LoginVO.

    I am implementing the service in PHP. Should it be same there or will it take two arguments username and password?

    Thanks and Regards
    Vikram

  11. Vikram says:

    To newbie developers who use this wonderful tutorial by David, but create the server side service in PHP, please remember that in LoginDelegate.as, you will have to change the method which calls the server-side method like this

    public function login(login:LoginVO):void {
    var call:Object = service.login( login.username, login.password );
    call.addResponder( responder );
    }

    This applies when you create the service in PHP which has the prototype like this..

    public function login($username, $password)
    {
    // implement login functionality here
    }

    I had to spend quite sometime figuring this out, being a newbie in Cairngorm and Flex.

    With best regards
    Vikram

  12. Rohit Marathe says:

    Hi David.
    Thnaks a ton for Gr8 articles about Cairngorm.
    As a newbee I have one problem
    In Cairngorm view fires cairngorm events which are routed to appropriate commands and these commands then update the model.
    As model is bound to view view gets updated.
    But if I want want to clear the user registration form after sucessfully inserting the record into data base what is the best method to do that, if I am using Cairngorm.
    Please Help :)

  13. [...] Artigo sobre Cairngorm parte 4 e o  Vídeo Cairngorm Part 4 [...]

  14. Codeguyz says:

    For those that were getting the on this line.

    this.service = ServiceLocator.getInstance().getRemoteObject(”unclaimService”);

    I was getting the same thing. Then I made a reference in my main application to the services and it worked fine.

    (put this in your main application, it defines the service before making the call).

  15. Deepak says:

    Hi David,
    Thanks a zillion for those videos. I learnt using caringorm withing half a day :)

    However, I have one problem in my current project. I am using webservices. If the webservice URL is fine, then there is no problem. The service gets invoked and it triggers result or fault event properly. But in case the webservice is offline for some reason, it wont trigger the fault function within my command class. It instead shows up that fault event message at runtime. Just wanted to know if there is any other place where I need to handle this case.
    -Deepak

  16. That shift of thinking you talk about is so important..

  17. RAVI says:

    Very nice tutorial…………….

    sir i have one problem…….
    instead of listing the people in i am using datagrid… to display firstname in FIRST column and secondname in SECOND column … but i getting error …………..
    con you suggest me the solution pleazzzzzzzzzzzzzzz.

  18. Mark Lowery says:

    Im creating a registration page with several paramaters could someone please show me an example of passing an array of data insted of passing them into the method (ie. service.login( login.username, login.password );

    could i do something like this
    var data:object = new object()
    data.username = username.text;
    data.phone = phone.text;
    data.email = email.text;
    delegate.fn(data);

    Im lost.. or could someone just give me an example code for this.

  19. Thank you for writing this awesome post. Keep up the good work.

  20. Jad El Hak HAMDAOUI says:

    First I want to thank you for your tutorials that help me as a new Flex developer.
    Why we don’t use getters and setters to get access to VO properties, that should be private, to respect Object encapsulation ?

    Thank’s.

  21. PPuskar says:

    Hello Sir,
    This is a great tutorial and i have implemented this in my previous project.

    i wanted to know how can i use data/Service (which produces RemoteObjectServiceWrapper and Value Object- Flash Builder 4)

    if i have LoginUtilService then how can i use the delegate to connect the Service with the responder…