13 Extending the Framework

All previous chapters demonstrated the Parsley feature set from the perspective of an application developer. But since Parsley provides such basic IOC capabilites like Dependency Injection or fully decoupled Messaging it is equally important that the framework is easy to extend. If you are going to build your own framwork, whether inhouse or Open Source, you wouldn't want to reinvent the wheel for these basic features and prefer to integrate with existing solutions.

So extensibility is an area a lot of thought has been put into. And hopefully when starting to build your own extensions you will enjoy the huge number of available extensions points, the flexibility and the ease of use. One extension point where Parsley particularly shines is the ObjectDefinitionDecorator interface which allows you to write a single extension class and use it as a custom Metadata, MXML or XML configuration tag.

When browsing the Parsley code base you'll notice that almost everything in the internal API lives behind interfaces. This makes it easy to create your own implementations for a particular aspect of the framework and switch it while still using the default implementations for other internal services.

13.1 Available Extension Points

Let's start with a quick overview over the extensible parts of the framework.

Other sections in this chapter deal with information about internal APIs that are useful for more than just one of the extension points listed above:

Finally there are further features of the framework that do not even require to directly talk to the framework API or implement framework interfaces, but nevertheless may be often used in a way similar to other types of extensions. These are:

The two links above both point to the lifecycle chapter, these features are not discussed in this chapter.

13.2 Custom Metadata Tags

Being one of the most widely used extension points it allows to create custom metadata you can add to any object managed by Parsley. Like with most other extension points all the builtin tags of the core framework like [Inject] or [MessageHandler] are implemented as extensions themselves.

13.2.1 The ObjectDefinitionDecorator Interface

Every custom metadata tag has to implements a simple interface with just one method:

public interface ObjectDefinitionDecorator {
    
    function decorate (builder:ObjectDefinitionBuilder) : void;
    
}

The method will be invoked by the container for each configuration tag it encounters for an object that was added to the container. It doesn't matter whether it is a builtin configuration tag or a custom extension tag, or whether it is a metadata tag, an MXML or XML tag. As long as the tag is mapped to a class that implements this interface the container will invoke it for each tag on each object.

The builder parameter it passes to the decorator can be used to specify configuration options for the object definition it currently processes.

13.2.2 Tasks performed by Custom Metadata

A custom metadata tag usually performs one or both of the following two tasks:

For your own applications you usually implement two types of custom metadata tags. One type is a fully generic, reusable extension. In this case you are of course invited to blog about it, post on our forum or contribute the tag to the framework, if you think that it is generally useful. The other type of tag would be specific to your application. Even if it is just reused in a few areas of a single application, a tag may very well help removing a lot of unnecessary plumbing.

13.2.3 Sample Implementations

As the builtin tags all use this extension point themselves, you can examine these implementations as a starting point for you own tags. Most of the tag implementations reside in the parsley-config source folder in the package org.spicefactory.parsley.tag. As you'll see most of them are fairly simply, only delegating to the configuration DSL. You should always strive to reduce the logic in your tag implementations, making it easier to apply the feature programmatically in cases where using metadata is not appropiate.

To give a simple example, lets assume that you have an application that dispatches NavigationEvents managed by Parsley whenever the user navigates to a new tab or window. Instead of using the generic MessageHandler tag for getting notified you could create a custom tag that applies only for these navigation events:

[NavigateTo("addUserForm")]
public function handleNavigation () : void 

The implementation for this tag would look like this:

[Metadata(types="method", multiple="true")]
public class NavigateTo implements ObjectDefinitionDecorator {

    [DefaultProperty]
    public var target:String;

    [Target]
    public var method:String;

    public var scope:String = ScopeName.GLOBAL;    

    public function decorate (builder:ObjectDefinitionBuilder) : void {
        
        MessageHandler
            .forMethod(method)
                .scope(scope)
                .type(NavigationEvent)
                .selector(target)
                    .apply(builder);
    }
    
}

The [Metadata] tag is required to configure the metadata tag, it is explained in 13.2.5 Registering Metadata Tags.

The [DefaultProperty] tag marks a property that can be used without attribute name. Without that tag you'd need to always explicitly specify the attribute name, so the example from above would read: [NavigateTo(target="addUserForm")].

The [Target] attribute tells the framework that this is the property that holds the name of the member of the class (property or method). For metadata to be placed on the class level this is not required of course.

The scope property allows to define handlers only interested in navigation events for a particular scope.

Finally we use these property values in building a regular MessageHandler and applying it to the ObjectDefinitionBuilder. We are passing the scope value, using the global scope as the default in case the attribute is omitted. The name of the navigation target is used as the selector for the handler, while the message type (NavigationEvent) is hard-coded in this case.

13.2.4 The ObjectProcessor Interface

In the simple example in the preceding section we merely applied an existing feature using the configuration DSL. When you want to add custom functionality not provided out of the box, some more work is required. In many cases this involves performing some configuration task on the target object once it gets instantiated, but before it is passed to the application. For this purpose an ObjectProcessor can be used. This is the interface that needs to be implemented:

public interface ObjectProcessor {
    
    function init (target: ManagedObject) : void;

    function destroy (target: ManagedObject) : void;
    
}

Again, fairly simple. The init method will be invoked after the object was instantiated, but before its [Init] method will be invoked (this is the default order which can be changed in configuration). Likewise the destroy method will be invoked when the object was removed from the Context.

The 1:N Relationship between Decorators and Processors

Understanding this concept is important before implementing your first ObjectProcessor. There is a 1:N relationship between a decorator and a processor. When you implement the ObjectDefinitionDecorator interface like shown in preceding sections, it will be invoked once for a particular definition in your Context. If it is a definition created by an <Object> tag or any other tag which represents a singleton, then the decorator will also map to only one processor. But for other types of objects, like those declared with <DynamicObject> or <Command> multiple instances may be created from a single definition. In this case one decorator can map to multiple processors, as a new processor might get created for each new instance. Whether you need a new processor instance for each new target instance solely depends on whether you keep state inside your processor that only applies to one target. In this case you should implement the StatefulProcessor subinterface which adds a clone method to create new instances for each new target.

With this mechanism you have the option to keep state that only applies to a single target instance inside the processor. This greatly simplifies a lot of common tasks performed by processors as most processors need to unregister some configuration artifact in the destroy method which they created and applied in the init method, like adding and removing a message handler which always points to one concrete target only.

Example: The MessageDispatcherProcessor

All that may sound more complicated than it actually is, so let's look at an example for a fairly simple stateful processor, the implementation for the builtin [MessageDispatcher] tag tag.

public class MessageDispatcherProcessor implements PropertyProcessor, StatefulProcessor {
    
    private var scope:String;
    
    private var property: Property;
    
    private var dispatcher:MessageDispatcher;
    
    
    function MessageDispatcherProcessor (scope:String = null) {
        this.scope = scope;
    }
    
    
    /* implementing PropertyProcessor */
    
    public function targetProperty (property: Property): void {
        this.property = property;
    }
    
    
    /* implementing ObjectProcessor */

    public function init (target: ManagedObject) : void {
        this.dispatcher = new MessageDispatcher(target.context.scopeManager, scope);
        property.setValue(target.instance, dispatcher.dispatchMessage);
    }
    
    public function destroy (target: ManagedObject) : void {
        dispatcher.disable();
    }


    /* implementing StatefulProcessor */
    
    public function clone () : StatefulProcessor {
        return new MessageDispatcherProcessor(scope);
    }
    
}

The init methods simply creates the dispatcher function (using a utility class) and injects it into the target property. Later the dispatcher gets disabled once the object gets removed from the Context to prevent it from getting used after the target instance became unmanaged. Since this is tied to the lifecycle of one particular instance the processor is stateful. Therefore it implements StatefulProcessor in addition to PropertyProcessor, both subinterfaces of ObjectProcessor. The clone method only passes the scope name to the constructor of the new processor instance, as that does not change from target to target. The dispatcher is not getting passed, like the property instance which will get passed into the new instance by the framework anyway.

Finally, in the corresponding decorator, which represents the actual tag you can use in metadata, MXML and XML, you need to add the processor to the definition:

[Metadata(name="MessageDispatcher", types="property")]
[XmlMapping(elementName="message-dispatcher")]
public class MessageDispatcherDecorator implements ObjectDefinitionDecorator {

    [Target]
    public var property:String;
    
    public var scope:String;
    
    
    public function decorate (builder:ObjectDefinitionBuilder) : void {
        builder
            .property(property)
            .process(new MessageDispatcherProcessor(scope))
            .mustWrite()
            .expectType(Function);
    }
    

Here we simply create a new instance of our processor and apply it to the ObjectDefinitionBuilder. You can also see that it is convenient to implement PropertyProcessor. You could do the reflection yourself, but the syntax above is a handy shortcut for just passing the name of the property as a String, and then relying on the framework to check whether the property actually exists on the target class and whether the validation rules you specified are met by that property. In this case we know the property must be writable and of type Function. If everything is correct, the framework will then pass the Property instance (from Spicelib Reflect API) to the targetProperty method of your PropertyProcessor.

Finally in rare cases you may also want to override the default phase the processor gets invoked in. By default all processor get invoked in the preInit phase except for the processor for the [Init] method itseld, which executes in the init phase. If you need your processor to be applied as a final step eben after the init method had been called you can specify a phase explicitly:

public function decorate (builder:ObjectDefinitionBuilder) : void {
    builder
        .property(property)
        .process(new MyCustomProcessor(comeParam))
        .mustWrite()
        .initIn(InitPhase.postInit());
}

If in very rare cases you even need ordering within a particular phase, you can pass an additional order attribute (the default is 0):

public function decorate (builder:ObjectDefinitionBuilder) : void {
    builder
        .property(property)
        .process(new MyCustomProcessor(comeParam))
        .mustWrite()
        .initIn(InitPhase.postInit(10));
}

13.2.5 Registering Metadata Tags

Finally you have to tell the framework and the Flex compiler about your custom metadata.

The Parsley support for metadata configuration tags is built on top of the Spicelib Reflection API which offers the capability to map attributes of metadata tags to properties of classes, bringing AS3 metadata a bit closer to the type-safe nature of Java Annotations for example. See 19.8 Mapping classes to metadata tags in the Spicelib Manual for details.

Making the custom tag available for metadata configuration requires three steps:

1. Add the [Metadata] tag to the class declaration:

[Metadata(types="method")]
public class NavigatorTo implements ObjectDefinitionDecorator {

With the types attribute we specify on which declarations we want the tag to be processed. In this case we only allow it on method declarations. Other tags may be placed on properties or on the class level.

2. Add the class to the Spicelib Metadata Support:

Metadata.registerMetadataClass(NavigatorTo);

This must happen before you create your first Parsley Context.

3. Add the metadata tag to mxmlc or compc compiler arguments:

-keep-as3-metadata+=NavigatorTo

If you create a reusable library containing Parsley tag extensions it is recommended to compile the library into an SWC. In this case you no longer have to explicitly add the compiler flag for applications that use this SWC as they will be automatically included for all applications that use this SWC. In this case you only have to provide the compiler argument to compc when creating the SWC.

For some general explanations on metadata configuration, like metadata inheritance or inconsistencies of the Flex compiler in dealing with metadata, see 3.1 Configuration with AS3 Metadata.

13.3 Custom MXML and XML Tags

In many applications you may be able to stick to the builtin tags like <Object> or <Command> for your MXML or XML configuration files. But in case you want to add custom options the format is easy to extend. Like with the metadata extension point, all the builtin tags like <Object> or <Command> are implemented as extensions themselves.

13.3.1 The Structure of Configuration Files

Although technically there are a lot of differences between MXML and XML configuration, they are very similar from the framework's perspective. This means that you usually only have to create one implementation of one of the available interfaces and will then be able to use the same tag in MXML and XML configuration.

The following example shows all the existing types of MXML and XML tags in a single configuration artefact.

This example shows an MXML configuration class, but apart from minor syntactical differences the structure for XML configuration files is exactly the same, and the same set of interfaces is supported.

As you see, you'll have two choices for tags on the root level: tags that implement RootConfigurationElement or literal values. In case of literal values they are interpreted as an instance that should be added to the Context as is. The class may have metadata tags for configuration which the framework will process, but the tag cannot have those framework specific MXML child tags. In case of tags implementing RootConfigurationElement the framework will invoke the process method. For details see 13.3.2 The RootConfigurationElement Interface and 13.3.4 Example: The MessageConfirmation Tag.

The NestedConfigurationElement shares some similarities with the interface for root tags. It can be used wherever tags allow child tags that represent values, like the <Property>, <ConstructorArgs> or <Array> tags. Like with root tags in any of these places literal values are allowed and are used as is, while for the tags implementing the framework interface their resolve method will be invoked first to determine the actual value represented by that tag. For details see 13.3.3 The NestedConfigurationElement Interface.

Finally there are places where tags implementing ObjectDefinitionDecorator can be used. Amongst other places they can be used as immediate children of the root tags <Object> or <DynamicObject>. This is the same interface that custom metadata tags have to implement. Thus a single implementation of that interface can be used as metadata, MXML or XML, allowing for a great level of flexibility.

The reason that they are allowed immediately below tags like <Object> is that they contain logic to configure an object, in the case of MXML or XML usage always the object defined by its parent tag. Custom metadata tags can support nested decorator tags, too. For this they must have a DefaultProperty of type Array that expects elements of type ObjectDefinitionDecorator. And of course the tag must contain logic that knows how to apply the decorators to the corresponding definition.

Most of the builtin metadata tags support MXML or XML configuration, too. Consider the following metadata tag:

[MessageHandler(scope="local")]
public function handleMessage (msg:MyMessage) : void

The equivalent XML configuration would be:

<object type="com.foo.MyClass">
    <message-handler method="handleMessage" scope="local"/>
</object>

As you see the major difference is that you have to specify the method name in XML. This is not needed with metadata as the name will be deduced from the property the tag is placed upon.

13.3.2 The RootConfigurationElement Interface

This interface can be implemented by tags that are intended for use on the top level of MXML or XML configuration files.

public interface RootConfigurationElement {
    
    function process (registry: ObjectDefinitionRegistry) : void;
    
}

In many cases the implementation of the process method creates, configures and registers a single definition for an object. Many of the builtin tags like <Object> behave like that. But it is not a requirement. The tag can alternatively register multiple object definitions or even none at all and instead do any other kind of processing.

The ObjectDefinitionRegistry instance passed to the process method gives access to the object definition builders. For instruction on how to use the DSL see 13.4 Working with ObjectDefinitions.

13.3.3 The NestedConfigurationElement Interface

This interface is similar to RootConfigurationElement, but comes with a subtle difference: In contrast to the root tag which basically can perform any kind of processing, this tag has to represent exactly one value. The reason is that such a tag is only used in places where a value is expected. For child tags of the <Array> tag for example it is expected that each child tag represents an element of that Array. Therefor the interface is slightly different:

public interface NestedConfigurationElement {
    
    function resolve (registry: ObjectDefinitionRegistry) : Object;
    
} 

The resolve method must return a value. This is interpreted as the actual value the tag represents. But there can be one more level of indirection: The returned value can represent something which needs to be resolved at the time the value is applied to the instance it belongs to. This applies for the <ObjectRef> tag for example. That tag has to resolve an id value or an object type at the time the target object is configured. Such a return value must implement the ResolvableValue interface. For examples you may examine the NestedObjectTag or ObjectReferenceTag classes.

13.3.4 Example: The MessageConfirmation Tag

One of the simplest tag implementations is the <MessageConfirmation> utility tag, so it serves well as a quick example. The tag allows a message type and optional selector to be defined that should trigger a confirmation dialog before proceeding with message processing. It can be used like this in MXML configuration classes:

<MessageConfirmation
    type="{DeleteUserMessage}"
    scope="local"
    title="Confirmation"
    text="Do you really want to delete this user?"
/>

So whenever a message of type DeleteUserMessage is dispatched this utility will kick in first and show the dialog using the provided text. When the user clicks cancel, message processing will be cancelled. When she clicks Ok, processing of the message resumes, invoking any [MessageHandler] that was defined for that message type.

This is how the implementation looks like:

public class MessageConfirmationTag implements RootConfigurationElement {

    public var text:String;
    
    public var title:String;
    
    public var scope:String = ScopeName.GLOBAL;
    
    public var type:Class;
    
    public var selector:*;
    
    public function process (registry: ObjectDefinitionRegistry) : void {
        
        var builder:ObjectDefinitionBuilder 
                = registry.builders.forClass(MessageConfirmation);
        
        builder
            .constructorArgs(title, text);
                
        MessageHandler
            .forMethod("showAlert")
                .type(type)
                .selector(selector)
                .scope(scope)
                    .apply(builder);
        
        builder
            .asSingleton()
                .register();
    }
    
}

It should be almost self-explanatory. It is a good example for how clean and simple the use of the configuration DSL can make the implementation of such a tag. It merely uses the values of the properties set by the user to talk to the API to define constructor arguments and a message receiver for the actual utility class. Note that the tag as such does not contain the actual processing logic. This has been moved into the MessageConfirmation class that is configured and registered by this tag. It is always good to keep configuration and runtime logic separate.

13.3.5 Creating Custom MXML Namespaces

Creating a namespace for your custom MXML tags is not required. You can use them like any other class in MXML: map a tag prefix to the package of your tag and use it as is. But if you create a larger collection of reusable tags your users will probably be happy if you provide a single namespace that holds all the tags of the extension.

Creating such a namespace is a Flex SDK feature. Nothing is specific to Parsley in this respect, so you can simply look up the Flex documentation on that feature. But for the sake of completeness, we'll give a short summary of the necessary steps here.

First you need to create a manifest file, that list all the tags that should be included in the namespace. If you checkout the Parsley project from SVN you can find the manifest files for all Parsley namespaces in the manifest folder. This is an example for how such a file might look like:

<componentPackage>
    <component id="Objects" class="com.foo.ObjectsTag"/>
    <component id="Object" class="com.foo.ObjectTag"/>
    <component id="View" class="com.foo.ViewTag"/>
    <component id="DynamicObject" class="com.foo.DynamicObjectTag"/>
    <component id="NestedObject" class="com.foo.NestedObjectTag"/>
</componentPackage>

It is a simple mapping of a tag name to the fully qualified class name. As you see the tag name does not have to be the same as the class name.

To compile such a namespace into your SWC the following compiler options need to be specified for compc:

-namespace http://www.myNamespaceUrl.com ${project.dir}/manifest/myNamespace-manifest.xml
-include-namespaces=http://www.myNamespaceUrl.com

13.3.6 Creating Custom XML Namespaces

In contrast to MXML creating a namespace for custom tags is required for XML. It's the only way to expand the basic feature set of the Parsley XML tags. The XML support is built on top of the Spicelib XML-Object-Mapper. For full documentation see 20 XML to Object Mapper. But often the full feature set is not required. For basic tags that can also be used in MXML they often only utilize attributes for configuration and avoid any complex structure. Since mapping to attributes is the default behaviour of the XML-Object-Mapper, nothing has to be configured explicitly then. You'd only need to create the namespace and list the classes that should belong to that namespace:

XmlConfigurationNamespaceRegistry
    .getNamespace("http://www.myNamespaceUrl.com")
    .mappedClasses(SomeTag, SomeOtherTag, YetAnotherTag);

You can then use the new namespace alongside the builtin Parsley namespace:

<objects xmlns="http://www.spicefactory.org/parsley" 
    xmlns:myNS="http://www.myNamespaceUrl.com">
    
    <object type="com.foo.NormalObject"/>
    
    <myNS:some-tag /> 

    <myNS:yet-another-tag /> 
    
</objects>

For the mechanism and supported interfaces see 13.3.1 The Structure of Configuration Files as it is the same as for MXML configuration classes.

13.4 Working with ObjectDefinitions

In the proceeding sections you were introduced to the mechanisms of creating custom tags, either as metadata, MXML or XML. With all of these types of tags you often need to create an ObjectDefinition or modify and existing one. This can be done with the help of the configuration DSL. This section lists the most important options. For further details you may want to browse the ASDoc for the DSL.

13.4.1 Creating New Definitions

This is usually only required in MXML or XML tags as metadata tags is most often used to configure existing definitions. If you implement RootConfigurationElement or NestedConfigurationElement for a custom MXML or XML tag, then the provided ObjectDefinitionRegistry instance can be used to obtain a new builder for an ObjectDefinition:

public function process (registry: ObjectDefinitionRegistry) : void {

    var builder:ObjectDefinitionBuilder = registry.builders.forClass(MessageConfirmation);
                
    [...]
    
}

Things you can do with such a builder will be explained in the following sections. When implementing ObjectDefinitionDecorator for a custom metadata tag, a builder has already been created and will be passed to the decorator:

public function decorate (builder:ObjectDefinitionBuilder) : void {

13.4.2 Defining Property Values

You can define literal values or injections.

Literal Values

builder
    .property("firstName")
        .value("Ann");

Injection by Type

If the type can be deduced from reflecting on the property type:

builder
    .property("service")
        .value(Inject.byType());

If the type cannot be determined through reflection, it can be specified explicitly:

builder
    .property("service")
        .value(Inject.byType(LoginService));

Injection by Id

builder
    .property("service")
        .value(Inject.byId("loginService"));

This is equivalent to using [Inject(id="loginService")] on the property declaration.

13.4.3 Defining Constructor Arguments

Like for properties literal values and injections can be specified.

Literal Values

builder.constructorArgs("Alison", "Goldfrapp");

Injection by Type

If the type for all parameters can be deduced from reflecting on the parameters of the constructor yo can simply omit all parameters:

builder.constructorArgs();

Note that there is a bug in current Flash Players that reports '*' as the type for constructor arguments instead of the actual declared type if you reflect on a class before any instances of that class have been created.

If the types cannot be determined through reflection, it can be specified explicitly:

builder
    .constructorArgs(
        Inject.byType(LoginService),
        Inject.byType(CartService),
        Inject.byType(ProductService)
    );

Injection by Id

builder.constructorArgs(Inject.byId("loginService"));

Of course any mechanisms can be mixed, like an injection by type with a literal value:

builder
    .constructorArgs(
        Inject.byId("loginService")
        "http://www.someDomain.com/"
    );

13.4.4 Standard Message Receivers

If you want to apply one of the builtin message receivers like [MessageHandler] or [CommandResult] programmatically, they are all available through their corresponding API:

var builder: ObjectDefinitionBuilder = ...;

MessageHandler
    .forMethod("intercept")
        .type(LoginMessage)
        .scope("local")
            .apply(builder);

CommandResult
    .forMethod("result")
        .type(SaveUserMessage)
        .scope("window")
            .apply(builder);
            
CommandStatus
    .forProperty("status")
        .type(LoadConfigMessage)
        .selector("loadServices")       
            .apply(builder);

13.4.5 Custom Message Receivers

In cases where you created a custom message receiver by directly implementing one of the core receiver interfaces like MessageTarget or CommandObserver or extending one of the builtin receivers, you can pass a custom factory function for these receivers:

var builder: ObjectDefinitionBuilder = ...;

var factory:Function = function (): Object {
    return new MyCustomMessageReceiver(someParam, anotherParam);
};
		
builder.method("handleMessage").process(new MethodReceiverProcessor(factory, ScopeName.LOCAL));

The factory function will be invoked for each instance that gets created from the target definition.

The framework will do the rest for you. Like registering that receiver when the object gets created and removing it again when the object gets removed from the Context. It also takes care of the complex proxy handling for singleton definitions. In those cases a receiver proxy will be registered even before the target instance will be created, to avoid scenarios where an instance "misses" a message just because it was created after the sender at container startup.

13.4.6 Custom Instantiators

This is a hook that allows you to define the way how the target instance gets instantiated. In this case any constructor arguments that were already specified will be ignored since it is this instantiator who is responsible for creating the object then. This is how an instantiator can be specified:

var builder: ObjectDefinitionBuilder = ...;

builder.instantiate(new MyInstantiator());

And this is the interface that must be implemented:

public interface ObjectInstantiator {
    
    function instantiate (target:ManagedObject) : Object;
    
}

It simply has to return the new instance that should be added to the Context. The specified target parameter gives access to the ObjectDefinition and the associated Context. The property target.instance is null still at this point. Obviously, because this will become the instance that this method returns.

13.4.7 Custom ObjectProcessors

This is a hook for performing configuration logic when the object gets created and when it gets removed. This is explained in detail in section 13.2.4 The ObjectProcessor Interface.

Do not use such a processor to register and unregister message receivers. It would work in many cases, but would not be as robust as using a factory for a message receiver as demonstrated in 13.4.5 Custom Message Receivers.

13.4.8 Registering Definitions

Finally after all configuration options have been specified like shown in preceding sections, the object has to be registered. Registering a definition has the same effect as adding an <Object> tag to an MXML configuration class: the object represented by the definition becomes available for dependency injection or may be fetched from the Context directly, e.g. with Context.getObject().

There are two types of objects you can register: Singletons, which will be created on container startup and will remain managed until the Context gets destroyed. In this case only one instance will be created from the definition. The second option is a DynamicObject. This can be created and removed from the Context at any point in time. And more than just one instance can be created from such a definition. Depending on what type of definition you create there are some final options for the registration available:

Singletons

builder
    .asSingleton()
        .id("someId")
        .lazy(true)
        .order(1)
        .register();

All the options above are optional, to simply apply the defaults (non-lazy singleton with no particular order) you can reduce the above to:

builder
    .asSingleton()
        .register();

Dynamic Objects

builder
    .asDynamicObject()
        .id("someId")
        .register();

Again the id is optional and may be omitted. Lazy or order options do not apply to DynamicObjects.

13.4.9 Defining Dependent Objects

Sometimes you want to create an object that is not registered and available as a root definition, but only gets used within another definition as a kind of private dependency. Something equivalent to the following MXML configuration:

<Object type="{SomeService}">
    <ConstructorArgs>
        <NestedObject type="{SomeCollaborator}"/>
    </ConstructorArgss>
</Object>

In this case the instance of SomeCollaborator is not available for injection or for being fetched directly from the Context instance. Instead it only gets injected as a dependency into the constructor of the host object, with the lifecycle of the dependency being tied to that of the host.

If you want to do the same in code, this is how it works:

var registry: ObjectDefinitionRegistry = ...;

var dependency:DynamicObjectDefinition = registry.builders
    .forClass(SomeCollaborator)
        .asDynamicObject()
            .build();
            
var builder:ObjectDefinitionBuilder = registry.builders
    .forClass(SomeService);

builder
    .constructorArgs(Inject.fromDefinition(dependency));
            
builder
    .asSingleton()
        .register();

As you see, we do not call register for the first definition we create. We just call build instead, which gives us a definition the container does not know about. We then pass it to Inject.fromDefinition for the constructor arguments of the other definition and only register the latter with the container.

13.4.10 Reflection

When reflecting on a class where there is already an existing builder for, you can simply use the ClassInfo instance avaibale through that builder:

var p:Property = builder.typeInfo.getProperty("service");

If for some reason you have to create a ClassInfo instance yourself, remember to always pass the ApplicationDomain that the Context your tag lives in belongs to:

var registry: ObjectDefinitionRegistry = ...;
var ci:ClassInfo = ClassInfo.forClass(SomeClass, registry.domain);

Without passing the domain the tag may not work properly in modules loaded into child ApplicationDomains.

13.5 Working with Scopes

In some cases you may want to create custom configuration tags that interact with some kind of central manager in a similar way like the builtin message receiver tags interact with the MessageReceiverRegistry for example. And often it would be beneficial if there would be a way to avoid just having one global manager instance. If you want to allow the users of your custom extension tags to specify scopes the same way like for messaging, so that the feature can be applied globally or just locally for one Context only or for a any custom scope, this can be accomplished by registering a scope extension.

A core framework feature that is built on top of a ScopeExtension is the support for Decoupled Bindings with the [Publish] and [Subscribe] tags. If you want to browse the code for inspiration on building scope-wide services, you find the implementation in the parsley-extensions source folder in the package org.spicefactory.parsley.binding.

Through the use of scopes a publisher may publish an object to a local scope only, which allows for multiple windows, popups or modules to have their own local publishing space without creating ambuigities through multiple publisher declarations.

For a custom scope-wide service it is recommended to use a service interface and program the tags against that interface. You can then register the implementation for the manager like this:

BootstrapDefaults.config.scopeExtensions
    .forType(NavigationManager)
    .setImplementation(DefaultNavigationManager);

This way a new instance of your scope-wide manager will be created for each scope, the global scope, each local scope of each Context and each custom scope. Someone else would then be able to specify alernative implementations without the need to change any of the configuration tags that might talk to the service.

A tag implementation may then fetch the manager instance that belongs to the specified target scope:

public class EnterScreenDecorator implements ObjectDefinitionDecorator {

    public var scope:String = ScopeName.GLOBAL;
    
    [...]
    
    public function decorate (builder:ObjectDefinitionBuilder) : void {
    
        var scope:Scope = builder.registry.context.scopeManager.getScope(scope);
        
        var navigationManager:NavigationManager 
                = scope.extensions.byType(NavigationManager) as NavigationManager;
       
        navigationManager.doSomethingWithDefinition(definition);
    }
    
}

In the example above the default global scope could be overwritten by the user through the scope attribute. Of course your manager does not necessarily have to deal with definitions directly. You can also use the scope-wide manager in an ObjectProcessor and work with the actual target instance instead of just the definition.

13.6 Custom Configuration Mechanisms

Although Parsley is already quite flexible in how you configure objects with support for configuration with Metadata, MXML, XML or ActionScript, you may require even more freedom in how you create object definitions. You may want to process configuration loaded through Sockets or WebServices or simply programmatically create some object definitions. For this purpose you can implement the ConfigurationProcessor interface. The interface contains a single method:

public interface ConfigurationProcessor {
    
    function processConfiguration (registry:ObjectDefinitionRegistry) : void;
    
}

Your implementation of this interface may create any number of ObjectDefinition instances and add them to the registry provided by the framework. If you still want to process the metadata tags of the classes you add to the registry (in addition to your own logic of constructing definitions) you should use the mechanism provided by the framework:

var type:Class = AddToCartAction;
var id:String = "addToCartAction";

registry
    .builders
        .forClass(type)
            .asSingleton()
                .id(id)
                .register();

The configuration DSL of the framework always processes metadata on the configured classes and combines the metadata configuration with the options specified programmatically through the DSL.

Your custom processor can then be combined with any existing builder:

MXML:

<parsley:ContextBuilder>
    <parsley:FlexConfig type="{BookStoreConfig}"/>
    <parsley:XmlConfig file="logging.xml"/>
    <parsley:CustomConfig>
        <mynamespace:MyCustomConfigurationProcessor/>
    </parsley:CustomConfig>
</parsley:ContextBuilder>

ActionScript DSL:

var viewRoot:DisplayObject = ...;

ContextBuilder.newSetup()
    .viewRoot(viewRoot)
    .newBuilder()
        .config(FlexConfig.forClass(BookStoreConfig))
        .config(XmlConfig.forFile("logging.xml"))
        .config(new MyCustomConfigurationProcessor())
        .build();

After the code above executed the objects defined with MXML and XML happily coexist with objects added through your custom builder in the same Context.

Asynchronous Processors

If your processor operates asynchronously you have to implement the AsyncConfigurationProcessor interface instead (which extends the ConfigurationProcessor interface). This might be necessary if you obtain the configuration with some remote service call for example. The interface looks like this:

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

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

public interface AsyncConfigurationProcessor 
                                      extends ConfigurationProcessor, IEventDispatcher {
    
    function cancel () : void;
    
}

First you must be prepared that your asynchronous processor may get cancelled (for example if the application destroys the associated Context before it was fully configured). Finally you have to throw either Event.COMPLETE or ErrorEvent.ERROR, depending on whether configuration succeeded or failed.

13.7 Replacing IOC Kernel Services

(Note: Prior to version 2.4 kernel services required a dedicated factory. This is no longer needed, you can now simply specify the implementation class like shown in the following examples.)

All central services of the IOC kernel live behind interfaces which can be replaced by custom implementations. The replacement can be done globally or just for a single Context, in case you build a modular application with a hierarchy of multiple Contexts.

In the following example the central ViewManager will be replaced globally by specifying a custom implementation:

BootstrapDefaults.config.services.viewManager.setImplementation(MyCustomViewManager);

A new instance of your custom ViewManager will then be created for each new Context. If you want to replace the ViewManager for a single Context (and its children) only, you can specify the factory with the ContextBuilder DSL:

var viewRoot:DisplayObject = ...;

ContextBuilder.newSetup()
    .viewRoot(viewRoot)
    .services().viewManager().setImplementation(MyCustomViewManager)
    .newBuilder()
        .config(FlexConfig.forClass(BookStoreConfig))
        .build();

For reusable replacements to be used in Flex applications it is recommended to create a simple custom tag like shown in 13.8 Initializing Extension Modules that can then be used as a child tag in a ContextBuilder tag.

Decorating IOC Kernel Services

For most scenarios it is recommended not to create a replacement from scratch but instead decorate an existing service. There are two reasons why you should prefer a decorator. First, in most cases you want to add or modify a certain aspect of a core service and not implement it with a totally different behavior. Second, there might be 3rd party frameworks or extensions that in turn want to replace or decorate a core service, which would fail if those were full replacements, as one replacement would overwrite the other. With a decorated service there is no limitation on the number of decorators you wrap around the original service.

This is how you would write a very simple decorator that simply logs whenever a view root is added or removed from the Context. (This is a contrived example as Parsley does that anyway, it's just to demonstrate the principle.)

package {

// [imports...]

public class LoggingViewManager implements ViewManager {
    
    private static const log:ILogger = Log.getLogger("com.foo.LoggingViewManager");
    
    private var delegate:ViewManager;
    
    function LoggingViewManager (delegate:ViewManager) {
        this.delegate = delegate;
    }

    public function addViewRoot (view:DisplayObject) : void {
        log.info("Adding view root " + view.name);
        delegate.addViewRoot(view);
    }

    public function removeViewRoot (view:DisplayObject) : void {
        log.info("Removing view root " + view.name);
        delegate.removeViewRoot(view);
    }
    
}
}

As you can see all you need to do is implement the kernel interface (ViewManager in this example) like when replacing kernel services and then adding a constructor that accepts the real ViewManager as a delegate. You can then apply this decorator globally like this:

BootstrapDefaults.config.services.viewManager.addDecorator(LoggingViewManager);

Knowing your Environment

In many cases you may need access to the configuration settings or collaborating services or just the Context instance itself. This can be accomplished for service replacements and decorators through implementing the InitializingService interface:

class SomeService implements ViewManager, InitializingService {

    public function init (info:BootstrapInfo) : void {
    }

    public function addViewRoot (view:DisplayObject) : void {
    }
	
    public function removeViewRoot (view:DisplayObject) : void {
    }
	
}

The BootstrapInfo class gives you access to all the other kernel services and the configuration settings for the Context your service instance belongs to. The init method will be invoked once after your service has been instantiated.

The next section gives you an overview over all services that can be replaced or decorated.

List of IOC Kernel Services

BootstrapManager Responsible for processing the configuration and then building and initializing the Context. May be fed with different types of ConfigurationProcessors, like the builtin ones for ActionScript, MXML or XML configuration.
Context This is the core interface of the framework, putting all the other pieces together and delegating most of the work to the other parts of the kernel listed below. It allows you to pull objects out of the container or examine its contents.
ObjectDefinitionRegistry The registry for all ObjectDefinitions the Context will manage.
ObjectLifecycleManager Responsible for processing ObjectDefinitions, instantiating, configuring and disposing the actual instances described by those definitions.
ScopeManager Responsible for managing all scopes that are associated with a single Context.
MessageRouter The core interface of the Messaging Framework.
ViewManager Responsible for dynamically wiring views to the Context.

All the services listed above can be replaced through the BootstrapDefaults.

13.8 Initializing Extension Modules

When you create a set of configuration tags and possibly some scope-wide managers that integrate with them, you may want to offer your users a convenient way to activate that extension:

<parsley:ContextBuilder config="{MyConfig}">
    <app:NavigationSupport/>
</parsley:ContextBuilder>

Here a custom MXML tag as a child of the <ContextBuilder> tag takes the role of doing all the necessary setup to make the extension available for that Context.

Child tags of <ContextBuilder have to implement the ContextBuilderProcessor interface:

public class NavigationSupportTag implements BootstrapConfigProcessor {

    private static var initialized:Boolean = false;

    public static function initialize () : void {
        if (initialized) return;
        
        Metadata.registerMetadataClass(EnterScreenDecorator);
        Metadata.registerMetadataClass(ExitScreenDecorator);

        BootstrapDefaults.config.scopeExtensions
            .forType(NavigationManager)
            .setImplementation(DefaultNavigationManager);
    
        [...]
        
        initialized = true;
    }

    public function processConfig (config:BootstrapConfig) : void {
        initialize();
    }
    
}

The processBuilder method of the implemented interface will simply delegate to a public static method. This has the advantage that you can use the same class programmatically, in cases where you don't use the MXML tags to declare the Context:

NavigationSupport.initialize();

Like shown in our example, most common tasks in such an initializer are registering custom metadata tags or scope-wide managers.

If you also offer your tags in an XML variant built on top of the custom configuration namespace support in Parsley you may want to prefer to offer them with a separate initializer class. This way you would make sure that users who do not use XML configuration do not end up with the entire Spicelib XML-Object-Mapper framework compiled into their SWF.

<parsley:ContextBuilder config="{MyConfig}">
    <app:NavigationSupport/>
    <app:NavigationXmlSupport/>
</parsley:ContextBuilder>

In the example above XML support is activated explicitly in addition to the core extension module. The XML initializer is responsible for configuring the custom XML configuration namespace. For details see 13.3.6 Creating Custom XML Namespaces.