November 4, 2009

A Demonstration of the ServerSocket API in AIR 2

In order to validate the new ServerSocket APIs in AIR 2, I wrote an application called HTTPeek. HTTPeek is a proxy server that sits between your browser and the network, and can show you HTTP request and response headers. It can handle compressed content, chunked content, binary content, etc. Check out the video below to see it in action:

Most of the HTTPeek code is dedicated to implementing just enough of the HTTP protocol to be an effective proxy, but the socket portion of the code is actually not all that complex. And the creation of the ServerSocket itself is very simple. The function below gets called when the user clicks on the "Listen" button:

private function onListen():void
{
    this.sockets = new Array();
    this.server = new ServerSocket();
    this.server.addEventListener(Event.CONNECT, onConnect);
    try
    {
        this.server.bind(Number(this.portNumber.text), String(this.interfaces.value));
        this.server.listen();
        this.listenButton.label = "Close";
    }
    catch (e:Error)
    {
        Alert.show(e.message, "Error", Alert.OK);
        return;
    }
    this.debugButton.enabled = true;
}

HTTPeek is open-source and hosted here on Google Code. Feel free to check out the source to see how it works.

Posted by cantrell at 8:20 AM. Link | Comments (5) | References

October 30, 2009

A Screencast Explaining and Demoing File Promises

File promises are kind of a difficult concept to describe, so I decided to explain them using a video. Hopefully this clarifies what file promises are, and why it's such a cool new feature of AIR 2.0:

Here's the code that creates a URLFilePromise for every file to be downloaded and puts them on the clipboard. But first, some code context:

private function onDragStart(e:DragEvent):void
{
    if (bucketList.selectedIndex == 0 || objectList.selectedItems == null) return;
    var c:Clipboard = new Clipboard();
    var items:Array = objectList.selectedItems;
    this.filePromises = new Array();
    for each (var item:Object in items)
    {
        var fp:URLFilePromise = new URLFilePromise();
        var req:URLRequest = new URLRequest(s3.getTemporaryObjectURL(bucketList.selectedItem.name, item.key, 60));
        fp.request = req;
        fp.relativePath = item.key;
        this.filePromises.push(fp);
    }
    c.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, this.filePromises);
    NativeDragManager.doDrag(objectList, c, null, null, null);
}

Below is the function that gets called when the drop is complete. It's responsible for opening the ProgressWindow component and passing it the array of URLFilePromise objects which the ProgressWindow component hooks into in order to receive progress events.

private function onDragComplete(e:DragEvent):void
{
    var progressWindow:ProgressWindow = new ProgressWindow();
    progressWindow.open(false);
    progressWindow.setFilePromises(this.filePromises);
}

The code for S3E is open source and available on Google Code.

Posted by cantrell at 6:11 AM. Link | Comments (2) | References

October 28, 2009

Global Error Handling in AIR 2.0 and Flash 10.1

One of the most popular feature announcements during my MAX presentation was global error handling (GEH). GEH lets you handle all uncaught errors (both synchronous errors and asynchronous error events) in one place in your code. Here's an example of how it will work:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="onApplicationComplete();">

    <mx:Script>
        <![CDATA[
            private function onApplicationComplete():void
            {
                loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtError);
            }

            private function onUncaughtError(e:UncaughtErrorEvent):void
            {
                // Do something with your error.
                trace(e.error, e.errorID);
            }
            
            private function onCauseError(e:MouseEvent):void
            {
                var foo:String = null;
                try
                {
                    trace(foo.length);
                }
                catch (e:TypeError)
                {
                    trace("This error is caught.");
                }
                
                // Since this error isn't caught, it will cause the global error handler to fire.
                trace(foo.length);
            }
        ]]>
    </mx:Script>
      
    <mx:Button label="Cause TypeError" click="onCauseError(event);"/>
 
</mx:WindowedApplication>

Registering for uncaught errors is hugely convenient, but it doesn't entirely excuse developers from handling errors where they're most likely to happen. For instance, you should still always register for things like IOError events and other errors that are likely to happen at some point, and that you can usually recover from. But starting with AIR 2.0 (and FP 10.1), you will also be able to register globally for errors that you weren't able to anticipate, and handle them at least somewhat gracefully.

The big question is what you do once you've caught an unhandled error. That will probably depend on the application. Since it's likely that a function exited without executing all the way through, there's no telling what state your app will be in. The safest thing to do is probably to log the error, show a very contrite dialog box, and then exit the app (after all, if the error had been predictable and easy to recover from, you should have caught it explicitly). You might try sending the error message to your server, including instructions for emailing the log file to a support email address, or if it's an internal application, provide a telephone extension to call for someone to come take a look. It's entirely up to you. We provide the API, you provide the solution.

Posted by cantrell at 10:46 AM. Link | Comments (7) | References

October 27, 2009

New AIR 2.0 API: URLRequest.idleTimeout

We recently added a new AIR 2.0 API which didn't make it into my MAX presentation:

These setters specify the amount of time (in milliseconds) that a connection will remain open before receiving any data. Certainly not as sexy as some of our other new features (native processes, file promises, multi-touch, accessibility, volume detection, socket servers, etc.), but if you're trying to use long polling, you'll probably find this API useful.

Posted by cantrell at 11:15 AM. Link | Comments (2) | References

October 26, 2009

Referencing ActionScript Reserved Words in E4X

If you have a chunk of XML with reserved ActionScript words in it, you won't be able to use E4X syntax like you're used to. For instance, say you need to parse the following XML:

var codeXML:XML =
<code>
    <class name="Order">
        <function name="getId">
            <access>public</access>
            <return>Number</return>
        </function>
    </class>
</code>;

Since this XML is full of reserved words, you won't be able to access nodes like this:

trace(codeXML.class.function.return);

Instead, use bracket syntax like this:

trace(codeXML["class"].@name);
trace(codeXML["class"]["function"].access);
trace(codeXML["class"]["function"]["return"]);

Posted by cantrell at 1:19 PM. Link | Comments (5) | References

October 23, 2009

Generating Dynamic XML With E4X in ActionScript

E4X makes working with XML in ActionScript extremely simple. Creating some XML is as easy as this:

var inventory:XML =
	<inventory>
		<product id="111" price="2999.99">Laptop</product>
	</inventory>;

But what if you want to generate the XML dynamically and use variables as attribute values or text nodes? Just use curly braces like this:

var products:Array = new Array();

products.push({name:"Laptop", id:111, price:2999.99});
products.push({name:"Mouse", id:222, price:49.99});
products.push({name:"Phone", id:333, price:199.99});

var inventory:XML = <inventory/>;

for each (var o:Object in products)
{
	inventory.appendChild(<product id={o.id} price={o.price}>{o.name}</product>);
}

The resulting XML is exactly what you'd expect:

<inventory>
	<product id="111" price="2999.99">Laptop</product>
	<product id="222" price="49.99">Mouse</product>
	<product id="333" price="199.99">Phone</product>
</inventory>

Simple and elegant!

Posted by cantrell at 9:08 AM. Link | Comments (3) | References

September 22, 2009

New ActionScript and Flex Spell Checking Engine on Adobe Labs

Adobe's Linguistic Services team recently launched a project code named Squiggly on Adobe Labs. From the project homepage:

Squiggly is a spell checking engine for Adobe® Flash® Player and Adobe AIR®. The Squiggly library allows you to easily add spell checking functionality in any Flex 3 based text control. The distribution package consists of a utility for building your own spelling dictionaries, a sample English dictionary, an ActionScript package that checks individual words for spelling accuracy, and sample code that demonstrates "check as you type" functionality.

The rest of the details can be found on Labs along with a simple demo.

Posted by cantrell at 9:56 AM. Link | Comments (0) | References

July 13, 2009

Ignoring Hidden Files in AIR

I'm writing an AIR application that recursively traverses the file system, and I ran into an interesting problem. I discovered a file called permStore which exists at the time that it's read, but just milliseconds later, it no longer exists. I wasn't able to definitively determine what this temporary file is for, but it exists (for very short periods of time) inside the .Spotlight-V100 directory which essentially means that it's supposed to be hidden, it's owned by the operating system, and it's really none of my business.

I determined pretty quickly that nothing in the .Spotlight-V100 directory was relevant to my program, so I decided I would just skip the entire directly, and any other hidden, or "dot" file, as well. One simple line of code did the trick:

if (!dir.isDirectory || dir.isHidden || !dir.exists || dir.name.search(/^\..*$/) != -1) return;

Here's the full function I wrote for recursively traversing a directory, ignoring hidden and "dot" files, and stopping at length determined by MAX_FILES.

private function iterateFiles(dir:File):void
{
    if (!dir.isDirectory || dir.isHidden || !dir.exists || dir.name.search(/^\..*$/) != -1) return;
    var listing:Array = dir.getDirectoryListing();
    for each(var f:File in listing)
    {
        if (this.fileList.length >= this.MAX_FILES) break;
        if (f.isHidden || !f.exists || f.name.search(/^\..*$/) != -1) continue;
        if (f.isDirectory)
        {
            this.iterateFiles(f);
        }
        else
        {
            this.fileList.push(f);
        }
    }
}

Posted by cantrell at 9:31 AM. Link | Comments (1) | References

June 1, 2009

ActionScript function for turning a date into a time interval

While working on a small Twitter utility, I wrote a function to turn a date into a time interval string. For instance, rather than formatting the date, it returns strings like "Just posted," "6 minutes ago," "4 hours ago," or "20 days ago". It's not a complex function, but I thought I'd post it here in case it might save someone a few minutes of coding. The less time we spend writing code someone else has already written, the more time we can spend innovating.

private function formatDate(d:Date):String
{
	var now:Date = new Date();
	var diff:Number = (now.time - d.time) / 1000; // convert to seconds
	if (diff < 60) // just posted
	{
		return "Just posted";
	}
	else if (diff < 3600) // n minutes ago
	{
		return (Math.round(diff / 60) + " minutes ago");
	}
	else if (diff < 86400) // n hours ago
	{
		return (Math.round(diff / 3600) + " hours ago");
	}
	else // n days ago
	{
		return (Math.round(diff / 86400) + " days ago");
	}
}

Posted by cantrell at 10:42 AM. Link | Comments (1) | References

September 17, 2008

Be careful how you call super()

Here's something all ActionScript 3 developers should know about extending classes and how parent constructors are called:

If class B extends class A, does class B have to call super() in its constructor in order to instantiate the parent class? Nope. What happens if you don't? The compiler inserts a call to the constructor for you. Usually.

The compiler just looks to see if you call super() at any point in your constructor. What if that call is inside of a conditional statement that evaluates to false? The default constructor won't get inserted, and the parent classes's constructor won't get called. Why is that a problem? You may very well get null reference exceptions since class level variables are often initialized in constructors. What's worse, you may only get them sometimes — only when your conditional statement evaluates to false.

Even if you determine that your code works whether super() is called or not, you still have to watch out. What if the implementation of the parent classes changes in the future? Code that used to work, and code that you could reasonably expect to continue working, may just break on you. I've actually seen this happen. It's not pretty.

How do you avoid this problem? Either call super(), or don't. Never put super() in a statement that may not execute, even if you find that your code works fine today since you never know what might happen to that parent class tomorrow.

Posted by cantrell at 10:41 AM. Link | Comments (6) | References

March 30, 2007

ActionScript 3 FedEx Libraries

I just checked some ActionScript 3 libraries into to Google code for accessing the FedEx web APIs. They aren't 100% complete, but they support a fair amount of functionality like:

The package is checked in as as3fedexlib. I expect to be adding additional functionality over time.

Posted by cantrell at 9:19 AM. Link | Comments (6) | References

March 28, 2007

vCard Parser in ActionScript 3

I just checked the vCard parser that I wrote for Maptacular into the as3corelib Google Code project. The entire project is available here, or you can go right to the vCard parser here.

It's simple, but it gets the job done.

Posted by cantrell at 3:18 PM. Link | Comments (7) | References

September 27, 2006

Getting Dictionary Definitions in ActionScript

There's a little known protocol out there called the "dict" protocol which enables clients to connect to a dictionary server and query it for definitions. Servers can (and usually do) host multiple dictionaries, many of which are specialized. For instance, a single server might host the "freedict" standard dictionary, some translation dictionaries, a thesaurus, and maybe a dictionary specific to computer jargon (FOLDOC: The Free Online Dictionary of Computing).

I wrote an Apollo application called "Lookup" which is a versatile dictionary client. It lets you choose a server, choose a dictionary on a particular server, and interactively query it. I'll release Lookup as soon as there is a public Apollo release, but in the meantime, I went ahead and checked the dict protocol library into the Adobe Labs source code repository. If you think you might have a need to look up words from ActionScript, check it out. It's not well documented, but if you start with the Dict class, it's pretty easy to figure out.

Posted by cantrell at 9:00 AM. Link | Comments (4) | References

August 17, 2006

ActionScript PNG and JPEG Encoders Updated

In October of 2005, Tinic Uro ported a PNG and a JPEG encoder to ActionScript 3. Shortly after, we added both to the collection of free ActionScript libraries on Adobe Labs. Tinic wrote them for an early alpha release of Flex Builder, so at some point along the line during the evolution of the compiler and the player, they stopped working.

I just checked in new versions of both that now compile and pass simple tests against the Flex 2 release. The PNG encoder is here, and the JPEG encoder is here. Let me know if you find any problems with them.

Posted by cantrell at 2:11 PM. Link | Comments (13) | References

July 27, 2006

A Proxy-savvy Socket in ActionScript 3

I'm working on an Apollo application that needs to make a TCP connection on a "non-standard" port, which, depending on your environment, usually means a port other than 80, 443, and few other commonly used ports. Development was going fine thanks to the new ActionScript 3 socket object until I went into the San Jose office to work for a day and discovered that the San Jose firewall is much stricter than the San Francisco one, and the port I was trying to connect on was blocked.

Fortunately, most environments with strict firewall rules also provide a way to get around them in the form of an HTTP proxy. After a little research and conferring with Chris Brichford, an Apollo engineer, we decided that this is a common enough problem that it would be worth solving in a generic way. So I wrote the RFC2817Socket class.

RFC 2817 "explains how to use the Upgrade mechanism in HTTP/1.1 to initiate Transport Layer Security (TLS) over an existing TCP connection." Not entirely relevant to our problem, however it also "documents the HTTP CONNECT method for establishing end-to-end tunnels across HTTP proxies" which means we can use a common HTTP proxy to make TCP connections on non-standard ports.

The RFC2817Socket class works exactly like the flash.net.Socket class, but if you give it proxy settings by calling setProxyInfo before calling connect, it will first handle the negotiation with the proxy server before dispatching the Event.CONNECT event. (If you don't set proxy settings, it will work just like the standard socket class.) All you have to know is your proxy server's hostname and port number, and RFC2817Socket takes care of the rest.

Unfortunately, this may not be the entire story, though. The reason I chose such a clumsy name for the class is that it will only work with proxy servers who adhere to RFC 2817. I suspect that most, if not all, proxies will use this technique (since it is a "standard"), however since I don't have a bunch of other proxies to test with, I have no way of knowing for certain. If it turns out that other proxies use different techniques for tunneling TCP connections, the thing to do would be to create other implementations in the same package, and then create a factory to return the right one. I'm hoping that the RFC2817Socket will work with most proxies out there so that won't be necessary, however if you find that it doesn't, it shouldn't be difficult to write one that does (if I can access the proxy that it doesn't work with, I'll even write it myself).

I should also mention that the entire tunneling portion of the RFC isn't implemented yet, so it doesn't do things like authentication and a couple of other things that are defined in the RFC. Adobe's proxy only uses the very basics, so that's all I implemented for now. If there's a demand for it, I'll add more.

For more information on how to get your hands on the RFC2817Socket class, or any of the other Adobe open source ActionScript 3 libraries, check out the this page on the Adobe Labs wiki.

Posted by cantrell at 10:41 AM. Link | Comments (3) | References

July 25, 2006

ActionScript 3 Makes My Life Easier

Now that I'm back and building Apollo apps, I'm obviously spending a lot of time with ActionScript 3 again. I've been using AS3 since there was a compiler capable of compiling it, but during my sabbatical, I wrote primarily Java and ColdFusion code. Now that I'm back, I have the pleasure of rediscovering all the things I love about ActionScript 3, and all the ways it makes my live easier than it was in the AS2 days:

Of course, there are a lot of other things about AS3 that I love . I know they've been thoroughly covered in the Flash blogosphere, but I'm having so much fun writing Flex 2 / ActionScript 3 code again that I couldn't help adding one more post. You can check out several examples of these things in action in the Adobe Labs source code repository browser (which I wrote in PHP, by the way, wishing the entire time that I could write it in AS3).

Posted by cantrell at 9:38 AM. Link | Comments (3) | References