Chapter 5. A Node's Lifecycle

One of the expected features of a component container is the ability to manage the lifecycle of managed components. By lifecycle we mean the stages through which an object progresses; in plain-old Java code this entails its construction, its preparation and use, and its eventual release for garbage collection.

Seedling defines a more abstract lifecycle model with many extension points that allow you to write components that exhibit desired behavior at each stage of their lives.

The Seedling lifecycle model consists of three broad stages:

5.1. Provisioning

There's only one way to construct an object in Java: the new operator. However, newly-constructed objects are not always immediately ready for use. Seedling defines provisioning as the broader process through which an object is prepared for installation into the component hierarchy. A node is provisioned indirectly and lazily when it's requested via BranchNode.getChild() or similar access methods: if the requested node isn't already installed in the tree, Seedling will attempt to provision and install it.

There are two provisioning mechanisms: aliasing and creation.

Aliasing is a near-trivial provisioning approach that simply identifies another installed node and inserts a reference to it at a separate location. This serves similar purposes to symbolic links in a file system: it allows the same object to appear in the hierarchy in several different contexts. Since aliasing is so simple, there's not much to say about it here; see the chapter on configuration to learn how to configure an alias.

Creation is when a new Java object is instantiated and configured. This process involves a few more steps and is used in the vast majority of cases. In fact, the ability to flexibly create nodes is one of the main reasons to use Seedling!

Creation of a node consists of a few distinct steps: construction, injection, and decoration.

Construction is the initial acquisition of the object. The standard configuration language implements this step via the .this meta-property. Most nodes are constructed by instantiation (keyword new), but Seedling can also use a factory method to construct objects indirectly.

Injection is the further modification of the object by calling property setter-methods, as declared by the node configuration.

Decoration takes the injected object and performs one last arbitrary transformation, perhaps resulting in a different object than we first constructed! This step can be used to implement the decorator pattern, to wrap the injected object with a dynamic proxy, or to do to other manipulations of the node before installation.

5.2. Installation

Installation of a node into the tree is a separate lifecyle phase from provisioning. Seedling allows you to install nodes directly via BranchNode.installChild(), bypassing provisioning entirely. Still, in most cases nodes are provisioned and installed automatically by the framework.

Regardless of the source of the node, installation consists of two steps: activation and validation.

Activation signals the node that it's being installed into the component hierarchy, so it should start doing whatever it's supposed to do. The standard branches activate nodes using two callbacks.

  • If the node implements LocatableNode, its nodeInstalled method is called with a NodeLocation indicating where the node is being installed. This gives the node a reference to its parent BranchNode, from which it can access other nodes in the container.

  • If the node implements ServiceNode, its startService method is called.

Activation is not performed on aliases, since the target of the alias must already be active.

After activation, the framework performs final validation of the node. This ensures that the node meets all declared constraints before it is exposed to the rest of the application. Typical constraints declare interfaces that must be implemented, but validation is a general process and Seedling will provide further hooks to check that a new node is worthy of installation.

If activation and validation succeed, the node is ready for business and the framework inserts it at the appropriate location in the tree so that other parts of the application can request it.

5.3. Uninstallation

Sooner or later every node will need to be destroyed. This can happen manually via BranchNode.uninstallChild(), but usually it happens transitively by deactivating a branch (or the entire Seedling tree).

Compared to provisioning and installation, uninstallation is straightforward: the node is removed from the tree and then deactivated.

Deactivation signals the node that it's being removed from the component hierarchy, so it should stop doing its thing. The standard branches deactivate nodes much like it activates them.

  • If the node implements ServiceNode, its stopService method is called.

And that's it, the node is out of the tree and can be released by the garbage collector (assuming other parts of the system haven't kept references to it).