Tuesday, September 15, 2009

Spring Actionscript version 0.8.1 released!

Its mainly a maintenance release with a slew of bugfixes, but there's some new functionality in there as well.
Read the full story at Herrodius' blog as per usual:

http://www.herrodius.com/blog/208

Download away:

http://www.springactionscript.org

Here's the changelog:

http://www.springactionscript.org/changes-report.html#a0.8.1

And should you find any bugs or have some feature request, please head on over to JIRA:

http://jira.springframework.org/browse/SESPRINGACTIONSCRIPTAS

cheers!

Friday, July 17, 2009

Spring Actionscript version 0.8 released!

Get the full story at herrodius blog:

herrodius.com

read the new documentation site front to back:

springactionscript.org

For all people upgrading from an older version and haven't done so, make sure you change the namespace declarations in your configuration files! Failing to do so will prevent springactionscript from working properly. (well, it won't work at all actually :))
This is what your XML should look like:

<objects xmlns="http://www.springactionscript.org/schema/objects"
xsi="http://www.w3.org/2001/XMLSchema-instance"
schemalocation="http://www.springactionscript.org/schema/objects
http://www.springactionscript.org/schema/objects/spring-actionscript-objects-1.0.xsd">

</objects>

That's the most important part to check.

Here's a quick glance at the changelog:

Changes in version 0.8 (16.07.2009)
-----------------------------------

General
* new website at www.springactionscript.org
* factored out common functionality into the AS3Commons projects (http://www.as3commons.org)
* major update to the documentation
* added autowire support
* added custom namespace handler support
* restructured subversion repository
* updated samples
* various performance improvements

Package org.springextensions.actionscript.cairngorm
* added IDataTranslator and IDataTranslatorAware interfaces
* added AbstractDataTranslatorAwareBusinessDelegate base class
* added 'batch command' support to CairngormFrontController

Package org.springextensions.actionscript.context.support.mxml
* added entire package for MXML support for application contexts

Package org.springextensions.actionscript.domain
* moved Enum to AS3Commons-Lang
* moved ICloneable to AS3Commons-Lang
* moved IEquals to AS3Commons-Lang

Package org.springextensions.actionscript.errors
* moved IllegalArgumentError to AS3Commons-Lang
* moved IllegalStateError to AS3Commons-Lang
* removed RuntimeError

Package org.springextensions.actionscript.ioc
* added "factoryObjectName" property to IObjectDefinition and ObjectDefinition

Package org.springextensions.actionscript.ioc.factory
* added ObjectDefinitionStoreError

Package org.springextensions.actionscript.ioc.factory.config
* added StageComponentInterceptionPostProcessor
* fixed e4x query to select nodes with Required metadata in RequiredMetadataObjectPostProcessor
* changed "getObject" and "getObjectType" so we can also return Function references from methods in FieldRetrievingFactoryObject
* added "getObjectDefinition" in IConfigurableListableObjectFactory

Package org.springextensions.actionscript.ioc.factory.config.flex
* added ApplicationPropertiesResolver

Package org.springextensions.actionscript.ioc.factory.support
* removed "allowObjectDefinitionOverriding" getter and setter from IObjectDefinitionRegistry
* error is now thrown when overriding an object definition while it is not allowed in DefaultListableObjectFactory
* added support for retrieving factory objects and autowiring in AbstractObjectFactory
* added ObjectDefinitionBuilder
* fixed bug with caching of factory objects in AbstractObjectFactory

Package org.springextensions.actionscript.ioc.factory.xml
* added StageInterceptionNamespaceHandler
* added spring-actionscript-stageinterception-1.0.xsd
* added spring_actionscript_stageinterception namespace
* added RPCNamespaceHandler
* added spring-actionscript-rpc-1.0.xsd
* added spring_actionscript_rpc namespace
* added UtilNamespaceHandler
* added spring-actionscript-util-1.0.xsd
* added spring_actionscript_util namespace
* added MessagingNamespaceHandler
* added spring-actionscript-messaging-1.0.xsd
* added spring_actionscript_messaging namespace
* added "resolveID" to AbstractObjectDefinitionParser method that allow object name lookup and generation

Package org.springextensions.actionscript.ioc.factory.xml.parser.support
* fixed XMLObjectDefinitionsParser so that abstract definitions are no longer parsed
* fixed XMLObjectDefinitionsParser to support nested custom nodes

Package org.springextensions.actionscript.ioc.factory.xml.parser.support.nodeparsers
* fixed ObjectNodeParser to support nested custom nodes

Package org.springextensions.actionscript.ioc.factory.xml.parser.support.nodeparsers.stageinterception
* added StageInterceptorNodeParser

Package org.springextensions.actionscript.ioc.factory.xml.preprocessors
* fixed parent properties not being overridden by child properties in ParentAttributePreprocessor
* fixed bug with properties replacement

Package org.springextensions.actionscript.mvcs
* removed AbstractService
* added IApplicationController
* added AbstractApplicationController
* added ApplicationEvent
* added ApplicationEventDispatcher
* added IApplicationEventListener

Package org.springextensions.actionscript.utils
* added ApplicationUtils
* moved ArrayUtils to AS3Commons-Lang
* moved Assert to AS3Commons-Lang
* moved DictionaryUtils to AS3Commons-Lang
* moved ObjectUtils to AS3Commons-Lang
* moved StringUtils to AS3Commons-Lang
* moved XMLUtils to AS3Commons-Lang
* removed TypedProxy

Samples
* added object-definition-parser-builder project

Go get it while its hot!

Sunday, May 31, 2009

as3commons v1.0 released

Just a quicky, as3commons version 1.0 has just been released, check it out here:

http://www.as3commons.org/

AS3Commons contains so far:

Reflect:
AS3Commons-reflect is an open-source library providing a reflection API for ActionScript 3.0. Being a pure actionscript library it can be used for any Flash/Flex/AIR project.

Logging:
AS3Commons-logging is an open-source library providing an abstraction over logging framework implementations. Being a pure actionscript library it can be used for any Flash/Flex/AIR project. Its use is recommended for use with other libraries/framework trying to be logging framework agnostic.

Go and check it out!

Thursday, April 30, 2009

Spring Actionscript + Cairngorm + Convention based coding == Less configuration

Spring Actionscript and its Cairngorm extensions provides you with a really nice way of configuring your events, command and delegates. Coupled with its AbstractCommandFactory it also makes your life very easy when it comes to injecting your commands and delegates with all sorts of properties.
It does, however, lead to quite a lot of configuration markup which, in turn, means a lot of typing for you, the developer.

Let's see if we can cut down on the amount of markup by using a little bit of convention based coding, a post processor and the new auto wiring functionality in Spring Actionscript.

Mind you, the new autowiring capabilities will be implemented in version 0.8 of Spring Actionscript, which isn't officialy released yet. So, to use these new features, you'll have to get the source form the SVN repository directly and compile your own version.
In my examples I also use the AS3Commons-Reflect library for a bit of reflection, you can find the lib here:
AS3-Reflect

Let's first define the naming convention for events, commands and delegates, in my example I use this:

<namespace>.events.<identifier>Event
<namespace>.commands.<identifier>Command
<namespace>.delegates.<identifier>Delegate

If I'm going to use the example of a GetProducts gesture the naming of the classes would then be, for example:

com.classes.cairngorm.events.GetProductsEvent
com.classes.cairngorm.commands.GetProductsCommand
com.classes.cairngorm.delegates.GetProductsDelegate

Let's start coding immediately.
The event class would be very straight-forward:

public class GetProductsEvent extends CairngormEvent
{
public static const EVENT_ID:String = "com.classes.cairngorm.events.GetProductsEvent";

public function GetProductsEvent()
{
super(EVENT_ID);
}

}

Do notice that I use the fully qualified classname of the event class as its ID though, this will be used later on when we associate the event with its corresponding command.

Normally we would configure our frontcontroller in the Spring Actionscript markup like this:

<object id="frontController" class="org.springextensions.actionscript.cairngorm.control.CairngormFrontController" singleton="true">
<constructor-arg>
<object>
<property name="com.classes.cairngorm.events.GetProductsEvent"
value="com.classes.cairngorm.commands.GetProductsCommand"/>
</constructor-arg>
</object>

But not this time, we only create an 'empty' frontcontroller, like so:

<object id="frontController" class="org.springextensions.actionscript.cairngorm.control.CairngormFrontController"/>

Instead, we create an IObjectPostProcessor implementation that creates the event/command object mapping for us, based on our naming convention.
Any IObjectPostProcessor that is declared in the Spring Actionscript configuration will automatically be called for every object that is instantiated by the Spring AS container. So also for our FrontController. Here's how such an IObjectPostProcessor can be used to configure your frontcontroller automatically:

public class FrontControllerPostprocessor extends Object implements IObjectPostProcessor
{
public function FrontControllerPostprocessor()
{
super();
}

private var _eventIds:Array;
//this is an array of event id strings:
public function set eventIds(value:Array):void
{
_eventIds = value;
}

//we won't do anything before the command is initialized, so just return null here
public function postProcessBeforeInitialization(object:*, objectName:String):*
{
return null;
}

//After the object has been initialised, check if the current object is an instance
//of CairngormFrontController. If so, perform the configuration:
public function postProcessAfterInitialization(object:*, objectName:String):*
{
var frontController:CairngormFrontController = (object as CairngormFrontController);
if (frontController != null)
{
frontController.commandMap = createCommandMap();
}
}

//Loop through the event id list and generate a valid command class based on each ID:
private function createCommandMap():Object
{
var commandMap:Object = {};
if (_eventIds != null)
{
_eventIds.forEach(function(item:String,index:int,arr:Array):void
{
commandMap[item] = createCommandClass(item);
});
}
return commandMap;
}


//Generate a class name for the specified eventId
private function createCommandClass(eventId:String):String
{
//The event id is the same as its fully qualified classname, like this:
//com.classes.cairngorm.events.GetProductsEvent
//therefore we split the path on its period character:
var parts:Array = eventId.split('.');
//the last element of the resulting array will be the eventname, this we
//want to keep for now:
var eventName:String = String(parts.pop());
//the next element is the 'events' part, let's get rid of this:
parts.pop();
//finally, we join back to the first part of the path, add the 'commands'
//part and replace the 'Event' part of the eventid with 'Command'
// and we end up with a valid command class:
//com.classes.cairngorm.commands.GetProductsCommand
return parts.join('.') + '.commands.' + eventName.replace('Event','Command');
}
}

That takes care of our frontcontroller, it now knows which command to create for which event, wonderful!

Now, our command will need a reference to the application model (because I'm pretty sure we will want the result of our GetProducts call to end up in the model, right?), for this we will make an interface called IModelLocatorAware. Its signature is very simple:

public interface IModelLocatorAware
{
function set modelInstance(value:IModelLocator):void;
}


And thusly, our GetProductsCommand's implementation will look like this:

public class GetProductsCommand extends AbstractResponderCommand implements IModelLocatorAware
{
public function GetProductsCommand()
{
super();
}

override public function execute(event:CairngormEvent):void
{
//extra logic goes here...
GetProductsDelegate(businessDelegate).getProducts();
}

private var _modelInstance:IModelLocator;
public function set modelInstance(value:IModelLocator):void
{
_modelInstance = value;
}

override public function result(data:Object):void
{
var resultEvent:ResultEvent = data as ResultEvent;
//extra logic goes here...
}

override public function fault(info:Object):void
{
var faultEvent:FaultEvent = info as FaultEvent;
//extra logic goes here...
}

}

As you see, in the execute method, this command expects its businessDelegate instance to be of type GetProductsDelegate.
We want to make sure that such a delegate has been injected into our command instance.
For this we will create a command factory. Spring Actionscript's CairngormFrontController allows us to add ICommandFactory instances that take care of the creation of certain classes of commands.
What we want is a factory that not only creates the right command, but also injects it with a reference to the application model and the right business delegate instance.
It doesn't take that much code, check out this command factory:

public class AutowiringCommandFactory implements ICommandFactory, IApplicationContextAware
{
public function AutowiringCommandFactory()
{
super();
}

//this factory implements the IApplicationContextAware interface, which means it
//will automatically be injected with the IApplicationContext instance that
//created it. We will use the IApplicationContext to perform the autowiring.
private var _applicationContext:IApplicationContext;
public function set applicationContext(value:IApplicationContext):void
{
_applicationContext = value;
}

//In this example we will be arrogant and claim we are able to create any
//command possible:
public function canCreate(clazz:Class):Boolean
{
return true;
}

//And here we actually create the reuqested class and inject it with the
//appropriate instances
public function createCommand(clazz:Class):ICommand
{
//First we retrieve the fully qualified classname and create an
//IObjectDefinition instance based on this classname. We then
//set the autowiring mode to 'byName' and let the IApplicationContext
//inject any properties that are defined in the container by name.
//(in our case the ModelLocator instance)
var className:String = Type.forClass(clazz).fullName;
var objectDefinition:IObjectDefinition = new ObjectDefinition(className);
objectDefinition.isSingleton = false;
objectDefinition.autoWireMode = ObjectDefinitionAutowire.AUTOWIRE_BYNAME;
var command:AbstractResponderCommand = new clazz() as AbstractResponderCommand;
_applicationContext.wire(command,objectDefinition);
//Then we create a business delegate based on the naming convention of our command classname:
var delegate:IBusinessDelegate = createBusinessDelegate(className);
delegate.responder = command;
command.businessDelegate = delegate;
return command;
}

//Assemble the business delegate class name, much in thew same way as we assembled the
//command class name based on the event ID:
private function createBusinessDelegate(commandClassName:String):IBusinessDelegate
{
var parts:Array = commandClassName.replace('::','.').split('.');
var delegateName:String = String(parts.pop());
parts.pop();
var businessDelegateName:String = parts.join('.') + '.delegates.' + delegateName.replace('Command','Delegate');
var cls:Class = (getDefinitionByName(businessDelegateName) as Class);
//com.classes.cairngorm.delegates.GetProductsDelegate
return new cls() as IBusinessDelegate;
}
}

So, now all we really need is the final Spring Actionscript configuration to tie this all together:

<?xml version="1.0" encoding="utf-8"?>
<objects>
<!-- The application model instance: -->
<object id="modelInstance" class="com.classes.cairngorm.model.ModelLocator" singleton="true"/>

<!-- The empty frontcontroller instance, injected with the AutowiringCommandFactory instance
which will create the configured commands: -->
<object id="frontController" class="org.springextensions.actionscript.cairngorm.control.CairngormFrontController">
<method-invocation name="addCommandFactory">
<arg>
<object class="com.classes.cairngorm.commands.factory.AutowiringCommandFactory"/>
</arg>
</method-invocation>
</object>

<!-- The post processor that will configure the frontcontroller, when new event/command/delegates are added
all you need to do is only add the event id to the specified array, the wiring will be done automatically
after that:
-->
<object id="frontControllerPostProcessor" class="com.classes.spring.postprocessors.FrontControllerPostprocessor">
<property name="eventIds">
<value>
<array>
<value>com.classes.cairngorm.events.GetProductsEvent</value>
</array>
</value>
</property>
</object>
</objects>

And that's it! With only the power of autowiring, post processing and a little bit of 'convention over configuration' we slimmed down our configuration markup considerably!

Of course, its debatable whether such an approach would be recommended in a large application with many, many events and commands. It does save a bunch of time typing out the markup but may make your overal source a little harder to understand for newcomers to the project.
This is a matter of preference I suppose, there's a case for and against it, I believe with proper project documentation this would be a viable solution. But it certainly isn't appropriate for every occasion, I admit.

I hope however, that this post does shine a little bit of light on the new autowiring capabilities of Spring Actionscript and may introduce some people to the wonderful world of the IObjectPostprocessor :)

Thursday, February 5, 2009

Whitespace frustrations


Update:
So, I got it to work eventually. Apparently if I also set this property:
XML.prettyPrinting = false;
I get the expected results. Not pretty at all, but at least it solved my problem for now...


I'm making this little Flex proof-of-technique application currently that deals with quite a bit of XML.
One of the first things I encountered was that an element looking like this:

<node>text with trailing whitespace </node>


The text of this node ended up in my object as 'text with trailing whitespace'. So *without* the trailing space, which was essential to DO end up there.

Well, a quick google turned out that before I parse my incoming XML I should add this line of code:

XML.ignoreWhitespace = false;

And then create my XML object.

On a side note, I think its kind of weird to set such a parameter through a static property. In my opinion it would have made more sense to just add a constructor argument which defaults to true, sort of like this:


var ignorewhitespace:Boolean = false;
var xmlObject:XML = new XML(BigFrakkingXMLString,ignorewhitespace);


But, unfortunately my problems weren't over.
On my way back to the server I'm supposed to create an XML object and send this.
So, what do I do? I set the static property: XML.ignoreWhitespace = false;
And I start creating elements and adding them to my XML object.
For example, I add an element like this:

var contentXML:XML = new XML("<Content>" + someStringOfText + "</Content>");

where the someStringOfText string has a value of, for example, "some text with a trailing space again ".

Yet, somehow the element ends up looking like this:

<Content>some text with a trailing space again</Content>


So, am I dealing with a bug here, or am I doing something fundamentally wrong here again?

I guess an option would be to just send value objects back to the server and handle all of the XML creation there. But I'm sort of buggered by the fact that I seem to hit a wall here over such a small little issue...

Friday, January 23, 2009

Different ways of forcing Flex to compile a class

As a user of Spring Actionscript I have, of course, run into the problem of classes not being compiled into the swf file.

When you have defined an object in the Spring AS configuration, but there's not direct reference in your source code to the class, the Flex compiler will optimize the class out of the swf and you end up with runtime errors such as this:


a class with the name 'x.y.z' could not be found


So far I know of three different ways to tackle this problem:

1. Add dummy variables in your source code like this:



privare var _dummy1:MyClass;
privare var _dummy2:MyOtherClass;



2. Add a Frame tag for each class you want to be included to one dummy class, and include only that dummy class. Code would look like this:



package com.myclasses
{
[Frame(extraClass="com.myclasses.MyClass")]
[Frame(extraClass="com.myclasses.MyOtherClass")]
public class MyDummyClass
{
}
}



3. Use a bit of XSLT, an ANT script and add this a builder in FlexBuilder. I have posted a PDF with a how-to on the springsource forums over here:

Spring Actionscript forum post

Hopefully in the future there will be a more elegant way of forcing certain classes to be included, but until then, pick one of these solutions.

Wednesday, January 14, 2009

Introducing: As3UndoHistory

Ok, so I've released a first version of my little undohistory lib on google code,
go and get it here:
as3undohistory

You can pull the sources right from the SVN repository there. So far I haven't made a release package because the whole thing's fairly small anyways.

There's three projects in the repository, the actual library, a unit test project and an example application which showcases what the library actually does.

I have no idea how well the library scales, I guess registering a few thousand objects with each 50 properties and then changing each property say 200 times will eventually eat a bunch of memory. It might be worth a stress test at some point. But on the other hand, I wouldn't know how to record changes in a different way than this. But then again, I'm just a bonehead, so if anyone has a better approach to this kind of edit history recording, I'd love to hear it.

I'll be working on the documentation some more this evening I guess because, right now, its almost non-existent. Should anybody be interested in using the library, go ahead and please give me a yell should you find any bugs.

Cheers.

Tuesday, January 13, 2009

undo history management in Flex

So I have a major flu which puts me in bed bored out of my skull...
I have this ongoing project, a Flex application, which is basically a sort of mind mapping system crossed with a drawing application. Now one of the next feature requests that the client made is an undo history.

Yikez.

So, I've been toying around with how to do it, and came up with, what I think, is a workable solution. I don't follow the usual command pattern which you find in most google searches on flex + undo history. What I basically needed was a recording of all the property changes on a bunch of objects.
I.e. property color was set to #00000, property width was set to 150, etc, etc.
These changes have to be recorded chronologically and undoing would basically be a matter of popping entries off a stack.

So, my solution makes use of some metadata and requires the objects that will be monitored to implement their properties with getters and setters.
An undoable property would look something like this:


[Undoable]
public function get testStringProperty():String
{
return _testStringProperty;
}
[Bindable(event="testStringPropertyChanged")]
public function set testStringProperty(value:String):void
{
if (value != _testStringProperty)
{
_testStringProperty = value;
dispatchEvent(new Event("testStringPropertyChanged"));
}
}

The object that implements this property needs to be registered once with a history manager class which analyzes the metadata and sets up eventlisteners,etc. In my implementation I use Christophe Herremans as3reflect library for retrieving the metadata.

My undo history manager also supports simple transactions, in the case several property changes represent only one user gesture. This is simply a matter of calling startTransaction and endTransaction on the history manager.

Anyways, I'm still busy cleaning up the code, finishing the documentation and making a little example application. But when I'm finished I'll release this on google code so maybe somebody else can benefit from this as well.

Or maybe someone on the interwebs thinks my solution is absolutely ridiculous, then I'd like to hear about it too :)

For now, I'm going back to being sick with the flu.

Cheers.