Issue with Flash Media Server 4 and Client.id value

In Flash Media Server 4, the way the Server-Side Actionscript Client.id property is represented has changed…but if you’re using the Admin API, it is still expecting the old version.

Previously, Flash clients connected to Flash Media Server would be given a server-generated id which looked something like “oAADAAAA” (this is the value you would see for the Client ID in the admin console). In FMS 4, the Client.id was changed to a 64-bit int in the form of “4702111234525315439”. It’s the same value represented two different ways. However, the Admin API was not updated to reflect the change and still expects the values in their old form.

To convert the new Client.id value back to the old one, I used the LongInt class found here:

ActionScript
< view plain text >
  1. var clientIDString:String = "4702111234525315439"; //should equal oAADAAAA
  2.  
  3. var longInt:LongInt = new LongInt(clientIDString);
  4. var bytes:ByteArray = new ByteArray();
  5. longInt.writeToByteArray(bytes);
  6. bytes.position = 0;
  7. trace(bytes.toString());  // prints AAAADAAo, so just need to reverse it

I’m definitely not in my comfort zone here, so if anyone knows a simpler way to to do this please let me know.

Speed up browsing SMB shares on a Mac

Browsing SMB shares in Mac OS X Snow Leopard (10.6) was killing me, sometimes taking almost a minute for a simple directory listing. Finally getting fed up I found a simple solution here that dramatically improved the speed of browsing the smb share: http://www.macwindows.com/snowleopard-filesharing.html#012610b .

Append html text to a spark TextArea component

Ran into an issue today trying to figure out how to append additional html formatted text to a Spark TextArea component. As stated here the insertText() and appendText() methods always add the new text as a literal string, meaning the html content doesn’t get parsed.
In Spark, when you’re dealing with the text components and doing any sort of formatting, you’re going to be dealing with TLF, the Text Layout Framework. It’s a complete pain in the ass and makes me sad to see how horrific flash/flex development has become. Anyway, with TLF you’re generally going to be dealing with TextFlow objects when getting/setting text on a text component. On top of that, you’ll be importing and exporting the text by running things through helper classes such as TextFlowUtil and TextConverter.

Here’s how I managed to “append” html text to a TextArea, in quotes because it actually exports the old text, merges the new text, and reassigns the whole things as the new content:

ActionScript
< view plain text >
  1. var oldText:String = TextConverter.export(messageOutput.textFlow, TextConverter.TEXT_FIELD_HTML_FORMAT, ConversionType.STRING_TYPE) as String;
  2. messageOutput.textFlow = TextConverter.importToFlow(oldText + message, TextConverter.TEXT_FIELD_HTML_FORMAT);

Definitely not as cool as textArea.htmlText.

NetStream.onPeerConnect handler must be a generic object

Doing some work with Flash Media Server and ran into a weird issue today. The Netstream class has a property called “client” which allows you to set an object to receive various callbacks. When I tried assigning the NetStream.onPeerConnect() callback to a method in my class, it would never fire. The only way I could get it to work was to create a generic object and proxy the callback through that:

ActionScript
< view plain text >
  1. listenerStream.client = { onPeerConnect:onPeerConnectHandler }

Mac eclipse word boundary issue fix

After updating Java on Snow Leopard (OS X 10.6) I hit a crazy issue with my Flash Builder install, where the editors no longer recognized the dot character as a word separator. This was driving me totally crazy since I’m always using the arrow keys to move around and edit text.

After giving up completely and being resigned to abandon FB and jump ship to FDT over this issue, I found a solution completely by accident while doing something unrelated:

Go to System Preferences > Language & Text > Text, and set the ‘Word Break’ dropdown to ‘English, United States (Computer)’. I needed to log out and log back in again, and everything was back to normal.

SpamAssassin 2010 bug

I have a DV server over at MediaTemple, and recently I started to get a lot of legitimate mail marked as spam. I checked out the raw headers in the email, and they all had FH_DATE_PAST_20XX in the X-Spam-Status header.

Turns out SpamAssassin had a bug with that particular rule, which would make it think 2010 dates were “grossly in the future”. Running sa-update should solve it.

More info at http://kb.mediatemple.net/questions/1792/SpamAssassin+2010+bug

Amazon S3 “RequestTimeTooSkewed” error

I was trying to connect to S3 today, and both Transmit and Forklift were giving me connection errors:

  1. <Error>
  2.     <Code>RequestTimeTooSkewed</Code>
  3.     <Message>The difference between the request time and the current time is too large.</Message>
  4.     <MaxAllowedSkewMilliseconds>900000</MaxAllowedSkewMilliseconds>
  5.     <RequestId>68532E845A05B015</RequestId>
  6.     <HostId>FfoBOO+7Kh+0Aa35f+Oa0P+Beeym+10LNyLVTGI3VgEHkVjotak8+L1QHaWOsIaf</HostId>
  7.     <RequestTime>Fri, 22 Jan 2010 00:10:57 GMT</RequestTime>
  8.     <ServerTime>2010-01-21T23:53:58Z</ServerTime>
  9. </Error>

The problem: my system clock was manually set 15 minutes fast. The s3 api will check the request time against the time on the server to see if it’s been too long, and if it has, it returns the above error. Fixing the time solved it.

Sending an ArrayCollection – Flex to amfphp

Here are my tweaks to amfphp for sending an ArrayCollection from Flex to amfphp. May not be the cleanest or most proper but it works. From what I understand, there is an ArrayCollection class included in Zend_AMF, but I haven’t looked into that.

I’ve been sending ArrayCollections from php to Flex for a while, it’s pretty simple and info for doing that can be found at Wade Arnold’s blog here. The issue going the other direction is that Flex only sends the AC’s underlying source array when serializing it for amf. So when amfphp unpacks the data, you’re dealing with a normal array.

Obviously, the first thing you’ll need is your ArrayCollection class to use (I use the version that extends ArrayObject at the link above), and make sure it is in your include path.

The first tweak to amfphp is to the method AMFBaseDeserializer->mapClass(). In that method, right after the checks looking for CommandMessage and RemotingMessage types, I added a similar check for ArrayCollection:

  1. if($typeIdentifier == "flex.messaging.io.ArrayCollection")
  2. {
  3.         return new ArrayCollection();
  4. }

Next change is to the AMFDeserializer->readAmf3Object() method. Near the middle of the method, around line 570, there is a check for ArrayCollection, and if found it will call the readAmf3Data() method. I changed it to pass in a value of “true” to the method, you’ll see why in a sec:

  1. if( $classDefinition['externalizable'] )
  2. {
  3.     if($type == 'flex.messaging.io.ArrayCollection')
  4.     {
  5.         $obj = $this->readAmf3Data(true);
  6.     }

Now just two small tweaks to the readAmf3Data() method. I changed the method signature to add an $isArrayCollection parameter, which just gets forwarded on when calling readAmf3Array. Here’s the whole method:

  1. function readAmf3Data($isArrayCollection = false)
  2. {
  3.     $type = $this->readByte();
  4.    
  5.     switch($type)
  6.     {
  7.         case 0x00 : return null; //undefined
  8.         case 0x01 : return null; //null
  9.         case 0x02 : return false; //boolean false
  10.         case 0x03 : return true;  //boolean true
  11.         case 0x04 : return $this->readAmf3Int();
  12.         case 0x05 : return $this->readDouble();
  13.         case 0x06 : return $this->readAmf3String();
  14.         case 0x07 : return $this->readAmf3XmlString();
  15.         case 0x08 : return $this->readAmf3Date();
  16.         case 0x09 : return $this->readAmf3Array($isArrayCollection);
  17.         case 0x0A : return $this->readAmf3Object();
  18.         case 0x0B : return $this->readAmf3XmlString();
  19.         case 0x0C : return $this->readAmf3ByteArray();
  20.         default: trigger_error("undefined Amf3 type encountered: " . $type, E_USER_ERROR);
  21.     }
  22. }

Now just another two small tweaks to the readAmf3Array method. As with the readAmf3Data() method, we add the $isArrayCollection parameter. Within the body of the method, I just check if that param is true when creating the $hashtable variable, and if it is, use an ArrayCollection instead of a regular array:

  1. function readAmf3Array($isArrayCollection = false)
  2. {
  3.     $handle = $this->readAmf3Int();
  4.     $inline = (($handle & 1)  != 0 ); $handle = $handle >> 1;
  5.  
  6.     if( $inline )
  7.     {
  8.         $hashtable = $isArrayCollection ? new ArrayCollection() : array();
  9.        
  10.         $this->storedObjects[] = & $hashtable;
  11.         $key = $this->readAmf3String();
  12.         while( $key != "" )
  13.         {
  14.             $value = $this->readAmf3Data();
  15.             $hashtable[$key] = $value;
  16.             $key = $this->readAmf3String();
  17.         }
  18.  
  19.         for($i = 0; $i < $handle; $i++)
  20.         {
  21.             //Grab the type for each element.
  22.             $value = $this->readAmf3Data();
  23.             $hashtable[$i] = $value;
  24.         }
  25.         return $hashtable;
  26.     }
  27.     else
  28.     {
  29.         return $this->storedObjects[$handle];
  30.     }
  31. }

That’s it. Now I can send the an AC to the server, serialize it, unserialize it and send it back to Flex with no issues. The one thing that may an issue is if one of the items within an AC is also another AC…haven’t checked out if that works yet.