Chapter 4. Application Deployment

A Seedling application (that is, an application built using the Seedling platform) is made up of one or more modules. Each module contributes new components to the tree, and customizes the configuration of existing components. This chapter describes how a Seedling application is organized and launched.

Modules can be broadly classified into two main roles. Supporting modules provide specialized frameworks or services, but are not useful in isolation. For example, the runtime module provides the core facilities of the Seedling platform, the jelly module provides support for XML-based node configuration and scripting, and the bonsai module provides a generic GUI framework. Primary modules provide an application's custom functionality, typically by pulling together supporting modules and configuring them into a useful system. In general, an application is launched by indicating just its primary module; any supporting modules are automatically loaded by the launcher.

[Note]Note
There is no concrete difference between primary and supporting modules; this is just terminology we use to talk about two common ways in which modules can be used.

4.1. Module Structure

A Seedling module has two aspects: a configuration layer that specifies the module's runtime components, and (optionally) a set of Jar files providing classes and resources. Since no module can stand alone (except the core runtime module), each one can declare its predecessors: required modules that provide necessary frameworks and/or services. The Seedling runtime launcher will automatically load all necessary modules, determine the appropriate class path, and construct the proper sequence of configuration layers.

A module is deployed in the form of a single directory tree; each module deployed within an application must therefore have a unique name. A module's directory has the following structure:

  • MODULE/

    • classes/ (optional) contains a tree of Java .class files to be added to the runtime classpath.

    • config/ (optional) contains the module's config tree when Directory Config Format is used.

    • config.zip (optional) contains the module's config tree when ZipFile Config Format is used.

    • lib/ (optional) contains additional libraries to be added to the runtime classpath.

    • module.properties (optional) defines various properties of the module.

The module.properties file is a standard Java properties file that is used to configure the module as a whole. There is currently only one property supported:

requires

If defined, this property's value must be a comma-separated list of module names. These are the predecessors of this module: each one must be available within the current deployment, and all are loaded into the Seedling before the current module. Note that nodes configured in this module will therefore override any configuration provided by predecessor modules.

To start the application, the Seedling launcher analyzes each module and creates a class path with all of the necessary resources. A module may contribute several items to the class path according to these rules:

  • If the module defines any predecessor modules, they are processed first, so that their resources precede this module in the class path. This rule avoids class hiding for security purposes: a module cannot replace classes or resources provided by a predecessor.

  • If the module has a classes directory, it is appended to the class path.

  • If the module has a lib directory, any jar or zip files within it are appended to the class path in an undefined order.

The module's configuration tree can be packaged in several ways. The following list describes the different formats; the first format found will be used.

  • In ZipFile Config Format, the config tree is stored in a config.zip file in the module's directory. This is a standard ZIP file, with resources arranged in subdirectories according to the branch/node hierarchy.

  • In Directory Config Format, the config tree is found in the module's config directory as normal files. You can easily convert this format to the ZipFile format by simply zipping up the contents of this directory (but not the config directory itself).

The Seedling build system provides an Ant target buildModule (defined in moduleTargets.xml) that builds the appropriate structures in the module's main Jar file.

4.2. Application Structure

The basic deployment approach is to construct a single directory that contains both the Seedling platform modules and all application modules. The typical file layout is as follows:

  • APPLICATION-DIR/ is also known as the SEEDLING_HOME directory.

    • launcher.jar is the Seedling bootstrap jar.

    • logs/ is the default location for any log files created by the application.

    • modules/ contains all of the modules deployed as part of the application.

      • MODULE/ contains a single deployed module. The contents of this directory are described in the Module Structure section.

Since an application is usually launched by naming one or more primary modules, it is acceptable for the deployed system to contain additional modules that may not be loaded (because they are not required by the primary modules). This feature can be used to deploy several applications within one directory tree by providing multiple startup scripts that launch Seedling with different primary modules.

The modules directory is known as a repository, and Seedling can load modules from more than one of them. The best use for this feature is to avoid modifying the standard Seedling home directory, keeping application code completely separate from the platform, in observance of the Open/Closed Principle. The various launching approaches described in the next section each have a means for utilizing multiple repositories.

4.3. Launching

Seedling provides a spectrum of options for launching the container, depending on how much control the application is willing to give Seedling with regards to main() and class loading.

4.3.1. Seedling on the Outside

At one end of the launching spectrum is Seedling on the outside, where Seedling takes resposibility for main() and for all module and class loading. This is easy to achieve: just run the launcher.jar and pass SEEDLING_HOME and the names of the required modules.

A typical application can thus be started simply by naming the primary module(s):

$ cd APPLICATION-DIR
$ java -jar launcher.jar MODULE ...

The launcher inspects the contents of the named modules and recursively loads all of the required modules, each of which must reside in one of the known repository directories. The launcher constructs a custom ClassLoader to gain access to the libraries provided by each module (in the lib directory).

The SEEDLING_HOME/bin/seedling script is a generic example of this approach and is very useful during development.

For more information on launching options, see the documentation for the consciouscode.seedling.launcher.Launcher class.

4.3.2. Seedling in the Middle

To put Seedling in the middle, the application provides main() and handles class loading for (at least) the Seedling runtime module, letting Seedling perform dynamic loading of other modules into the container. There are several viable ways to achieve this:

  • Add all of the bootstrap jars to the JVM command line. The required jars are SEEDLING_HOME/modules/runtime/lib/*.jar

  • Deploy the application as a jar that has the bootstrap declared in the manifest's Class-Path attirbute. Seedling's launcher.jar is an example of this approach.

  • Construct a dynamic ClassLoader that provides the bootstrap jars, then calls a trampoline method via reflection. The trampoline isolates the outer launch code from having a static dependency on Seedling classes.

The go-to class for putting Seedling in the middle is consciouscode.seedling.boot.SeedlingBuilder which allows the application to provide SEEDLING_HOME, additional module repositories, and the required modules.

4.3.3. Seedling on the Inside

At the other end of the launching spectrum is Seedling on the inside, where the application embeds Seedling as a library and handles all class loading itself. This option is not recommended, since it requires the application to determine the transitive closure of required modules and build the appropriate class path.

There is currently minimal platform support for this approach. Again, the important API is the consciouscode.seedling.boot.SeedlingBuilder class, particularly the setDelegatingClassLoading method.