Using Subsystems

Sean Corfield edited this page Aug 30, 2014 · 4 revisions

This documentation covers FW/1 2.5 (and earlier, to some extent). See the FW/1 3.0 Documentation for the latest Getting Started Guide (for release 3.0).

Using Subsystems

Subsystems give you a way of dropping in one FW/1 application into an existing one. Subsystems can be used to create a module that has no dependencies on the parent application or you can use subsystems to group common functionality together.

Enabling Subsystems

If you want to enable subsystems, you must set the following in the parent application’s Application.cfc:

variables.framework.usingSubsystems = true;

Once you enable subsystems, you’ll need to follow a couple of additional conventions:

  1. Your site’s default application must be implemented as a subsystem. By default, the framework will look in the sub-directory home.
  2. A sitewide layout can be specified at common/layouts/default.cfm. If this file exists, it will be applied.

The locations for your default subsystem and sitewide layout can be overridden in Application.cfc with:

variables.framework.defaultSubsystem = 'home';
variables.framework.siteWideLayoutSubsystem = 'common';

Accessing Subsystems

To access a subsystem in the browser, you’ll have to specify it in the action:

index.cfm?action=subsystem:section.item

If you leave off the subsystem in the url, the section and item will reference the default subsystem.

When creating links in your views and layouts, it’s recommended that you use buildUrl(). You do not have to specify the current subsystem inside buildUrl(), it will automatically be prepended. This method is preferred. If you change the name of a subsystem, all of your links inside the subsystem will represent the change.

buildUrl('section.item') - action=currentSubsystem:section.item

You can also link to other subsystems:

buildUrl('otherSubsystem:section.item') - action=otherSubsystem:section.item

Configuring Subsystems

There is an optional method that can be declared in Application.cfc for configuring subsystems:

function setupSubsystem(subsystem) {}

setupSubsystem() is called once for each subsystem, when a subsystem is initialized. When an application is reloaded, the initialized subsystems are cleared and setupSubsystem() will be called on the next request to a subsystem.

Framework Configuration

The following options relate to subsystems:

  • usingSubsystems – This will be true if you are using subsystems.
  • defaultSubsystem – This is the default subsystem when none is specified in the URL or form post.
  • subsystemDelimiter – This specifies the delimiter between the subsystem name and the action in a URL or form post.
  • siteWideLayoutSubsystem – This specifies the subsystem that is used for the (optional) site-wide default layout.

Controllers

Subsystem controllers are located in subsystem/controllers (e.g., admin/controllers).

Services

Subsystem services are located in subsystem/services (e.g., admin/services).

Views

Subsystem views are located in subsystem/views (e.g., admin/views).

Layouts

Subsystem layouts are looked up in the same order as before, but with the additional inclusion of a sitewide layout, if it exists. The default sitewide layout folder is common. Subsystem specific layouts are located in subsystem/layouts (e.g., admin/layouts).

  • subsystem/layouts/section/item.cfm
  • subsystem/layouts/section.cfm
  • subsystem/layouts/default.cfm
  • common/layouts/default.cfm

Using Bean Factories

The introduction of subsystems introduces the ability to have subsystem specific bean factories.

function setupApplication() {
    var bf = createObject('component','coldspring.beans.DefaultXmlBeanFactory').init();
    var homeBf = createObject('component','coldspring.beans.DefaultXmlBeanFactory').init();
    var adminBf = createObject('component','coldspring.beans.DefaultXmlBeanFactory').init();
    // load the main bean factory:
    bf.loadBeans('config/coldspring.xml.cfm');
    setBeanFactory(bf);
    // load a bean factory for the home subsystem:
    homeBf.loadBeans('home/config/coldspring.xml.cfm');
    homeBf.setParent(getDefaultBeanFactory());
    setSubsystemBeanFactory('home', homeBf);    
    // load a bean factory for the admin subsystem:
    adminBf.loadBeans('admin/config/coldspring.xml.cfm');
    adminBf.setParent(getDefaultBeanFactory());
    setSubsystemBeanFactory('admin', adminBf);
}

The following methods are now at your disposal:

  • setSubsystemBeanFactory(subsystem, beanFactory) – sets up a subsystem specific bean factory
  • hasSubsystemBeanFactory(subsystem) – returns true if a subsystem specific bean factory exists
  • getBeanFactory() – returns the bean factory for the current subsystem. Alternately, it can be used to retrieve the bean factory for another subsystem by passing the name of the subsystem (e.g., getBeanFactory(subsystem) ).
  • getDefaultBeanFactory() – returns the default bean factory that was passed to setBeanFactory()

Services

If you specified a subsystem specific bean factory and it knows about a bean named sectionService, it will attempt to use that as the service. If a bean named sectionService doesn’t exist, the subsystem will attempt to use subsystem/services/section.cfc, if it exists.

Controllers

If you specified a subsystem specific bean factory and it knows about a bean named sectionController, it will attempt to use that as the controller. If a bean named sectionController doesn’t exist, the subsysem will attempt to use subsystem/controllers/section.cfc, if it exists. Remember, if you use a bean factory to manage your controllers, the framework will not be passed to the init() method. It’s recommended that you use subsystem/controllers/section.cfc as the convention for declaring controllers.

Auto Wiring

If you did not declare a subsystem specific bean factory, the framework will attempt to auto wire beans from the default bean factory into subsystem specific controllers and services.

If you have declared a subsystem specific bean factory, the framework will attempt to auto wire only the beans in the subsystem specific bean factory into your subsystem controllers and services. If a subsystem specific bean factory exists, beans from the default bean factory will NOT be autowired into your subsystem controllers, even if the bean doesn’t exist in the subsystem specific bean factory.

If you are using a subsystem specific bean factory and wish to auto wire beans from the default bean factory, consider using an IoC framework that supports setParent().

Setting Bean Factories With setupSubsystem()

With setupSubsystem(), it’s possible that you can use your own convention to load subsystem specific bean factories, instead of explicitly declaring each one in setupApplication(). The following example makes the assumption that each subsystem has a bean factory config file in a common subsystem specific folder. If the config file is found, it then loads the subsystem bean factory.

function setupSubsystem(subsystem) {
    var bf = createObject('component','coldspring.beans.DefaultXmlBeanFactory').init();
    var bfConfigFilePath = subsystem & '/config/coldspring.xml.cfm';
    // conditionally load the bean factory for this subsystem by convention:
    if ( fileExists(expandPath('./') & bfConfigFilePath) ) {
        bf.loadBeans( bfConfigFilePath );
        setSubsystemBeanFactory( subsystem, bf );           
    }         
}

Accessing Other Bean Factories From A Subsystem

If you have a default bean factory, you can access it in your controllers and views from any subsystem with getDefaultBeanFactory().

While it’s not considered a best practice, there may be a chance when you will need to access a bean factory from another subsystem. You can do this by calling getBeanFactory(subsystem) (i.e. getBeanFactory(‘user’)).