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 | |
---|---|
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. |
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:
|
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.
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:
is also known as the APPLICATION-DIR
/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.
contains a single deployed module. The contents of this
directory are described in the Module Structure
section.MODULE
/
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.
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.
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
script is a generic example of this approach and is very useful during
development.
SEEDLING_HOME
/bin/seedling
For more information on launching options, see the documentation
for the consciouscode.seedling.launcher.Launcher
class.
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.
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.