Mach II 1.5 Feature: New Property Datatypes - Mach-II/Mach-II-Framework GitHub Wiki

Table of Contents

  1. The Problem
  2. Exploring the Solutions
  3. The Solution in Mach-II: Structs and Arrays as Properties + New MachII.framework.Property CFC
  4. Load Order Dependency
  5. In Closing

The Problem

Properties in Mach-II applications are a convenient way to make variables available throughout an entire Mach-II application. Properties are declared in mach-ii.xml and after the application initializes, properties may be accessed via a simple getProperty() method call from anywhere within the application, including within all Mach-II object types (Listeners, Filters, Plugins) as well as in views. In addition to declaring properties in mach-ii.xml, they may also be added to Mach-II's property manager programmatically.

The current limitation with properties, however, is that property declarations within mach-ii.xml are limited to simple name/value pairs. Complex data types and custom CFCs may not be declared as properties in mach-ii.xml. Variables that go beyond simple name/value pairs may be added programmatically, but there is no mechanism built into Mach-II for handling this when the application is initialized. The most common solution for adding complex data types or CFCs as Mach-II properties upon application initialization is to use a plugin, and within the configure() method of the plugin, the objects and/or variables needed are instantiated and the objects and/or variables are then manually added to the property manager.

Although this approach works, there are a few issues that have led to requests for enabling the ability to more easily use CFCs and complex data types as properties:

  1. Using a plugin solely to have access to the configure() method so that it is called when the application initializes is a bit of a "hack," as it leverages only the configure() method of the plugin and not any of the plugin points. This clearly isn't a job that a plugin was designed to handle, because the plugin is being used not as a plugin, but rather as a property loader.
  2. Using a plugin to put properties in the property manager on application initialization adds unnecessary complexity to the process.
  3. As a result of the name/value pair limitation in mach-ii.xml, properties are treated completely differently based on their data type, and therefore all the application properties cannot be declared in the XML configuration file. This means having to look in multiple places to keep track of properties, and the difference in treatment based on data type is unnecessarily inconsistent.

Exploring the Solutions

Several solutions to this issue have been discussed and briefly explored.

  1. Allow for the declaration of structs and arrays as properties in mach-ii.xml, but limit additional property types to only structs and arrays (i.e. any other data type would still have to be added to the property manager programmatically using the plugin method described above). This would provide a minor amount of additional functionality and these data types could be fully declared (struct keys and array elements included, to an extent) within the XML configuration file. This is, however, of rather limited use. First, using something like a struct but only allowing simple data types to be put in the struct is only an incremental benefit over using separate name/value pairs as properties. Second, where arrays are concerned, the immediate question is what data types can be used as elements of the array. If the data types allowed in either case are constrained, then this approach is of extremely limited benefit.
  2. Allow for the use of simple "bean" type objects as properties. In this way, Mach-II would know what to expect and could dictate exactly how the bean must be structured in order for it to be used as a property. This is of some additional benefit when compared to number 1 above, because it would at least offer some encapsulation and data hiding, but ultimately the benefit here is also relatively limited because of the constraints that would be placed on what developers can and can't do. In other words, if someone wanted to use a CFC that didn't conform to the bean-type pattern, they would still have to use the plugin method described above to put a CFC in the property manager, meaning this approach (and number 1 for that matter) is only a partial solution. Also when compared to using something like a struct, there is only a slight benefit of having getters/setters for all the properties. Finally, there is the issue again of what data types can be used as properties within the CFC itself.
  3. Introduce a new MachII.framework.Property CFC (the name of this object is up for debate) to the core. This object would be extended by the developer in similar fashion to the other Mach-II core objects (Listener, Filter, Plugin) and would contain a configure() method that Mach-II would automatically call when the application initializes. This would allow developers to use custom CFCs as properties in Mach-II applications, but would allow them to be declared in mach-ii.xml right along with the simple name/value properties. Because the behavior is consistent with other Mach-II object types, this also makes the new Property CFC immediately familiar to Mach-II developers, reduces complexity and increases accessibility to property declarations (no need to hunt around because they're all in the XML configuration file), and allows developers total freedom to use custom CFCs as application properties.

Through the discussions on the Mach-II mailing list and among the developers of Mach-II, number 3 above seems to make the most logical sense as a solution. It's consistent with the way other object types within Mach-II work, it eliminates the need to leverage a plugin to handle this, and it allows all properties for the application to be declared within mach-ii.xml. When number 3 is combined with number 1, it creates a complete solution that allows for the use of simple name/value pairs, structs and arrays, and custom CFCs to all be easily used as properties, and all be declared in the <properties> section of mach-ii.xml.

In addition to what is outlined above, another potential downside for allowing the use of CFCs as properties is the possibility that developers will expect Mach-II to manage object dependencies within properties in similar fashion to something like ColdSpring. While this is a concern, this same concern also applies to the existing native Mach-II object types (Listeners, Plugins, Filters), so in that sense the new Property.cfc would not be adding any issues that don't already exist in Mach-II. To be clear, Mach-II will not manage dependencies for developers. That is the responsibility of developers, and they may either choose to manage dependencies themselves, or they may use ColdSpring to manage dependencies. (As an aside, the addition of this new object type to Mach-II will require an update to the Mach-II plugin for ColdSpring so that ColdSpring can manage dependencies in the new object type in addition to the Mach-II object types it already handles.)

The Solution in Mach-II: Structs and Arrays as Properties + New MachII.framework.Property CFC

Structs and Arrays as Properties

As described above the addition of structs and arrays to the allowable data types for properties is of limited benefit, but it is also relatively easy to implement within the framework, and when combined with the new proposed core framework object type (Property.cfc), will allow developers maximum flexibility with application properties.

Structs would be declared in mach-ii.xml as follows, and keys could be declared using any of the methods outlined below:

    <properties>
        <property name="myStruct">
            <struct>
                <key name="key1" value="valueA"/>
                <key name="key2">
                    <value>valueB</value>
                </key>
            </struct>
        </property>
    </properties>

Arrays would be declared in mach-ii.xml as follows, and array elements could be declared using any of the methods outlined below:

    <properties>
        <property name="myArray">
            <array>
                <element value="valueA"/>
                <element>
                    <value>valueB</value>
                </element>
            </array>
        </property>
    </properties>

Structs and arrays could also be mixed and nested.

Array as struct element:

    <properties>
        <property name="myStruct">
            <struct>
                <key name="key1">
                    <array>
                        <element value="valueA"/>
                        <element value="valueB"/>
                    </array>
                </key>
            </struct>
        </property>
    </properties>

Struct as array element:

    <properties>
        <property name="myArray">
            <array>
                <element>
                    <struct>
                        <key name="key1" value="valueA" />
                    </struct>
                </element>
                <element value="ABC123"/>
            </array>
        </property>
    </properties>

CFCs cannot be used as elements in arrays and structs. Due to the complexity, we'll be limiting them to simple data types (or nested structs/arrays) if you choose to implement a property this way.

MachII.framework.Property CFC

As described above, the new Property.cfc framework object is a first-class framework object along with Listeners, Plugins, and Filters, and would follow the same pattern as these existing framework objects. To elaborate, the developer would extend the Property.cfc, implement a configure() method as needed, and this configure() method would be called by Mach-II when the application initializes. The developer is then free to implement any other code necessary in the object itself. Additionally, upon application initialization and after calling the configure() method, Mach-II would put the object in the property manager and give it the name declared in mach-ii.xml.

A sample Property.cfc called MyAppProperty.cfc would be implemented as follows:

    <cfcomponent
        displayname="MyAppProperty"
        output="false"
        extends="MachII.framework.Property"
        hint="My app property CFC for my app">

        <cffunction name="configure" access="public" output="false" returntype="void"
            hint="Called automatically by Mach-II on application initialization">
            <!--- do necessary startup stuff here such as getting parameters --->
        </cffunction>

        <cffunction name="myCustomMethod" access="public" output="false" returntype="string"
            hint="Some custom method">
            <cfreturn "Hello world." />
        </cffunction>

    </cfcomponent>

This object would be declared in mach-ii.xml as follows. Notice that there is a type attribute which is only used when defining CFCs as properties:

    <properties>
        <property name="myAppProperty" type="path.to.MyAppProperty" />
    </properties>

Property.cfc supports parameters in similar fashion to plugins, filters and listeners which of course would be available to the MyAppProperty.cfc as parameters:

    <properties>
        <property name="myAppProperty" type="path.to.MyAppProperty">
            <parameters>
                <parameter name="parameter1" value="value1" />
                <parameter name="parameter2" value="value2" />
            </parameters>
        </property>
    </properties>

All configuration logic should take place in a configure() method. Do not override the init() method that is inherited from the MachII.framework.Property (which inherits from the MachII.framework.BaseComponent).**

Property Type Shortcuts

As of Mach-II 1.9, you can use a shortcut for the property type when referencing Mach-II property CFCs that are shipped with the core framework.

Shortcut Name Value Substituted at Runtime
ColdspringProperty MachII.properties.ColdspringProperty
EnvironmentProperty MachII.properties.EnvironmentProperty
HtmlHelperLoaderProperty MachII.properties.HtmlHelperLoaderProperty
HtmlHelperProperty MachII.properties.HtmlHelperProperty
UrlRoutesProperty MachII.properties.UrlRoutesProperty
CachingProperty MachII.caching.CachingProperty
LoggingProperty MachII.logging.LoggingProperty
GlobalizationLoaderProperty MachII.globalization.GlobalizationLoaderProperty

Anonymous Property Names

As of Mach-II 1.9, you can omit the property name for property CFCs. This allow for anonymous property CFCs so you do not have to arbitrarily name property CFCs that are used for configuration.

    <properties>
        <property type="path.to.MyAppProperty">
            <parameters>
                <parameter name="parameter1" value="value1" />
                <parameter name="parameter2" value="value2" />
            </parameters>
        </property>
    </properties>

Mach-II will automatically name the property using the last list element from the type attribute plus a number. In the example above, the property name would be MyAppProperty_1. If another property of the same type is defined without a property name, the number counter is increase.

N.B. If you need to reference a property by name, do not omit the property name and rely on the order in which the properties are loaded. This feature is mainly useful for multiple properties like UrlRoutes which provide configuration information to Mach-II but are not directly referenced in your application.

Load Order Dependency

Some properties may be dependent on others, because of this be aware of the order in which they are specified in the configuration file. As of Mach-ii 1.9, the framework enforces a load order for the EnvironmentProperty and ColdspringProperty components. EnvironmentProperty will be loaded first, followed by ColdspringProperty regardless of the order they are specified in the configuration file (but only of course if they are defined in your configuration.)

In Closing

The addition of these new property data types does not change the backwards compatibility of the framework nor does it force developers to use this new feature. However, the new property data types will allow developers to more easily deal with properties within Mach-II applications that are not simple name/value pairs. The syntax outlined above for declaring structs and arrays within mach-ii.xml allows for the easy use of these data types as properties.

The implementation of Property.cfc is consistent with existing Mach-II framework objects (Listeners, Plugins, and Filters) and eliminates the need to use a plugin's configure() method to put complex data types in the property manager when the application initializes. The addition of these new supported property data types also has the added benefit of creating a more consistent handling of properties, because the data type of the property will not dictate how it's handled within the application. Furthermore, developers will be able to declare all of the properties within the mach-ii.xml file, making maintenance of applications a bit less complex.

⚠️ **GitHub.com Fallback** ⚠️