PureMVC Tutorial – Flex, PureMVC, Jabber and XIFF 3: Part 5 – Model & Proxy

Introduction
Part 1 – Frameworks
Part 2 – Directory structure
Part 3 – Application and ApplicationFacade
Part 4 – Notifications, Commands & Use Cases
Part 5 – Model & Proxy
Part 6 – The Application View & Mediator
Part 7 – The Login View & Mediator
Part 8 – The Roster View & Mediator
Part 9 – The Chat View & Mediator
Conclusion, Demo & Downloads

The model is the ‘data’ of our application and the proxy is our interface onto that data.  For our particular application the model itself is basically our Jabber connection object, so the Proxy will encapsulate that and expose an interface with the following methods:

  • connect(username:String, password:String, server:String):void
  • disconnect():void
  • sendMessage(message:Message):void
  • getRosterDataProvider():ArrayCollection

As you can see, the interface is very similar to our use case and commands, and this in no coincidence; each command is going to call the relevant method in its execute method.

Create a Proxy in the model folder called XMPPProxy.as using Add->New Proxy… (if you don’t see this menu item be sure you’ve installed the PureMVC FlashDevelop templates from PureMVC: First thoughts & FlashDevelop templates correctly).

I’m just going to include the Proxy code here without much explanation as it doesn’t really do anything overly complicated (all of the hard stuff has been done for us in the XIFF library ๐Ÿ™‚ )  Have a read through the comments of each method and everything should be clear.

   1: /*
   2: Proxy - PureMVC
   3: */
   4: package org.davekeen.xiffer.model {
   5:     import flash.events.Event;
   6:     import flash.system.Security;
   7:     import org.davekeen.xiffer.ApplicationFacade;
   8:     import org.jivesoftware.xiff.core.JID;
   9:     import org.jivesoftware.xiff.core.XMPPSocketConnection;
  10:     import org.jivesoftware.xiff.data.Message;
  11:     import org.jivesoftware.xiff.data.Presence;
  12:     import org.jivesoftware.xiff.events.*
  13:     import org.jivesoftware.xiff.im.Roster;
  14:     import org.puremvc.as3.interfaces.IProxy;
  15:     import org.puremvc.as3.patterns.proxy.Proxy;
  16:     import mx.collections.ArrayCollection;
  17:  
  18:     /**
  19:      * Proxy to XMPP server
  20:      */
  21:     public class XMPPProxy extends Proxy implements IProxy {
  22:         
  23:         public static const NAME:String = "XMPPProxy";
  24:         
  25:         private var xmppSocketConnection:XMPPSocketConnection;
  26:         private var roster:Roster;
  27:  
  28:         public function XMPPProxy(data:Object = null) {
  29:             super(NAME, data);
  30:             
  31:             setupConnection();
  32:             configureListeners();
  33:         }
  34:         
  35:         /**
  36:          * Create the required XMPP objects and do any configuration on them that we might require
  37:          */
  38:         private function setupConnection():void {
  39:             xmppSocketConnection = new XMPPSocketConnection();
  40:             
  41:             roster = new Roster();
  42:             roster.connection = xmppSocketConnection;
  43:         }
  44:         
  45:         private function configureListeners():void {
  46:             // Add event listeners related to the connection
  47:             xmppSocketConnection.addEventListener(LoginEvent.LOGIN, onLogin);
  48:             xmppSocketConnection.addEventListener(XIFFErrorEvent.XIFF_ERROR, onXiffError);
  49:             xmppSocketConnection.addEventListener(DisconnectionEvent.DISCONNECT, onDisconnect);
  50:             
  51:             // Add event listeners related to messages
  52:             xmppSocketConnection.addEventListener(MessageEvent.MESSAGE, onMessage);
  53:             
  54:         }
  55:         
  56:         /**
  57:          * Attempt to connect to a XMPP server
  58:          * 
  59:          * @param    username
  60:          * @param    password
  61:          * @param    server
  62:          */
  63:         public function connect(username:String, password:String, server:String):void {
  64:             // Attempt to load a crossdomain permissions file
  65:             Security.loadPolicyFile(server + "/crossdomain.xml");
  66:             
  67:             // Connect using standard profile
  68:             xmppSocketConnection.username = username;
  69:             xmppSocketConnection.password = password;
  70:             xmppSocketConnection.server = server;
  71:             xmppSocketConnection.connect("standard");
  72:         }
  73:         
  74:         /**
  75:          * Disconnect from a XMPP server.  If not currently connected this will have no effect.
  76:          * 
  77:          */
  78:         public function disconnect():void {
  79:             xmppSocketConnection.disconnect();
  80:         }
  81:         
  82:         /**
  83:          * Return the roster as a data provider
  84:          * 
  85:          * @return
  86:          */
  87:         public function getRosterDataProvider():ArrayCollection {
  88:             return roster;
  89:         }
  90:         
  91:         /**
  92:          * Send a message to the server
  93:          * 
  94:          * @param    message
  95:          */
  96:         public function sendMessage(message:Message):void {
  97:             xmppSocketConnection.send(message);
  98:         }
  99:         
 100:         /**
 101:          * The user has successfully logged on to the XMPP server
 102:          * 
 103:          * @param    connectionSuccessEvent
 104:          */
 105:         private function onLogin(loginEvent:LoginEvent):void {
 106:             roster.setPresence(Presence.SHOW_CHAT, "", 0);
 107:             
 108:             sendNotification(ApplicationFacade.VALID_LOGIN);
 109:         }
 110:         
 111:         /**
 112:          * There has been a Jabber error - most likely an incorrect username/password error
 113:          * 
 114:          * @param    xiffErrorEvent
 115:          */
 116:         private function onXiffError(xiffErrorEvent:XIFFErrorEvent):void {
 117:             if (xiffErrorEvent.errorCode == 400)
 118:                 sendNotification(ApplicationFacade.INVALID_LOGIN);
 119:             
 120:         }
 121:         
 122:         /**
 123:          * The user has disconnected from the XMPP server
 124:          * 
 125:          * @param    disconnectionEvent
 126:          */
 127:         private function onDisconnect(disconnectionEvent:DisconnectionEvent):void {
 128:             sendNotification(ApplicationFacade.DISCONNECT);
 129:         }
 130:         
 131:         /**
 132:          * Received a message from the server
 133:          * 
 134:          * @param    messageEvent
 135:          */
 136:         private function onMessage(messageEvent:MessageEvent):void {
 137:             sendNotification(ApplicationFacade.RECEIVE_MESSAGE, messageEvent.data);
 138:         }
 139:         
 140:     }
 141: }

One thing you will notice is that the proxy dispatches another few notifications that we haven’t included in our ApplicationFacade:

  • VALID_LOGIN
  • INVALID_LOGIN
  • RECEIVE_MESSAGE
  • DISCONNECT

So lets go back to our ApplicationFacade and add them in:

   1: public static const VALID_LOGIN:String = "valid_login";
   2: public static const INVALID_LOGIN:String = "invalid_login";
   3: public static const DISCONNECT:String = "disconnect";
   4: public static const RECEIVE_MESSAGE:String = "receive_message";

The final thing we need to do is register our new Proxy with PureMVC.  We do this in StartupCommand.as using the registerProxy method:

   1: override public function execute(notification:INotification):void {
   2:     facade.registerProxy(new XMPPProxy());
   3: }

Its quite easy to forget to do this and end up with all kinds of strange errors, so be sure to remember to register any proxies you create.

We’ve finally got the bones of our application up and running so now its on to the views and mediators!

9 comments

  1. I’m confused about proxies. I want to continue to use MVC outside of the XMPP/chat features of my application. So for my non-xmpp functions, should I create a proxy for these? Or when do you decide you need to create another proxy?

  2. Hey Will,

    There aren’t any hard and fast rules to this – its really up to you how you split up the proxies. However, here are some things that I personally think about:

    o If a proxy contains methods that operate on a specific logical concept then seperate it out. For example, in an address book application all the methods to do with contact CRUD might be in ContactProxy and all the methods to do with import/export might be in ImportExportProxy.
    o I often put things that access different remote services into their own proxies. For example, if our Jabber application also got information about users via a web service these methods might well be in their own proxy.
    o Sometimes there is a fairly direct map between the services in your proxy, delegate and backend so I try to keep these synchronised. For example, if I have a large AMFPHP application I’ll have UserProxy only calling methods in UserMethods.php, etc. This means that the way the backend is compartmentalised sometimes dictates the way the proxies are split.
    o Its often good to try and get all similar functionality into a single proxy for purposes of re-use. I always try to split them up in such a way that if I decide that I want to use ‘UserProxy’ in a new application it contains everything to do with users.

    But, when you get right down to it, split the proxies however you feel most comfortable – they are there for your convenience!

    Hope that helps,

    Dave

  3. Hey there, great tutorial! I’m just getting started working with XMPP and the dearth of documentation online is disheartening, but your tutorial is a godsend.

    One thing I think you might have missed is to add
    import org.davekeen.xiffer.model.XMPPProxy
    near the start of StartupCommand.as – trying to build the project without it results in “Error: Call to a possibly undefined XMPPProxy” after registering the new proxy.

    Other than that, works like a charm!

  4. Hi Dave,

    Great writeup, I was looking for a framework to use with Flash, and this tutorial encouraged me to try PureMVC and I have to say it works very well so far. As its my first time workign with Flash I fell down a few holes (hint: if putting views in a TabNavigator or other hidden layout, set creationPolicy=all or your mediators get passed null instances of the view)

    Anyway .. back to a question on the structure … Everyting seems to work for me in the way you have set the project up, excpet for one small niggle .. I can’t seem to get comfortable with the commands calling up an instance of the xmpp object to talk to … to my mind, they should be decoupled from the implementation of the chatclient model .. through either an interface or a wrapper class. I should be able to swap my xmpp model for a newFunkyChatProtocol model without any change to the app or its command structure …

    I think .. anyway … but there again I often get confused ๐Ÿ™‚

  5. Hey Robin,

    You are 100% correct; the best way to do this would be to have an interface on the XMPP model. For the sake of brevity in the tutorial I didn’t do this, but its the best way to go.

    I have recently started using RobotLegs which handles mapping of Models (and other things) to interfaces in a brilliant way. I would recommend checking it out if you are interested.

    Cheers,

    Dave

  6. Hi there from Germany! Glad i founfd “you” ๐Ÿ˜‰
    Really made my day with this tutorial, for getting startet with my own little jabber client via AS3… thanx for the great work!

    BUT, am i to dumb or is there no more XMPPSocketConnection class in the XIFF 3.0 library?! havent got that deep into it right now… is it changed?

    Cheers, Ben

  7. Hey Ben,

    I’m afraid that I haven’t used the new XIFF library (to be honest I didn’t even know there was one ๐Ÿ™‚ ), so its very possible that its changed.

    However, I doubt it will have changed too much so you can probably hack the example code to work with the new library without much difficulty.

    Good luck!

    Dave

Leave a Reply

Your email address will not be published. Required fields are marked *