Module Logical Structure
As described in the
module overview
a module has an address space export declaration
and an internal private address space which contains all resources and code.
The logical structure of a module is defined in the module definition file:
module.xml
which resides at the root of each module.
It includes the major sections:
-
Metadata
: Information about the module.
-
Export
: The exported address space.
-
Rewrite
: The mapping between the exported and private address spaces.
-
Mapping
: The definition of the private internal address space.
as shown in the skeletal
module.xml
file:
<module>
<!---->
<identity />
<info />
<publisher />
<license />
<!---->
<export>
<uri />
<class />
</export>
<!---->
<rewrite>
<rule />
</rewrite>
<!---->
<mapping>
<ura />
<import />
<rewrite />
<this />
<super />
</mapping>
</module>
Module.xml Sections
Metadata
In
module.xml
there are several elements that contain
metadata about the module.
These elements include
identity
,
info
,
publisher
and
license
.
Path |
Description |
/identity |
The identity section contains two child elements that, when taken
together, uniquely identify an instance of the module.
You can think of the combination of uri and version as the
"primary key" for a module instance.
|
/identity/uri |
A unique uri identifying this module.
By convention URNs are used.
The name should be universally unique using a base name that is
controlled by the author or owner of the module.
|
/identity/version |
A version number containing integers separated by "." marks.
For example 1.0.12 is a valid version number.
The version numbers may be extended to any degree of precision.
|
For example:
<identity>
<uri>urn:com:myco:accounting</uri>
<version>1.0.12</version>
</identity>
Identifies a module by the name
com:myco:accounting
at
version number
1.0.12
.
Path |
Description |
/info |
The info section contains human
readable information about the module that is used
in various reports.
|
/info/name |
A human readable name for the module.
There is no requirement for this to be unique, but it should
be informative.
|
/info/description |
A human readable description of the module |
/info/type |
Categorization of the module used with dynamic deployment.
The value can be one of
application ,
fulcrum , or
library .
An application is a module that may be deployed in a fulcrum.
A fulcrum module hosts one or more transports.
A library module is imported by other modules.
|
For example:
<info>
<name>Accounting Program</name>
<description>Our company's accounting program</description>
<type>application</type>
</info>
Provides descriptive information about the module.
Path |
Description |
/publisher/name |
optional
publishers name
|
/publisher/uri |
optional
URI reference to information about the publisher
|
For example:
<publisher>
<name>My Company</name>
<uri>http://www.mycom.com</uri>
</publisher>
Path |
Description |
/license/name |
optional
license name
|
/license/uri |
optional
URI reference to license information
|
For example:
<license>
<name>My Company Commercial License</name>
<uri>http://www.mycom.com/license/</uri>
</license>
To see how NetKernel displays this module information, look
at the
Module Report
for this installation of NetKernel.
Export
A module may provide services and resources to other modules
by declaring an exported URI address space.
A module may also export a portion of a its Java class package space.
The exported address space is specified in the
export
section using a set of URI regular expression patterns.
Specifications in the
export
section are logically combined if they
define overlapping address spaces.
Inbound requests that match
any
export expression are
captured by the module.
Regular expressions are powerful and their syntax can be confusing.
You may want to read the
Regular Expression Tutorial
for additional information.
Here is an example export section:
<export>
<uri>
<match>ffcpl:/mymodule/.*</match>
<match>active:myOnlyURA.*</match>
</uri>
<class>
<match>org\.ten60\.netkernel\.mymodule\.proxy\..*</match>
</class>
</export>
Here, the module is exporting two URI patterns.
The first:
ffcpl:/mymodule/.*
matches everything below the
/mymodule/ path for the
ffcpl
scheme.
The second:
active:myOnlyURA.*
,
exports the URI of an accessor contained in this module.
This module also exports a public Java class space that includes
the package
org.ten60.netkernel.mymodule.proxy
and its sub-packages.
All exported classes are available to the classloader of importing modules.
The NetKernel module architecture extends the JAR model and provides
private encapsulation of classes with select exporting.
When combined with the NetKernel's module version management,
Java developers are provided a powerful, safe, and fine-grained
class management system.
Rewrite
The rewrite section decouples the
module's exported address space and the private address space.
Requests accepted into the exported address space are sent
through the
rewrite
rules before being delivered to
the private address space which is defined by the
mapping
rules
export
|
v
rewrite
|
v
mapping
This decoupling provides a point of architectural flexibility
valuable during development and life-long maintenance of an application.
The section is aptly named "rewrite" because the destination address in a request can
be completely rewritten into a new form.
In the rewrite section rule entries contain a
match
and a
to
element.
The
match
element contains a regular expression pattern that
is textually matched to the URI address contained in a request.
If the regular expression pattern matches an address, then the
to
element defines how the address is rewritten.
In the following example rewrite section we find a single rule:
<rewrite>
<rule>
<match>ffcpl:/mymodule/(.*)</match>
<to>ffcpl:/$1</to>
</rule>
</rewrite>
This rule moves the exported address space to the top
level of the private address space.
An arbitrary number of rewrite rules may be specified and they are
evaluated in order, progressing along a chain of rewrites.
The section on
internal module patterns
demonstrates a number of common ways
rewrite rules can be used to map the exported address space to the internal private address
space.
In particular it shows how URI mapping is used to initiate the execution of runtime processes.
Mapping
The
mapping
section defines the module's
private address space
and the linking between specified addresses
and accessors.
The private address space is empty until entries
are placed in the
mapping
section.
Requests are passed through the mapping
rules one by one.
The first rule that matches determines the resolution
(except for the rewrite rule which changes the
request and passes it along).
This implies the order of rules is significant.
The elements supported are:
this
,
rewrite
,
import
,
ura
,
and
super
.
Here is the mapping section from the example module definition.
<mapping>
<ura>
<match>active:myOnlyURA.*</match>
<class>org.ten60.netkernel.mymodule.accessor.MyOnlyURAAccessor</class>
</ura>
<import>
<uri>urn:org:ten60:netkernel:ext:xml</uri>
<version-min>1.0</version-min>
<version-max>2.0</version-max>
</import>
<import>
<uri>urn:org:ten60:netkernel:ext:layer1</uri>
<version-min>1.0</version-min>
<version-max>2.0</version-max>
</import>
<import>
<uri>urn:org:ten60:netkernel:cache:default</uri>
<version-min>1.0</version-min>
<version-max>2.0</version-max>
</import>
<rewrite>
<match>ffcpl:/mymodule/(.*)</match>
<to>ffcpl:/org/ten60/netkernel/mymodule/$1</to>
</rewrite>
<this>
<match>ffcpl:/org/ten60/netkernel/mymodule/.*</match>
</this>
<super />
</mapping>
This
this
entries define portions of the internal private address space
that map to the
module's file system
with the ffcpl: scheme.
Multiple
this
specifications can be made in the mapping section or multiple match
elements can be placed within one this element.
For example, the following two rules declare that a portion
of the ffcpl: name space are mapped to the module's
file system.
<this>
<match>ffcpl:/src/.*</match>
<match>ffcpl:/images/.*</match>
</this>
The example establishes a direct map from the virtual space to the physical space
where the directories src
and images
are
located just below the root of the module's directory.
ffcpl:/src/.* --> /src/...
ffcpl:/images.* --> /images/...
this
entries can also override the default resource expiry as specified in the
Kernel Parameter Manager
.
For example:
<this>
<expiry>
<match>.*\.png</match>
<offset>60000</offset>
</expiry>
</this>
will force all .png resources loaded by this module to have
a recheck period of 60 seconds.
In general you
do not need to worry about overriding the default resource
expiry but it may be useful for deployment
optimization.
Note:
Java class resources do not need to be specified by
this.
A module's Java classloader can access all internal module resources.
Import
The
import
element integrates the public address space of the identified module
into the private internal address space of the importing module.
The
import
element may include a constraint on
the range of module version numbers that are acceptable.
If the a module with an acceptable version is not available,
then the importing module will not be configured and a failure
will be indicated on start-up.
Module versioning allows multiple versions of modules and legacy
modules to co-exist with newer or future versions without
collision.
An
import
element contains a
uri
element which specifies the URI of the module you wish to import.
Optionally it can use the
version-min
and
version-max
elements to constrain the valid versions for importation.
For example, the following
import
element brings in the address space of the layer1 NFK code
as long as it is between version 1.0 and 2.0 inclusive:
<import>
<uri>urn:org:ten60:netkernel:ext:layer1</uri>
<version-min>1.0</version-min>
<version-max>2.0</version-max>
</import>
It is important to note that if the address in a request
matches the imported module's exported address space
then that request enters the imported module for processing
and it is no longer considered to be within the importing
module.
URA
The
ura
element defines the link between
a portion of the private address space and an accessor.
The
ura
element consists of a
match
and an associated
class
element.
For example, the following ura element uses the
regular expression
active:myOnlyModule.*
and links
that portion of the private address space to the
myOnlyModule
accessor.
The class specification is the fully qualified name of the Java class
implementing the accessor.
<ura>
<match>active:myOnlyURA.*</match>
<class>org.ten60.netkernel.mymodule.accessor.MyOnlyURAAccessor</class>
</ura>
Note: it is important to match everything after the scheme
and module name, using '.*', as additional active URI
arguments may be passed to your accessor
at the end of the address.
For example, with the example above, the following request URI addresses
will be mapped to the accessor:
active:myOnlyURA
active:myOnlyURA+operator@ffcpl:/myfile.txt
All parameters that are appended to the URI address are available to the accessor's code.
For information about writing accessors, please refer to the
Extension Developer's Guide
.
Rewrite
The rewrite otherwise works identically to the rules documented above in the rewrite
section of the module definition.
Best Practices
The evaluation order of the rules defining the private address space
are significant.
The following tips are offered for their organization:
-
Resources exposed in the exported address space should
be placed at the top of the mapping section.
-
Imported modules should be next.
-
Next, include
this
entries that describe private local resources
-
Last include the super
element - if it is appropriate for the module.
Address Resolution
One element in the mapping
section not yet discussed is super.
super - part 1
To understand super
consider what
happens if
we generate an internal request
for a resource located in another module.
We cannot expect a reusable library module
to
import all modules that might use it.
In order
to deal with this situation we can hand the request back up to requestor by specifying
super.
super
should always be the last item in the mapping
specification since a request hand-off to super will never return to the current module and so any
mappings below
super can never be reached.
super
can be quite hard to comprehend abstractly but is easily understood with an
example.
However before we
can do that we need to put together what we know about modules and see that connected modules constitute
an
application, we'll return to
super
below.
Application Composition
It is probably clear by this point that the ability of a module to import other modules allows modules to
be
composed into applications.
An application is a hierarchical set of imported modules.
An application typically has a single root module, which is referred to as the 'Fulcrum'.
In order to execute the application it must receive a request. Initiating requests are always generated
from a
transport, since a transport maps
external requests to internal requests.
Since applications are invoked from transport generated requests it
generally makes sense to put the
transport in the Fulcrum module - there are cases where this is not so
but it's good rule of thumb to start with.
The figure below shows a
simple model of an application.

An initiating URI request is initially handled in the transport's home module.
If it fails to match, the Module
Manager will traverse the mapping
of the current module entering it's import modules from above,
matching on their publicly exported interface,
applying their rewrite rules etc.
If an imported module's public interface does not match,
the original request is then tried on the
next module and so on.
A transport initiated request therefore leads the application to traverse
the publicly exported interface of a set
of modules until it finds a match.
It can be seen that a module should generally only export public
interfaces that
it can satisfy itself.
Of course it is fine for a module to satisfy a resource request by using the resources provided from
an imported
module, or by requesting resources
from above by handing back a request by calling
super
- we must now return to the super discussion.
super - part 2
Now that we have described the structure of an application we can resume the discussion of
super.
Below is a simple application configuration.
Suppose that the transport issues a request for a DPML idoc to be executed.
That idoc contains a single
instruction to transform resource1.xml with
transform1.xsl and return the result. Let's go through it a step at a time.

-
A request for an idoc is generated in the Fulcrum,
active:dpml+operand@operation1.idoc.
The Module Manager goes through the Fulcrum module's mappings. It discovers that
active:dpml.*
is matched by the imported DPML module.
The request is issued to the DPML module, the active:dpml... URI initiates an instance of the DPML
runtime
accessor which begins to
execute.
Let's skip over that the DPML accessor must request operation1.idoc from somewhere and assume it has
got all it needs to start
executing the idoc.
-
The idoc contains a single XSLT instruction. The DPML accessor issues a request for the XSLT Accessor
to
perform the operation with
active:xslt+operand@resource1.xml+operator@transform1.xsl.
First the module manager looks for a match in the DPML
module's mappings.
It doesn't find one but it does find
super
as the last mapping entry. The
super
declaration causes
the Module manager to return to the requestor module, which in this case was the Fulcrum.
-
The module manager attempts to find a match for the request in the Fulcrum's mappings.
It finds that the
Fulcrum imports the XSLT module
and finds a match,
active:xslt.*
, for the request.
Request is handed to the XSLT module, the XSLT accessor is initiated
with the request and the
transform is processed, again we leave aside how resource1.xml and transform1.xsl
are obtained - though it would require a super call back up to the Fulcrum again.
The XSLT transform result is
returned to the DPML accessor
since that was XSLT's requestor.
The DPML accessor returns the result to the Fulcrum and thence back to the
original transport requestor.
This walk through shows a typical example of the use of
super.
It allows a module at the same level or lower to make requests for accessors
it has no prior-knowledge of.
In this case DPML is not aware of any accessors, it's only job is issue requests and
process the results of those
requests, based upon the structure of the idoc it is asked to execute.
Two important points must be made.
-
super
always hands a request back up the callstack.
That is a module does not necessarily hand a request
back to the module which imports it since it could be
imported multiple times and may even be shared between
two or more applications.
- When traversing
super
the Module Manager will only ever look for a request match once for a given module.
super
can never result in a circular race condition as once a super hand-off results in a downwards
traversal into
an import, super will not be called again for that request as the downwards import has stated that it
can
handle the request.