DelegateInvoker for the Mate Flex Framework

| No Comments | No TrackBacks

I've been using the Mate Flex Framework a lot in recent history.   I think it's a solid framework that does a lot of things right and it's very lightweight and easy to use.  If you haven't used it yet, it's at least worth looking into.

That said, I was recently confronted with a situation in which the current service invokers were insufficient.  By service invokers, I mean specifically the RemoteObjectInvoker, HTTPServiceInvoker, and WebServiceInvoker classes.  Let me explain.

When you use the Web Service code generation tools from FlexBuilder Gumbo MAX Preview, you end up with a service class that wraps a web service and provides a well defined public interface.  Each method in the class returns an AsyncToken that allows you to add a responder to be notified of success/failure when the remote method call comes back.  The generated code looks something like this:

public class MyService extends WebServiceWrapper
{
    public function myRemoteMethod(param1:String, param2:int) : AsyncToken
    {
        loadWSDLIfNecessary();
        var operation:AbstractOperation = _serviceControl.getOperation("myRemoteMethod");
        var token:AsyncToken = operation.send(param1, param2);
        return token;
    }
}

If you've used Cairngorm before, this code should look very familiar.  It's the same type of code that you would normally see in the Delegate layer.

How do we use this type of delegate class in Mate?  My first approach was to try and use a WebServiceInvoker, but the delegate class is not a WebService.  My second approach was to try and leverage AsyncMethodInvoker, but that had a few other problems.  The biggest was that the success/failure listeners respond to the class itself and not the token returned by the method.  Additionally, the AsyncMethodInvoker doesn't integrate with the UnhandledFaultEvent approach to application-wide service fault handling.

So, I decided the best approach was to create a new kind of invoker, DelegateInvoker, specifically to handle calling these types of delegate classes. Usage is essentially the same as the other Mate service invokers:

<DelegateInvoker instance="{ services.myService }" method="myRemoteMethod"
	arguments="{ [ 'param1', 12 ] }"
	showBusyCursor="true">
			
	<resultHandlers>
		...
	</resultHandlers>
			
	<faultHandlers>
		...
	</faultHandlers>
</DelegateInvoker>

If no faultHandlers are defined, then an UnhandledFaultEvent is generated. This allows the DelegateInvoker to integrate with the application-wide service fault handler.

The DelegateInvoker can be used with any method that returns an AsyncToken. Behind the scenes, the DelegateInvoker attaches a responder to the AsyncToken returned by the method. The responder listens for success/failure and triggers the inner handlers when the method call returns. If the method does not return an AsyncToken to attach a responder to a runtime error is logged.

I've spoken with Nahuel about getting this new DelegateInvoker class incorporated into the Mate core. He will be adding it into the Mate in_development branch. It should be available for download in the next day or so, and I'll update this entry when the class is posted.

The DelegateInvoker class is available as of revision 93 in Mate's SVN. It's currently in the in_development branch, but will eventually be moved to the trunk in a future release.

Flex 2.0.1 SDK is dying

| No Comments | No TrackBacks
If you have a project that still uses the Flex 2.0.1 hotfix 3 SDK, now is a good time to consider upgrading.  When the new version of FlexBuilder is released (presumably in the second half of 2009), you will no longer be able to build your Flex 2.0.1 projects using FlexBuilder.

When you try to build a project with the Flex 2.0.1 SDK using the FlexBuilder Gumbo MAX Preview build, you'll encounter an error indicating that the flex-compiler-oem.jar is missing from the classpath.  I've logged this as the following issue: https://bugs.adobe.com/jira/browse/FB-15688.  The issue was resolved as Not a Bug with the comment Flex Builder 4 will not support Flex SDK 2.

I'll repeat that.  On a separate line.  In Bold.  Pay attention.

FlexBuilder 4 will not support Flex SDK 2.

Of course, ANT scripts and command-line compiles will continue to work.  However, losing IDE support should be a pretty compelling reason to look into the upgrade process.

Flex 3.0 was released February 25, 2008.  Since release the SDK has been updated to Flex 3.1 on August 15 and Flex 3.2 on October 29.

To learn more about the Flex SDK and download new versions, visit http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK.

For those of you looking to upgrade from Flex 2 to Flex 3, check out the compatibility issues in the Flex 3 release notes.

And now, I leave you with Monty Python's "Bring Out Your Dead".  Flex 2.0.1, you'll be stone dead in a moment.

AS3 JSON library now a little less strict

| No Comments | No TrackBacks
I've just checked in revision 83 of as3corelib that adds a strict flag to the AS3 JSON library.  In the past the library would generate an error if the JSON string was deemed invalid according to the syntax rules.  Now, based on the strict flag setting, the parser does a better job at allowing "valid" input even if the input doesn't match the JSON spec.

I've actually had this code completed since MAX 2008.  I wrote it on my flight from San Fransisco to Harrisburg, but haven't had the time to get it ready for check-in until now.

Here is the commit message:

  • Added throwing error if entire string cannot be consumed (when there are remaining characters left in the input stream)
  • Added "strict" flag to decoding process.  Default is true for backwards compatibility.  When false, relaxes input format rules.  Fixes #34, Fixes #41, Fixes #71:
    • Allows for hex specification for numbers, ex: { "myNum": 0xFFCC33 }
    • Allows for NaN to be parsed as a number, ex: { "myNum": NaN }
    • Allows for trailing commas in arrays, ex: [1,2,]
    • Allows for trailing commas in objects, ex: { "p1":true, "p2":false, }
    • Does not throw error if there were characters left in the input string
  • Added tests for strict flag
  • Added tests to better exercise comments
  • Updated tests to use proper assert types
  • Some minor cosmetic code cleanup
Usage is exactly the same as before, with the additional option of specifying the strict flag as part of the JSON.decode( string, strict ) call.  Example:

// Set strict mode to false to allow for trailing commas to not throw an error
var o:Array = JSON.decode( "[0,1,]", false ) as Array;
assertEquals( 0, o[0] );
assertEquals( 1, o[1] );

// Same usage as before, strict mode will throw a runtime error if the string is not valid
var a:Array = JSON.decode( "[0,1,]" ) as Array;

I still need to address #35 to allow for unquoted keys in objects to be parsed in non-strict mode.  This is a bit more complicated of a change because it changes how the tokenizer recognizes what is in the input stream.  It's coming though!

Note that a new .swc file was not yet posted.  If you want to experiment with these changes, you'll have to pull the latest from source.  I'll work with Mike to get the download .swc updated soon.

Hello World 2.0

| No Comments | No TrackBacks
It's been about a year since I've last posted something.  Pardon the dust...  it's not that I haven't had anything to say, it's more that I just haven't had the time to say it.

In fact, I've neglected my weblog so much that I had to upgrade through not just one but two major versions of MovableType.  It was interesting upgrading from 2.6 to 3.35 to 4.21 then finally to the latest 4.23.  The upgrade from 2.6 to 3.35 was smooth, but 3.35 to 4.21 was an epic fail.

Years and years ago when I first created my weblog I chose Berkeley DB as my database option.  I would've chosen MySQL but at the time either my host didn't support it or MT didn't support it, I don't really remember.  Today, it turns out that Brekeley DB is no longer supported in modern versions of MT.

I lost basically everything during the 3.35 to 4.21 upgrade when my database became unreadable.  I had some backups that I was able to use to import all of my old entries and comments without too much issue, but I wasn't able to recover the site design.  I had customized the default MT templates to leverage my ColdFusion custom tag templating system that I createad for my website, but the MT templates have radically changed over the years.  Also, the CSS and page structure were too structurally different and I wasn't able to quickly/easily modify the default MT template to work with my past site design.  I'm not a huge fan of the default design I'm using now so I'm still working on getting my old site design back (but I expect that it's going to take quite a bit time).

Not only that, but I also lost my code coloring plugin.  In the past I've used MTCodeBeautifier but this no longer works with the MT4.  I've started using SyntaxHighlighter which is a completely JavaScript-based solution and should be future-proof, however, I haven't been through all of my old entries to convert them.  I'm still working on that, too...

Anyway, what I have been up to lately?

Well... I've been up to my elbows in Flex development.  It's been a great year and I've worked on some really interesting and challenging projects.  Perhaps the biggest news, though, is that my employment situation has changed.

I'm now officially employed by Universal Mind as a Principal Architect (and have been since the summer).  It feels great to be part of such an elite and passionate team.  I'm really excited about what the future holds for both myself and the company, and I'm especially happy to see the immense positive reaction that SpatialKey has been getting.

Now that I'm back up and running, in the future I hope to get through the backlog of weblog ideas that I've been meaning to post for... well... over a year now.  I'm just as busy as always but now that I actually have modern weblog software that can do things like schedule entry posting dates, it should be easier for me to stay on top of things.

Stay tuned...

Oh, one final note, my RSS feed link has changed to http://feedproxy.google.com/darronschall.  The old rdf link is no longer being updated.

Creating Default, Cancelable Event Handlers

| 4 Comments | No TrackBacks

Events play a key role in Flex-based RIAs so it's important to have a solid understanding of them when building applications. Today I'm going to show you how to create a default event handler that performs an operation, and what steps are needed to cancel that default behavior to prevent it from happening.

A default event handler is a method of a component that handles the same event it dispatches and performs some default action. This happens a lot in component development. A component can dispatch an event, but also act on that event with some default behavior. You don't have to explicitly listen for the event because the default action happens automatically within the component itself.

For example, let's think about a "closeable" tab navigator. When you click on the "x" to close a tab, a "closeTab" event might be dispatched to signal that a tab is intended to be closed. The tab navigator itself listens for the "closeTab" event and provides a default behavior of removing the tab that was intended to be closed.

But what if you want to present the user with a confirm dialog first before the tab gets removed? In this situation, you don't want the default behavior to occur because the tab will be removed before the user has a chance to respond to the confirm dialog. So, you need a way to prevent the tab from being removed by the component.

There are 4 things associated with creating default event handlers and allowing them to be canceled. They are:

Rather than elaborating on these pieces with a few paragraphs of text, I think a code sample illustrates this best. Here's a sample component that has an "alarm" event with a default behavior of just showing an Alert dialog to let you know the default behavior has happened:

package
{

import flash.events.Event;

import mx.controls.Alert;
import mx.core.EventPriority;
import mx.core.UIComponent;	

[Event( name="alarm", type="flash.events.Event" )]

public class MyComponent extends UIComponent
{

	public function MyComponent()
	{
		// Assign a default event listener for the "alarm" event.  The key here
		// is the event priority of DEFAULT_HANDLER.  This is a low priority and 
		// allows normal listeners to execute first, giving them a chance to call
		// event.preventDefault() to cancel the event's default behavior
		addEventListener( "alarm", handleAlarm, false, EventPriority.DEFAULT_HANDLER, true );
	}
	
	/***
	 * Silly method here just to have the component dispatch an event that it
	 * has a default behavior for.
	 */
	public function triggerAlarm():void
	{
		// Create a new event of type alarm.  The key here is the third
		// parameter which signals that the event is allowed to be canceled
		// via event.preventDefault();
		dispatchEvent( new Event( "alarm", false, true ) ); 
	}
	
	/**
	 * The default behavior for handling the "alarm" event
	 */
	protected function handleAlarm( event:Event ):void
	{
		// Check to see whether or not the event had it's default behavior prevented
		// by another event listener
		if ( !event.isDefaultPrevented() )
		{
			// The event has NOT been canceled, so continue with
			// the default behavior
			Alert.show( "handleAlarm default handler executed from MyComponent" );
		}
	}
	
} // end class
} // end package

Here's a sample Flex application using this component. I add a listener for the "alarm" event, and if the user wants to cancel the default event behavior I call event.preventDefault() in my alarm listener:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*">
	
	<mx:Script>
		<![CDATA[
			
			protected function handleAlarm( event:Event ):void
			{
				// Just for example purposes, we have a flag in the UI to indicate
				// if we want to prevent the default event handler from executing
				// or not...
				if ( preventToggle.selected )
				{
					trace( "Default behavior is prevented" );
					event.preventDefault();
				}
				else
				{
					trace( "We didn't prevent the default behavior, so we'll see the alert from MyComponent." );
				}
			}
			
		]]>
	</mx:Script>
	
	<mx:Button label="Trigger Event" click="myComp.triggerAlarm();" />
	
	<mx:CheckBox id="preventToggle" label="Prevent default behavior?" selected="true" />
	
	<local:MyComponent id="myComp" alarm="handleAlarm( event );" />
	
</mx:Application>

So, there you have it! There's not much to it really, but it's a pretty useful technique. I'd actually like to see this covered a bit more in the Events portion of the livedocs site. Creating cancelable default event behaviors is a key part of Flex component development. Having a solid understanding of events will go a long way when it comes to building Flex applications.

Enjoy!

Launching Firefox from ANT on OSX

| 2 Comments | No TrackBacks

I've been a happy Linux user for quite some time, but I recently switched to OSX after getting a new Macbook laptop. After the switch, one of the first things that caught my attention was that my ANT tasks to launch a web browser would no longer work as expected.

Let's take a look at a quick example. I was launching Firefox with the following technique:

// In linux.properties
browser = firefox

// In win.properties
browser = C:/Program Files/Mozilla Firefox/firefox.exe
<!-- Launch the browser via the exec ANT task -->
<exec executable="${browser}" spawn="yes">
	<arg line="${output.index.html}" />
</exec>

Naturally, since this worked on Linux and Windows I figured it would work on OSX just fine as long as I got the path to the browser correct. The first thing that I tried was this:

browser = /Applications/Firefox.app/Contents/MacOS/firefox

When I tested this it almost worked. If I didn't have Firefox already open, it would open the browser correctly and display my target output file. However, things broke down if I already had an instance of Firefox open.

Rather than open the target output file in a new tab (which was the Linux/Win behavior), I would get an error dialog saying that Firefox was already in use and that I was only allowed to launch one instance of it at a time. It asked me to quit Firefox first, and then try again. Since I almost always have a browser window open to keep tabs on my webmail, obviously this wasn't a workable solution for me.

After a bit of digging and experimenting, I came up with the idea of using the "open" command and passing it the Firefox application as a parameter. This has been working great so far and only required a slight modification to the script:

// In mac.properties
#browser = open -a Firefox  #Does not work, need to pass -a Firefox as args to exec

browser = open
browser.args = -a Firefox
<!-- Updated the exec tag to use additional arguments -->
<exec executable="${browser}" spawn="yes">
	<arg line="${browser.args}" />
	<arg line="${output.index.html}" />
</exec>

I know this really isn't new or groundbreaking information here. But, I couldn't find an easy answer trying to google for my problem. I'm posting this to try and save someone else some time in the future who might be running into the same problem that I did. Enjoy!

P.S. I'm starting to like OSX, but I still prefer Linux. I'd consider myself somewhat of a power-user, and there's just too many things Linux has that OSX doesn't. However, I can't get my external monitor xinerama display to work in Linux (doh!) so I think I'm an official OSX user for the time being until I can get my xorg.conf configured right...

ActionScript 3 Singleton Redux

| 14 Comments | No TrackBacks

Not to beat a dead horse, but....

Ever since developers have been using ActionScript 3 the quest for the perfect Singleton has been underway. The heart of the issue is that there are no private constructors in ActionScript 3. Thus, it's been a tricky road trying to find the best way to prevent other developers from inadvertently invoking a Singleton's constructor and creating an additional (and erroneous) instance.

For example, a quick google search yields quite a few results. Here are a handful of them that I've tried to place in chronological order:

What do I think? My initial reaction was to not worry about it. Seriously, what does it matter? Do we really need compile time checking? Isn't runtime checking and unit testing enough? If a developer uses the class wrong, is that the class author's fault? As long as there is documentation as to what the class is and how it's used, then this is really a non-issue at best and trivial at worst.

Admittedly that's a bit of a naive attitude. However, in over a year of not caring about the private constructor issue I can honestly say that I've never run into a problem with singletons, or had any of my team members accidentally use them the wrong way.

It was actually a combination of other factors (and personal preference) that made me revisit the singleton pattern. The singleton technique that I prefer was developed to address the other issues I was running into and to reflect my personal coding style. While I was at it, I figured I might as well address the private constructor issue too. After all, we all love being smacked around by the compiler sometimes.

The technique I prefer has the following highlights:

  • Usage of static instance read-only property instead of a static getInstance() function. This is somewhat a matter of style and personal preference, but I prefer the succinctness of the read-only property. This is especially obvious when the singleton is used in binding expressions. Plus, getInstance() is so 1990s Java which ActionScript is most definitely not. I kid, I kid. But seriously, ".instance" is cooler.
  • Usage of a private lock class to prevent outside construction. While this is a common theme in the above links, my approach is slightly different. Instead of passing an instance of the private locking class to the constructor, I just pass the Class reference itself. This does two things. First, it clarifies the conditional check in the constructor. The test for the proper locking Class reference communicates the code's intent better than the traditional check for not null. Second, it encapsulates the private locking class itself. The constructor's argument is simply lock:Class which doesn't expose the name of the private locking class to the outside world, but still communicates that the constructor is locked.
  • Removal of 'Unable to bind' warnings in Flex 2. When you use a singleton in a binding expression, it's typical to have the Console log flooded with warning messages indicating that binding to the instance property will not be able to detect updates. My singleton version fixes these warnings.
  • Use of const instead of var for the instance storage. This one is pretty obvious, but using const here communicates intent better. The variable storing the singleton instance is not allowed to change so const is the better choice.

So, with that said, I thought it was worth throwing my singleton version into the ring. This is the method I prefer. If you like it, great. If not, that's fine too. No hard feelings. Really.

Sample code is after the jump.

Hooking dispatchEvent for Cairngorm Events

| No Comments | No TrackBacks

Gone are the days of cairngormEvent.dispatch(); and CairngormEventDispatcher.getInstance().dispatchEvent( cairngormEvent );. Say hello to dispatchEvent( cairngormEvent );!

A downside of using Cairngorm is that you need to remember that Cairngorm events are "special". Because of how the controller registers listeners, you need to dispatch Cairngorm events through the CairngormEventDispatcher instance in order for the associated commands to execute. Self-dispatching events were added in Cairngorm 2.2 to make things a little easier, but that addition still doesn't address the fact that these events are "different" from other events:

public function doLogin():void
{
	var event:LoginEvent = new LoginEvent( username.text, password.text );
	// Dispatch through the dispatcher directly
	CairngormEventDispatcher.getInstance().dispatchEvent( event );
	// Or, have the event dispatch itself using Cairngorm 2.2+
	event.dispatch();
}

A better approach, I think, is to remove the "special"-ness of Cairngorm events and treat them the same as any other regular event. Rather than having to remember that Cairngorm events get dispatched a special way, doesn't it make sense to simply use the standard dispatchEvent function?

In order to accomplish this, we need to abstract the Cairngorm event dispatching logic further up the chain. We still need to to dispatch Cairngorm events through the CairngormEventDispatcher instance, but we can create an abstraction that allows us to not have to know this extra information. This abstraction allows us to treat Cairngorm events as regular events and just dispatchEvent() them.

So how do we create this abstraction? Simple. We hook dispatchEvent at the UIComponent level. Example code after the jump.

Do you use the public Flex bug base?

| 1 Comment | No TrackBacks

If you're not familiar with the Flex Bug and Issue Management System, you need to do yourself a favor and get involved.

As I've spoken about before, when the Open Source Flex initiative was first announced one of the things I was most excited about was the opening of a publicly accessible issue management system. I've touched on a little bit of my reasoning here, but wanted to call attention to how awesome a system like this truly is.

If you use Flex day in and day out like I do, chances are you're going to run into issues or things that you think can be improved. That's just the nature of software development. Having access to the public bugbase allows you to be actively involved in identifying issues and helping to improve the product for other developers. Better yet, when you encounter a problem it allows you to defer the work of fixing or working around the issue to Adobe.

Let me share an example to demonstrate what I mean.

Earlier this summer I ran into a strange issue with the DateChooser component. Code that should have worked was failing for no apparent reason.

I opened up a web browser to the Flex bugbase, http://bugs.adobe.com/flex/, and searched for the issue. There are a lot of search options here, but I just used the "Quick Search" to get started looking, and then narrowed down via the extended options.

After searching for my specific issue, I didn't see anything related to the problem I was having. This meant that Adobe was not aware of a potential issue. So, I created a new issue describing my problem.

The process for creating a new issue is really straightforward. Use the "Create New Issue" link and just describe the problem your encountering. Having sample code to reproduce the problem really helps well. When you're done creating the issue it gets assigned an id.

For my particular issue, it was assigned SDK-12004. Since this issue was affecting my ability to complete some code I was working on, I was really interested to find out what Adobe thought and I wanted to be notified if anything changed on the issue. I clicked the "Watch it" link so that any changes to the issue would be emailed to me.

By watching the issue I was able to see it move through the Adobe QA system. I knew who was looking at it and what their thoughts where. I was emailed anytime someone commented on the issue, and ultimately I knew when it was fixed. In this case, another bug was found that was related to my original report but not quite the same, so SDK-12008 was opened as well. I opted to watch that issue too since it also affected what I was working on at the time.

When I saw that the issue was resolved as fixed, I found the comment with the associated build number of the fix. Here's where things get really interesting. The particular issue was listed as working fine in build 179224. Sweet, but where's the code for that?

Did you know there are nightly builds of the Flex SDK?

Did you know that when an issue in the bug system is fixed, you can grab the code for the fix from the nightly SDK having a build number greater than or equal to the issue's "fixed in build" number? You have to agree to the terms of the early build access license, but once you click the checkbox for that there is a matrix at the bottom of the page that populates with links to download the nightly build. Even better, there's a change log for each version.

I scanned the change logs looking for the change set that related to my SDK-12004 and SDK-12008 issues. When I found it, I made note of the files that were changed. Then, I downloaded that particular nightly build that contained the fix.

I should note here that the nightly builds are actually for the upcoming Flex 3 SDK, but my current project is still using the Flex 2.0.1 hotfix 3 SDK.

I used the "underriding" technique that Doug described here to apply the changes to my current SDK. Essentially, I took the code for the files that have changed and created versions of them in my local project. My project now has an "mx.controls.DateChooser" class defined that contains the code from the nightly build. Because it has the same fully qualified name as the default DateChooser in my Flex 2.0.1 hf 3 SDK, the version in my project takes greater precedence and it is what gets compiled into the ptoject and used.

This allowed me to apply only the the relevant changes from the nightly build without affecting the rest of my installed SDK. It took a little bit of effort to remove some of the "Flex 3" features from the updated code to downgrade it to work with the Flex 2.0.1 hf 3 SDK, but it wasn't a difficult process.

If you're already using Flex 3, it's even easier to apply a nightly build. In fact, you can just add the nightly build as a new installed SDK in FlexBuilder 3 and simply recompile your project with that SDK to incorporate the changes. It was only because I'm using an older SDK that I had to go through the process of manually applying the changes.

To summarize:

1. I ran into a bug
2. I looked to see if it was a known issue or already fixed
3. It wasn't, so I told Adobe about it
4. I watched the bug to keep informed of its progress
5. Adobe developed a fix for me
6. I downloaded the nightly SDK that included the code for the fix
7. I applied the fix in my project
8. All is well

A positive side effect here is that the fix now goes out to all developers who will be using the Flex 3 SDK going forward. By identifying issues and being involved you not only help yourself by getting Adobe to do the work for you, but you also help the entire Flex community by allowing Adobe to continually improve an already amazing product.

This process isn't limited to just bugs. If you think something could be done better, log an issue for it. It will get prioritized, and Adobe might even take your advice and build the enhancement into a future version.

So, if you haven't been using the Flex bug base, isn't it about time you start?

On [Transient], ObjectUtil.copy(), and Casting

| 1 Comment | No TrackBacks

I ran into an interesting situation not too long ago where ObjectUtil.copy wasn't working quite as I had expected. The solution that I came up with was to rely on [Transient] metadata. Confused? Let me explain...

ObjectUtil.copy is a wonderful utility method to perform a deep copy of an object. Its implementation is also amazingly simple:

var buffer:ByteArray = new ByteArray();
buffer.writeObject(value);
buffer.position = 0;
var result:Object = buffer.readObject();
return result;

Rather than using introspection and recursively copying properties from one object to another, the entire object is serialized into a array of bytes. When the bytes are deserialized, a brand new object is created copying all of the original contents.

Simple, but effective. Instead of writing a custom deep copy algorithm , the copy() method uses the built in Flash Player AMF capabilities. This is the same serialization that Flash Remoting uses. When you send an object to the server via Flash Remoting (<mx:RemoteObject/>), you send an AMF array of bytes describing the object that the server deserializes to create an identical object on the server.

Because of using AMF behind the scenes to perform object copies, a few interesting things happen:

  • If you try to cast the results of a copy to a class, the cast might fail.
  • Using the [Transient] metadata tag affects the output of copy().

When an object is deserialized from AMF, it does not automatically get created as a class instance. The object might have all of the properties of a class instance, but it will not be a true class instance unless the AMF packet includes type information about the object. The type information gets added to AMF in one of two ways:

If you're using [RemoteClass] metadata on a class instance being copied, then it is safe to cast the result as that particular class instance:

// The works if Book has [RemoteClass] metadata
var bookCopy:Book = Book( ObjectUtil.copy( book ) );

If you're not using [RemoteClass], before performing the copy you need to register the class against a string alias. The alias is written to the AMF packet so that when the object is deserialized, it can be created as the proper type. For example:

// Book doesn't have [RemoteClass] metadata, so associate the my.package.Book string
// with a reference to the Book class.
registerClassAlias( "my.package.Book", Book );

// Now we can cast the result of the copy without errors.
var bookCopy:Book = Book( ObjectUtil.copy( book ) );

So, what about [Transient], and how is this related to the issue I ran into where the copy wasn't working as I had expected? Now that you know how copy works behind the scenes, let's talk about [Transient].

The [Transient] metadata tag is not very well documented in Flex 2 or 2.0.1. If you look in some of the LiveCycle Data Services documentation you can see mentions of it, but there isn't a dedicated page on the subject. In the beta Flex 3 documentation, [Transient] is finally explained. There is also some documentation gathered on The Flex Non-Docs weblog.

Essentially, what it comes to is that using [Transient] on a property removes that property from the AMF packet during serialization. This is important. The reason [Transient] properties are not sent over the wire via Flash Remoting is because, again, they're not included in the AMF packet.

With the background information explained, onto the problem I ran into. Here's a small class that doesn't copy correctly (after the jump):

Flex.org - The Directory for Flex

Recent Comments

  • darron: You can download the project by following the instructions on read more
  • jim: I'm researching card engines at the moment and this project read more
  • Mike Keesey: I'll second what the last poster said, except that I've read more
  • Josh Dura: Great article Darron. Thanks! I'm just starting to learn AS3 read more
  • darron: You would need to wrap the default behavior in a read more
  • Ben Clinkinbeard: I bet you're not surprised to see me here. :) read more
  • darron: Thanks for the comment Luke - I didn't know about read more
  • Luke Bayes: Hey Darron, There is one major difference between the open read more
  • Mike Keesey: Nice! I like it. (I mean, I don't like it read more
  • Ryan Taylor: Good thoughts, Darron. I've certainly used the pattern in a read more

Archives