Category: Flex

  • Using setCredentials in Flex 2 via Renaun’s RemoteObjectAMF0 in AMFPHP 1.2.5

    To skip rant and see solution, scroll to bottom.

    This crap was too frikin’ hard to find in Google. After spending 3 hours debugging, I figured out a solution, but not the source of the problem. Following the authentication example at amfphp.org, it just wans’t working. I kept getting an error about the user not having access to the method I was calling, in this case doLogin. Props to Patrick btw for giving us such informative and useful error messages we can catch in our fault handlers.

    This, however, didn’t go away when I put in the correct credentials. Credentials are basically the username and password added as a header to the remoting call so the server can authenticate you. This method is on NetConnection, but in ActionScript 2, you’d access it via:

    serviceInstance.connection.setCredentials ( "username" , "password" )

    and in AS1 via:

    serviceInstance.setCredentials ( "username" , "password" )

    The AS2 way is the same way it was in Flex 1.5 I think.

    Now, the proxying in Abstract service is working. Although the class is dynamic, and I think uses a __resolve like mechnism (returning a make-shift function if that function doesn’t exist on the class instance you are calling ), you can clearly see by ServiceCapture & TamperData that it only makes the 1 remoting call. However, both TamperData and ServiceCapture don’t show the damn headers. I couldn’t figure out how to do traces in AMFPHP to see what the headers were, and I can’t find a NetConnection swf anywhere on my hard drive to use the old NetDebug.trace way.

    Now, mx.rpc.AbstractService isn’t included in the Flex source. I put up with this no source bullshit back in Flex 1.5 and was hoping it wouldn’t negatively affect me in Flex 2, but apparently not. GIVE ME THE F’ING CODE, ADOBE! That said, you CAN at least set a breakpoint. Oddly, it was creating a credential variable and I think like using a “:” colon as a seperator; the var was called “cred”. I was wondering if maybe AMFPHP was hardcoded to look for a specific header called “Credentials”, what it was called in Flex 1.5 & Flash 8 on down, and this perhaps changed in Flex 2. Or, maybe the actual names changed to username vs. userid, which was what it was in ActionScript 2. :: shrugs ::

    Google didn’t turn up anything (first 10 results anyway), but Renauns blog had a comment with a guy with a similiar problem. He posted his solution. I had problems understanding it, though, because my copies of Cairngorm 2 and 2.1 don’t have the getRPCService, so that through me for a loop, but I saw what he was doing; he was basically manually adding the header. I didn’t want to mess with Renaun’s class, so I did it manually.

    It didn’t work. Kept bitching about a null reference. Digging in Renaun’s code, the connection “gateway_conn” is instantiated via lazy loading. In Flex 1.5 & Flash 8 & 7, this was done in the constructor. Renaun’s, however, does it on the first remoting call you make.

    if( gateway_conn == null )
    	gateway_conn = new RemotingConnection( endpoint );

    So, I first did a call to ping which sets the connection, and then added the header to the connection, THEN called doLogin.

    my_ro.ping();
    my_ro.gateway_conn.addHeader("Credentials", false, {userid: "admin", password: "myPassword"});

    She worked.

    SON OF A LAKSJDROItuioqasdf… :: whips out MG42 :: :: lights up office, flooding neihborhood with bullets ::

    Speaking of hellfire, my site now works in IE7; I’m not finished, but at least you can read the text. I hope IE burns for all eternity. While I do dig their implementation of RSS, if that is their best attempt at making RSS friendly to users… well.. um… we have a loooooooong way to go. Installing it didn’t fix that dang IE script pop-up error I get in Flex Builder 2 either. Weeeeeeeeeee!!!!!!!!

  • CheckBox Item Renderer Example for Flex 2

    My man A&W had some DataGrid woes. He’s now indoctrinated to the hardest part of Flex / Flash Development: Item Renderers. Adobe deserves credit for making these a lot easier with inline editors and some other nice properties. That doesn’t change the fact that they are f’ing hard and time consuming.

    Weirder still is the way you get events from ’em, via event bubbling. Granted, I’m sure rendererIsEditor on the DataGridColumn class works wonders when it works, but at the end of the day, a lot of the itemRenderers and itemEditors you create may need more detail that you want to know about, and bubbling events are a great way to get at that data.

    The gist is, you create an itemRenderer class and set one of the DataGrid columns to use it as an itemRenderer. This GUI MXML or ActionScript component emits an event you care about either using the default flash.events.Event class, or one of your own. This event has its bubbles property set to true. As long as its type doesn’t conflict with events the DataGrid is listening for, it’ll bubble up to the form hosting the DataGrid. There is where you handle the event.

    The trick is you have to use ActionScript to register for the event because the DataGrid doesn’t know about your custom event; it’s not in its metatags. Therefore, MXML won’t work and AS will.

    Source code – Example (Right click to View Source) | ZIP

  • Graeme Harker on Marketing Flex to Developers

    I really liked this entry Graeme Harker wrote about marketing Flex to developers. I feel like I wasted the past year (2005) evangelizing Flex 1.5. Flex 2 is really gaining momentum, but still, it’s not the explosion a lot of us were expecting. I think if Adobe takes heed of some of what Graeme says, that should help with some much deserved inroads. Adobe has worked hard on Flex 2 and I think if a lot of the demographics Graeme talks about see her in action, they’ll agree and dive in.

  • Flash Media Server, Flex Builder 2, and ANT

    Developing Flex 2 applications that talk with Flash Media Server is really fun.  I’m sure Adobe will tell you that Flex Data Services are the way to go, but that is overkill for a lot of the things I want to do.  If you want to create a set of widgets that support multiuser activity such as chat and sharing of real-time data, you don’t need the Enterprise data services that are FDS.  While Flash Media Server has real-time video and audio, I use it strictly for the real-time data via Remote SharedObjects , and the server-side scripting done in case-sensitive JavaScript.  The persistent data of Remote SharedObjects with a simple, yet flexible coding model makes them a very powerful way to get multi-user Flex 2 applications.  Combined with some simple server-side logic to maintain “data authority” as I like to call it, and you’ve got a really fun way to create real-time data, mult-user apps.

    What are the alternatives?

    XMLSocket ?  Parsing XML is more programmer friendly now with E4X, ECMAScript for XML , in ActionScript 3.  XMLSocket servers do not enforce XML, however… they are merely string servers that run on ports of 1024 or higher, thus making them flexible.  The cons?  They run on ports 1024 or higher… ONLY.  Client got a firewall?  Tough.  There are not a lot of big name XMLSocket hosting services.  Those that are out there are extremely expensive for what you get.  While the hosting might not be expensive, the license fee’s are.  A lot of the open source solutions aren’t finished.  Those that are assume you know PHP, Ruby, Perl, or Python well enough to finish it.  Assuming I could, where do I host these servers?  My host isn’t going to dig me having a PHP script stuck in a while loop…

    FDS?  Sorry, can’t afford 60k for fun side projects that have the potential to go to extremely small commercial markets.

    Some other TCP/IP binary server?  I’d like to create things today instead of getting burnt out writing something from scratch in ActionScript 3’s binary Socket API.

    It’d be nice if Adobe would release a Remote SharedObject server, complete with server-side JavaScript on a totally different pricing and license scheme than Flash Media Server or Flex Data Services are.  There are a lot of use developing mulit-user Flash Player applications on FMS that have no need for real-time streaming video and audio.  I think the same crackheads that ran pre-Flex 2’s business unit took over Flashcom’s.

    At any rate, there is no denying FMS is elegant to code with.  With a little work via the Observer pattern , you can implement FMS server-side components into Cairngorm 2 .  For ever server-side component you create, you merely create a client side equivalent as an Observer.  It listens for NetConnection or Remote SharedObject sync events and dispatches events.  These events run Commands which in turn set data… just like normal.  If your Views are bound to said data, boom, you’ve got a nice, clean separation.

    One thing that sucks about the work flow, though, is:

    • you have to redeploy your .asc files, your server-side code (basically JavaScript written server-side components)
    • you have to restart your app via the Communication App Inspector, basically a small app written in Flash
    • you then toggle back to Eclipse (Flex Builder 2 )

    That sucks.  The better way is to use some ANT tasks I’ve created along with a special SWF.  This SWF connects to the FMS Admin server, reloads the app, and then immediately quits.  I go into more detail below, or you can just download the source.  You could re-write the ActionScript 2 into ActionScript 3, but AS2 is just quicker than AS3 for this type of stuff.

    Bottom line, you just double-click an ANT task in Eclipse, it does all that for you, and you don’t have to leave Eclipse.  That’s so hot!

    First, the ANT tasks.

    <project name=”DnDTools” default=”Deploy FCS” basedir=”.”>
       
        <description>
        by Jesse R. Warden
        jesterxl@jessewarden.com
        https://www.jessewarden.com
        jesse@universalmind.com
        http://www.universalmind.com
       
        A series of tasks to work with Flashcom.
        ************************************************   
           
        Start Flashcom will write out a bat file, and run it to start
        Flashcom as well as the Flashcom Admin service.
       
        Stop Flashcom stops both the Flashcom service and the
        Flashcom Admin service.
       
        Deploy FCS does a few things:
        * copies your .asc files to the FCS applications directory
        * writes a configuration XML file for the FCS App Reloader
        * runs the FCS App Reloader to reload your application instance on the FCS server
       
        </description>
       
        <!– ************************************************ –>
        <!– set global properties for this build –>
        <property name=”bin” value=”bin” />
        <property name=”localFCSDir” value=”../flashcom”  />
        <property name=”deployFCSDir” value=”C:\Program Files\Macromedia\Flash Communication Server MX\applications\fcsappdirectory” />
        <property name=”FCSAppReloader” value=”FCS_App_Reloader.exe” />
        <property name=”FCSAdminHost” value=”localhost” />
        <property name=”FCSAdminPort” value=”1111″ />
        <property name=”FCSAppName” value=”fcsappdirectory” />
        <property name=”FCSAdminUser” value=”myUsername” />
        <property name=”FCSAdminPassword” value=”myPassword” />
        <property name=”appWaitTime” value=”3″ />
        <property name=”bat” location=”bat” />
       
        <!– Copy files into the FCS app directory, write a config xml, and reload an FCS app –>
        <target name=”Deploy FCS”>
           
            <!– ************************************************ –>
            <!– Copy new FCS files to the applications directory –>
            <echo message=”Copying Flashcom files to application directory…” level=”info” />
            <copy file=”${localFCSDir}/Application.xml” todir=”${deployFCSDir}” />
            <copy file=”${localFCSDir}/asc/main.asc” todir=”${deployFCSDir}” />
            <copy file=”${localFCSDir}/asc/Chat.asc” todir=”${deployFCSDir}” />
           
            <!– ************************************************ –>
            <!– Write the config xml file for the FCS Reloader App –>
            <echo message=”Writing FCSAppReloader configuration xml file…” level=”info” />
            <!– Host –>
            <echo message=”&lt;config host=’${FCSAdminHost}’ “
                file=”${localFCSDir}/fcs_app_reloader_config.xml” />
            <!– Port –>
            <echo message=”port=’${FCSAdminPort}’ “
                append=”true”
                file=”${localFCSDir}/fcs_app_reloader_config.xml” />
            <!– App Name –>
            <echo message=”appname=’${FCSAppName}’ “
                append=”true”
                file=”${localFCSDir}/fcs_app_reloader_config.xml” />
            <!– Username –>
            <echo message=”username=’${FCSAdminUser}’ “
                append=”true”
                file=”${localFCSDir}/fcs_app_reloader_config.xml” />
            <!– Password –>
            <echo message=”password=’${FCSAdminPassword}’ “
                append=”true”
                file=”${localFCSDir}/fcs_app_reloader_config.xml” />
            <!– Hang Time –>
            <echo message=”hangtime=’${appWaitTime}’ /&gt;”
                append=”true”
                file=”${localFCSDir}/fcs_app_reloader_config.xml” />
           
            <!– ************************************************ –>
            <!– Launch the FCS Reloader App –>
            <echo message=”Re-loading the FCS application…” level=”info” />
            <exec executable=”${FCSAppReloader}”
                dir=”${localFCSDir}/”
                spawn=”false”
                resolveexecutable=”true” />
           
           
        </target>
       
        <target name=”Start Flashcom”>
           
            <echo message=”Starting Flashcom…” level=”info” />
            <mkdir dir=”${bat}” />
            <echo file=”${bat}/start_fcs.bat”>%SYSTEMROOT%\system32\net.exe start &quot;FlashCom&quot;
                   
            </echo>
            <exec executable=”start_fcs.bat”
                dir=”${bat}”
                spawn=”false”
                resolveexecutable=”true” />
           
        </target>
       
        <target name=”Stop Flashcom”>
           
            <echo message=”Stopping Flashcom…” level=”info” />
            <mkdir dir=”${bat}” />
           
            <echo file=”${bat}/stop_fcs.bat”>%SYSTEMROOT%\system32\net.exe stop &quot;FlashCom&quot;
           
            </echo>   
            <echo message=”%SYSTEMROOT%\system32\net.exe stop &quot;FlashComAdmin&quot;”
                append=”true”
                file=”${bat}/stop_fcs.bat” />
           
            <exec executable=”stop_fcs.bat”
                        dir=”${bat}”
                        spawn=”false”
                        resolveexecutable=”true” />
           
        </target>
       
    </project>

    Now, the ActionScript 2 for the FCS Reloader App:

    /*
    
    FCS App Reloader
    by Jesse R. Warden
    jesterxl@jessewarden.com
    http://www.jessewarden.com
    jesse@universalmind.com
    http://www.universalmind.com
    
    This is release under a Creative Commons license.
    More information can be found here:
    
    http://creativecommons.org/licenses/by/2.5/
    
    v 1.0.0
    This app connects to your FCS server, and reloads
    an application instance.  The typical use case is to
    launch this app from ANT in Eclipse.
    
    The alternative is to:
    - toggle to the Communication App Inspector
    - click the app instance and click "reload"
    - if it's not there, type in it's name, and then click "load"
    - toggle back to Eclipse
    
    Now, you can just click once on your ANT task, and you're done!
    
    This API assumes Flash Communication Server 1.0 and Flash Player 6.
    I tested this in Flash Player 8, and the API should work
    in Flash Media Server 2 per the livedocs.
    
    */
    import mx.utils.Delegate;
    
    function init()
    {
    	Stage.align = "TL";
    	Stage.scaleMode = "noScale";
    	Stage.addListener(this);
    	
    	createTextField("debug_txt", 0, 0, 0, 100, 100);
    	debug_txt.background = true;
    	debug_txt.backgroundColor = 0xFFFFFF;
    	debug_txt.border = true;
    	debug_txt.borderColor = 0x000000;
    	debug_txt.html = true;
    	debug_txt.multiline = true;
    	debug_txt.selectable = true;
    	debug_txt.wordWrap = true;
    	var fmt:TextFormat = new TextFormat();
    	fmt.font = "Verdana";
    	fmt.size = 11;
    	debug_txt.setTextFormat(fmt);
    	debug_txt.setNewTextFormat(fmt);
    	onResize();
    	
    	debugHeader();
    	debug("Loading config xml...");
    	
    	NETCONNECTION_CALL_SUCCESS = "NetConnection.Call.Success";
    	CONFIG_XML_FILE = "fcs_app_reloader_config.xml";
    	
    	TIMEOUT_WIDTH = 100;
    	TIMEOUT_HEIGHT = 12;
    	config_xml = new XML();
    	config_xml.ignoreWhite = true;
    	config_xml.owner = this;
    	config_xml.onLoad = Delegate.create(this, onConfigLoaded);
    	config_xml.load(CONFIG_XML_FILE);
    }
    function onConfigLoaded(success:Boolean):Void
    {
    	debugHeader();
    	if(success == true)
    	{
    		debug("Config XML loaded, parsing...");
    		var at:Object 		= config_xml.firstChild.attributes
    		theHost				= (at.host == null) ? "localhost" : at.host;
    		thePort				= (at.port == null) ? "1111" : at.port;
    		theAppName 			= at.appname;
    		theUsername 		= at.username;
    		thePassword			= at.password;
    		theHangTime			= (at.hangtime == null) ? 3 : parseInt(at.hangtime);
    		if(isNaN(theHangTime) || theHangTime < 0 || theHangTime == null) theHangTime = 3;
    
    		var tabs:String = "\t\t\t\t";
    		debug("host: " 			+ theHost);
    		debug("port: " 			+ thePort);
    		debug("app name: " 		+ theAppName);
    		debug("user: " 			+ theUsername);
    		debug("password: " 		+ thePassword);
    		debug("hang time: " 	+ theHangTime);
    		
    		connect();
    	}
    	else
    	{
    		debug("Failed to find " + CONFIG_XML_FILE + ", aborting.");
    		lightFuse();
    	}
    }
    function connect()
    {
    	debugHeader();
    	debug("Connecting to FCS Admin...");
    	
    	nc = new NetConnection();
    	nc.owner = this;
    	nc.onStatus = function(info)
    	{
    		if(info.code == "NetConnection.Connect.Success")
    		{
    			debugHeader();
    			debug("Successfully connected!");
    			this.owner.onConnected();
    		}
    		else
    		{
    			debugHeader();
    			debug("Problem connecting to FCS Admin, aborting.");
    			this.owner.lightFuse();
    		}
    	};
    	nc.connect("rtmp://" + theHost + ":" + thePort + "/" + theAppName,
    		   theUsername,
    		   thePassword);
    }
    
    function onConnected()
    {
    	//getActiveInstances();
    	reloadApp("flex2test");
    }
    
    function reloadApp(str:String)
    {
    	debugHeader();
    	debug("Reloading app...");
    	nc.call("reloadApp",
    		      new Responder(this,
    				    reloadApp_result,
    				    reloadApp_fault),
    				    str);
    }
    function reloadApp_result(o)
    {
    	debugHeader();
    	if(o.code == NETCONNECTION_CALL_SUCCESS)
    	{
    		debug("App successfully rebooted, exiting.");
    	}
    	else
    	{
    		debug("Failed to reboot the app!");
    		debugProps(o);
    		debugProps(o.level);
    		debugProps(o.error);
    		debugProps(o.code);
    		debug(o.description);
    		debugProps(o.description);
    		debug("Exiting.");
    	}
    	
    	//debugProps(o);
    	lightFuse();
    }
    
    function reloadApp_fault(o)
    {
    	debugHeader();
    	debug("reloadApp_fault");
    	debugProps(o);
    	lightFuse();
    }
    
    
    /*
    function getActiveInstances()
    {
    	debugHeader();
    	debug("getActiveInstances...");
    	nc.call("getActiveInstances",
    	      new Responder(this,
    			    getActiveInstances_result,
    			    getActiveInstances_fault));
    }
    
    function getActiveInstances_result(o)
    {
    	debugHeader();
    	debug("getActiveInstances_result");
    	if(o.code == NETCONNECTION_CALL_SUCCESS)
    	{
    		debug(o.data.length + " active instances.");
    		debugProps(o.data);
    	}
    }
    
    function getActiveInstances_fault(o)
    {
    	debugHeader();
    	
    	debug("getActiveInstances_fault");
    	debugProps(o);
    }
    */
    function lightFuse()
    {
    	nc.onStatus = null;
    	nc.close();
    	nc = null;
    	
    	createEmptyMovieClip("timeout_mc", 1);
    	timeout_mc.st = getTimer();
    	timeout_mc.timeout = 3; // seconds
    	timeout_mc.onEnterFrame = function()
    	{
    		var ct:Number = getTimer();
    		var et:Number = ct - this.st;
    		if(et > (this.timeout * 1000))
    		{
    			this._parent.boom();
    		}
    		else
    		{
    			var p:Number = et / (this.timeout * 1000);
    			var w:Number = TIMEOUT_WIDTH * p;
    			
    		}
    		timeout_mc.clear();
    		timeout_mc.lineStyle(0, 0x000000);
    		timeout_mc.lineTo(TIMEOUT_WIDTH, 0);
    		timeout_mc.lineTo(TIMEOUT_WIDTH, TIMEOUT_HEIGHT);
    		timeout_mc.lineTo(0, TIMEOUT_HEIGHT);
    		timeout_mc.lineTo(0, 0);
    		
    		timeout_mc.moveTo(1, 1);
    		timeout_mc.beginFill(0x666666);
    		timeout_mc.lineTo(w, 1);
    		timeout_mc.lineTo(w, TIMEOUT_HEIGHT - 1);
    		timeout_mc.lineTo(1, TIMEOUT_HEIGHT - 1);
    		timeout_mc.lineTo(1, 1);
    		
    		timeout_mc.endFill();
    	};
    	timeout_mc.onEnterFrame(); // draw immediately
    	onResize();
    }
    function boom()
    {
    	delete timeout_mc.onEnterFrame;
    	timeout_mc.removeMovieClip();
    	fscommand("quit");
    }
    
    function onResize()
    {
    	debug_txt._x = 0;
    	debug_txt._y = 0;
    	debug_txt._width = Math.max(12, Stage.width);
    	debug_txt._height = Math.max(12, Stage.height);
    	
    	var sx:Number = Math.max(6, Stage.width);
    	var sy:Number = 6;
    	timeout_mc._x = sx - (timeout_mc._width) - 6;
    	timeout_mc._y = sy;
    }
    function debug(o)
    {
    	trace(o);
    	debug_txt.htmlText += o + "";
    }
    
    function debugHeader()
    {
    	//debug("---------------");
    }
    
    function debugProps(o)
    {
    	if(o instanceof Array)
    	{
    		var len:Number = o.length;
    		for(var i:Number = 0; i<len; i++)
    		{
    			trace(o[i]);
    		}
    	}
    	else
    	{
    		for(var p in o)
    		{
    			debug(p + ": " + o[p]);
    		}
    	}
    }
    
    init();
    
    
    

    Source Files – ZIP