1. Introduction

JBoss Modules is a standalone implementation of a modular (non-hierarchical) class loading and execution environment for Java. In other words, rather than a single class loader which loads all JARs into a flat class path, each library becomes a module which only links against the exact modules it depends on, and nothing more. It implements a thread-safe, fast, and highly concurrent delegating class loader model, coupled to an extensible module resolution system, which combine to form a unique, simple and powerful system for application execution and distribution.

JBoss Modules is designed to work with any existing library or application without changes, and its simple naming and resolution strategy is what makes that possible. Unlike OSGi, JBoss Modules does not implement a container; rather, it is a thin bootstrap wrapper for executing an application in a modular environment. The moment your application takes control, the modular environment is ready to load and link modules as needed. Furthermore, modules are never loaded (not even for resolution purposes) until required by a dependency, meaning that the performance of a modular application depends only on the number of modules actually used (and when they are used), rather than the total number of modules in the system. And, they may be unloaded by the user at any time.

1.1. About this manual

The source code for this manual is presently hosted on GitHub. Contributions and corrections are welcome; when opening a pull request, please remember to set the base branch to manual.

2. JBoss Modules concepts

There are several important terms and concepts that must be understood to use JBoss Modules effectively. Some of these terms might be familiar, but their complete definition may be surprising nonetheless.

Terms and definitions
Resource

a data entity which may be found and read by way of a class or class loader, represented in JBoss Modules by the Resource interface

Class loader

the Java entity responsible for the creation, management, and resolution of new classes and resources

Defining class loader

the class loader which defines a given class; this class loader can be found by way of the Class.getClassLoader() method

Initiating class loader

the class loader which was used to start a class load request; this will be the defining class loader of the class which caused a class load operation to begin

Class definition

the first stage of loading a new class, whereby a class name is resolved to a sequence of bytes and registered to the JVM; the JVM also verifies the class bytes at this time

Class resolution

the second stage of loading a new class, whereby all the symbolic references in the class constant pool are resolved to concrete values

Class initialization

the third and final stage of loading a new class, whereby the class or interface initialization method is executed (which may include executing dynamic code to populate the initial value of static fields)

Visibility

the set of classes and resources that a class can directly find via its own class loader

Module

an encapsulated and isolated grouping of classes and resources, which can reference (by dependency) other modules or content; a module is represented by a Module object and backed by a ModuleClassLoader object

Dependency

a reference from one module to another, which indicates that the source module consumes some content from the target module

Module specification

a description of a module, which includes its dependencies and their order as well as its contents (classes and resources)

Isolation

the concept of preventing interference between two class loaders; isolated class loaders can (for example) have classes and packages which have the same name as classes and packages in a different class loader from which it was isolated

Package name

the part of a class name which precedes the final period (.) character

Package

a set of classes which have both the same package name and the same defining class loader

Module loader

the JBoss Modules entity responsible for the creation, management, and resolution of new modules

Module finder

the portion of the module loading logic which is responsible for locating and defining module specifications

Resource loader

the JBoss Modules entity responsible for locating and serving class, package, and resource data to the module class loader

2.1. The class path

Most Java programmers are familiar with the class path. This is the set of files and directories which comprise a typical Java program, usually given in a long list on the command line. These classes are all loaded by the same class loader, known as the application class loader.

The application class loader uses a parent-first loading methodology: when a request to load a class is received by the class loader, it first delegates to the platform class loader, which typically includes all the classes which are provided by the JDK itself. If the platform class loader cannot provide the given class, then the class path defined in the application class loader is checked in order from start to end.

This simple system is sufficient for many simple applications. However, the lack of isolation between class path items can cause a variety of problems for more complex systems.

2.2. Modules, defined

The concept of a module in JBoss Modules is based on the notion of isolated class loaders. Each module is backed by a single class loader, and is comprised of a small number of resource roots (for example, JAR files, or directories on the file system) in combination with a list of dependencies which determine the content from other modules that will be imported in to this one. The graph formed when all dependencies are resolved (which may include cycles) determines what content is ultimately visible to a module.

This allows the code in a module to know definitively what classes and resources it is sharing a class loader with, which can resolve conflicts caused when (for example) more than one version of a library must be used within the same application, or when certain libraries need control over what other libraries it loads services or other resources from.

2.3. The module loading process

Modules are loaded in a process which is similar (in concept) to the Java class loading process. The module loader queries each of its module finders in order, asking for a module with the given name. If found, the module finder may return a specification object which describes the module, which is then used by the module loader to establish the new module. The new module contains a module class loader which is then used to load the classes in the module.

If the module finder does not find the module, the module loader may delegate to another module loader to find the module. If there is no delegation process established for the module loader, or the delegate module loader(s) did not find the module, then typically an exception will be thrown.

2.4. Module names

Each module has a name. This is a unique textual string that identifies the module being loaded.

The name syntax is constrained only by the module loader in use. The filesystem module repository uses a dot-separated, reverse domain name convention which is similar to the Java package name convention; here are some examples:

Typical filesystem repository module names
  • org.apache.commons.logging

  • org.jboss.remoting

  • cglib

  • javax.ejb.api

  • ch.qos.cal10n

The filesystem JAR repository uses the absolute path name of the JAR (for example, /opt/jars/commons-lang.jar or C:\MyJars\commons-lang.jar). Other module loaders may use different conventions. It is the responsibility of each module loader or module finder to enforce any validity checks on module names.

2.5. Module versions

Since JBoss Modules 1.6, a module may include an optional version string. The module version is used for diagnostic purposes, and does not constrain or affect the resolution of the module graph (contrast with OSGi, wherein the bundle version is a part of its identity and strongly affects resolution logic).

2.5.1. Module version syntax

A module version adheres to a strict syntax, which may described by the following grammar:

Version string grammar
version         = letters-version / numbers-version

letters-version = [ version separator ] letters / numbers-version letters

numbers-version = [ version separator ] numbers / letters-version numbers

separator       = "." / "-" / "+" / "_"

letters         = 1*( letter )

letter          = ; any valid Unicode code point for which Character.isLetter(int) is true

digits          = 1* ( digit )

digit           = ; any valid Unicode code point for which Character.digit(codePoint, 10) returns a valid value

JBoss Modules does not enforce one single version string convention; users are encouraged to choose whatever convention works best for their situation. It is common, however, to use the version of the corresponding Maven artifact for a module’s version string, for modules which are based on Maven artifacts.

The following are all valid version strings:

Valid version string examples
  • 1.0.Beta3

  • 10

  • Release9

  • 12r6u4

  • 5.9_224u5-build-2948+2017-11-04_11-32-04.003

2.5.2. Examining versions at run time

Versions are represented by the Version class in JBoss Modules. They are Comparable and have a stable sort order: short version strings sort before long version strings, and digit segments sort before letter segments. Letter segments are sorted in a lexical (case-sensitive) manner using the Unicode code point of each letter. Numeric segments are sorted by numeric value. Separators sort in the order empty, '.', '-', '+', '_' (from highest to lowest).

In addition, the org.jboss.modules.Version.Iterator class may be used to iterate the parts of a version, which may be useful in certain dynamic or plugin based systems, or for the introduction of a local version verification policy, in order to extract semantic information from a given version string.

When JBoss Modules runs on Java 9 or later, the module name and version will appear in the stack trace of any exceptions, which is useful for diagnostic purposes.

2.6. Module version slots

Version slot identifiers were used when you wish to have more than one instance of a module in a module loader under the same name.  This may occur when introducing a new major version of a module which is not API-compatible with the old version but is used by newer applications.  A version slot identifier is an arbitrary string; thus one can use just about any system they wish for organization.  If not otherwise specified, the version slot identifier defaulted to main.

When identifying a module in a string, the version slot identifier could be appended to the module name, separated by a colon :.  For example, the following two module identifier strings refer to the same module:

  • org.jboss.remoting:main

  • org.jboss.remoting

The following three module identifier strings refer to different modules:

  • org.jboss.remoting:2

  • org.jboss.remoting:3

  • org.jboss.remoting

Within the Modules API, a module identifier with a slot was represented by the org.jboss.modules.ModuleIdentifier class, which has the ability to parse identifier strings as well as assemble a name or a name plus a version slot identifier into a module identifier.

Note

The concept of slot names has been deprecated since version 1.5 in order to more closely align with the future Java Platform’s module system, which has only names. Legacy module identifiers containing a slot component are transformed into plain names in the following way:

  • If the slot is main or is not given, the effective module name is equal to the name portion of the identifier.

  • If the slot is not main, the effective module name is equal ot the name portion of the identifier, followed by a colon : character, followed by the slot name.

  • If the name portion of the identifier contains a colon : character, the character is escaped with a backslash \ so that in the final name, it is given as \:.

2.7. Class loading order

Class loaders in Java can (for the most part) be broadly described as falling in to one of two categories: parent-first or child-first.

In JBoss Modules, module delegation is done graph-wise, not tree-wise, so modules generally may have more than one "parent". Regardless, each module loader can determine what class loading order is used, including exotic hybrid orders wherein some dependencies are parent-first (i.e. checked before the current module) and some are child-first (i.e. checked after the current module). However, all of the module loaders which are included with JBoss Modules use child-first loading exclusively.

The best reason for this can be described using a simple example. Given two modules, A and B, which each depend on one another, like this:

Module cycle
Figure 1. Simple cycle

If both of these modules contain the same class (or, more likely, resource), then when A attempts to load the class or resource, it will get B’s version; likewise if B loads the class or resource, it will get A’s version. Needless to say, this can be very surprising at run time, and difficult to debug as well. Using child-first loading, each module will see its own classes, avoiding this problem.

However, there is a reason that parent-first loading is used pervasively in the JDK’s simple class loading delegation tree. If a library is provided by a parent class loader, then it may be that the child class loader should not be able to load another copy of it. Using parent-first loading is a simple way to prefer the parent’s version without affecting the child too much.

With JBoss Modules, the better solution to this requirement is to exclude the packages which contain duplicated or undesirable resources. The best approach here is to have care when packaging a given module, to ensure that it does not contain redundant classes. However, container authors will note that this sometimes does not happen. A container can however implement this technique on an automatic basis, by examining dependency package names, and pruning that set of package names from the set of packages that are to be defined by the module’s own resources.

3. Using JBoss Modules to run an application

Since module definition is essentially pluggable, a module can be defined in many different ways.  However, JBoss Modules ships with two basic implemented strategies which are most commonly utilized when running an application.

The first strategy is the Filesystem module repository approach.  Modules are organized in a directory hierarchy on the filesystem which is derived from the name and version of the module.  The content of the module’s specific directory is comprised of a simple module descriptor and all of the content itself (JARs or loose files).

The second strategy is the JAR module repository approach. This approach runs individual JARs as modules.

3.1. Filesystem module repository

The filesystem module repository is a module storage format which is used for applications which are comprised of a graph of modules. The basis of operation for the filesystem module repository is that modules are located by scanning one or more module path directories for a module descriptor whose location is determined by converting the dot-separated segments of the module name to path elements, followed by a path element which consists of the version slot for that module (if any). This path is then appended to each module path root in turn until a file named module.xml is found within it.

The module.xml file format is described in the XML Module descriptors section.

3.2. JAR module repository

The JAR-backed module repository format allows individual JARs to run as modules directly. This repository type is designed for executing JARs from the command line as well as situations where a JAR may be deployed in a container such as the JBoss Application Server.

Each JAR’s MANIFEST.MF file can be used to define dependencies and other module-related information. In addition, JARs which are loaded by this repository may themselves contain embedded module repositories which are visible only to that JAR. This method of packaging has an advantage over traditional "fat" JAR approaches in that each nested module has the full JBoss Modules isolation and linking capabilities available.

The format and meaning of the various MANIFEST.MF attributes are described in the JAR repository module descriptors section.

4. XML Module descriptors

Module loaders may, but are not required to, use an XML module descriptor. An XML module descriptor file describes the structure, content, dependencies, filtering, and other attributes of a module. This format is highly expressive and is tailored for use by module loaders which require the module description to reside alongside its content, rather than inside it.

Below is an example of a module descriptor used by the JBoss Application Server:

Example module descriptor
<?xml version="1.0" encoding="UTF-8"?>

<module xmlns="urn:jboss:module:1.6" name="org.jboss.msc" version="1.0.1.GA">

    <main-class name="org.jboss.msc.Version"/>

    <properties>
        <property name="my.property" value="foo"/>
    </properties>

    <resources>
        <resource-root path="jboss-msc-1.0.1.GA.jar"/>
    </resources>

    <dependencies>
        <module name="javax.api"/>
        <module name="org.jboss.logging"/>
        <module name="org.jboss.modules"/>

        <!-- Optional deps -->
        <module name="javax.inject.api" optional="true"/>
        <module name="org.jboss.threads" optional="true"/>
        <module name="org.jboss.vfs" optional="true"/>
    </dependencies>
</module>

Not every JBoss Modules release has a corresponding namespace. The supported namespaces are as follows:

Table 1. Allowed namespaces for module.xml files
Namespace Version(s)

urn:jboss:module:1.0

1.0+

urn:jboss:module:1.1

1.1+

urn:jboss:module:1.2

1.2+

urn:jboss:module:1.3

1.3+

urn:jboss:module:1.5

1.5+

urn:jboss:module:1.6

1.6+

Note that every version of JBoss Modules supports the full descriptor formats of all previous versions, including elements that were deprecated or removed in later versions.

4.1. The root module element

The root element of the module descriptor determines what type of module is being specified. There are two types: a regular module and a module alias.

Regular module descriptors have a root element named module from the urn:jboss:module:xxx namespace. The module element supports the following attributes:

Table 2. Attributes of the module element
Attribute Type Use Version(s) Description

name

string

required

1.0+

The name of the module. This name must match the name of the module being loaded.

slot

string

optional

1.0-1.5

The modules slot. If not specified, defaults to "main". Deprecated in 1.5, removed in 1.6.

version

string

optional

1.6+

The optional version designation of the module. Appears in stack traces on Java 9+.

The module element may contain any of the following elements:

Table 3. Child elements of the module element
Element Minimum Maximum Version(s) Description

main-class

0

1

1.0+

The name of the main class of this module, if any.

properties

0

1

1.1+

A list of properties to make available on this module.

resources

0

1

1.0+

The resources that make up this module.

dependencies

0

1

1.0+

The dependencies for this module.

exports

0

1

1.0+

The path filter expressions to apply to the export filter of the local resources of this module.

permissions

0

1

1.2+

The permissions that are to be granted to this module when a security manager is present.

4.1.1. The main-class element

A module which is defined with a main-class element is said to be executable. In other words, the module name can be listed on the command line, and the standard static main(String[]) method in the named module’s main-class will be loaded and executed.

The main-class element supports the following attributes:

Table 4. Attributes of the main-class element
Attribute Type Use Version(s) Description

name

string

required

1.0+

The name of the main class, which must be visible from this module.

This element may not contain any nested elements.

Note
The main class need not come from the module’s actual resources, nor does it need to be exported. Any public class which is visible to the module - which includes all imported dependencies as well as all resource roots - is a valid main class, as long as it has a method with the signature public static void main(String[] args).

4.1.2. The resources element

In order for a module to actually have content, you must define the resources element with at least one resource root.

A resource root is a specification of a location where the class loader for a module will look for classes and resources. Each module has zero or more resource roots, though most regular modules will contain exactly one, which refers to the JAR file with the module’s content.

It is possible to define resource roots for a module which correspond to JAR files as well as file system directories, just like class paths. File system directory resource roots have the additional property of supporting the specification of native libraries, which cannot be loaded from JAR files.

The resources element may contain any of the following elements:

Table 5. Child elements of the resources element
Element Minimum Maximum Version(s) Description

resource-root

0

unbounded

1.0+

A file or directory on the filesystem whose contents are to be added to the module as classes and resources.

artifact

0

unbounded

1.1+

A Maven artifact whose contents are to be added to the module as classes and resources.

4.1.2.1. The resource-root element

The resources element does not support any attributes; it contains zero or more resource-root and/or artifact elements. The resource-root element supports the following attributes:

Table 6. Attributes of the resources element
Attribute Type Use Version(s) Description

path

string

required

1.0+

The path of this resource root, relative to the location of the module.xml file.

name

string

optional

1.0+

The name of the resource root. If not specified, defaults to the resource root’s path.

In addition, the resource-root element may contain a nested element:

Table 7. Child elements of the resource-root element
Element Minimum Maximum Version(s) Description

filter

0

1

1.0+

A path filter to apply to this resource root. If not specified, all paths are accepted.

See the section on filter definition for more information about defining filters.

4.1.2.2. The artifact element

Since JBoss Modules 1.3, the artifact element can be used to cause a module’s contents to be built from one or more Maven artifacts, and can be used in place of resource-root. The artifact element may contain the following attributes:

Table 8. Attributes of the artifact element
Attribute Type Use Version(s) Description

name

string

required

1.3+

The colon-delimited Maven coordinates (GAV) of the artifact to include.

In addition, since JBoss Modules 1.5, the artifact element may contain a nested element:

Table 9. Child elements of the artifact element
Element Minimum Maximum Version(s) Description

filter

0

1

1.5+

A path filter to apply to this resource root. If not specified, all paths are accepted.

See the section on filter definition for more information about defining filters.

4.1.2.3. The properties element

The modules API exposes a method which can read property (string key-value pair) values from a module. To specify values for these properties you use the properties element which can contain zero or more property elements, each supporting the following attributes:

Table 10. Attributes of the property child element
Attribute Type Use Version(s) Description

name

string

required

1.1+

The name of the property.

value

string

optional

1.1+

The property value; if not given, the property value defaults to true.

4.1.2.4. The dependencies element

A module may express one or more dependencies on other module(s) via the dependencies element.

The dependencies element does not support any attributes. It contains zero or more nested elements as follows:

Table 11. Child elements of the dependencies element
Element Minimum Maximum Version(s) Description

module

0

unbounded

1.0+

A module name upon which a dependency should be added.

system

0

unbounded

1.0+

A specification for expressing a dependency upon the system or application class path.

The module dependency element

The module element supports the following attributes:

Table 12. Attributes of the module dependency element
Attribute Type Use Version(s) Description

name

string

required

1.0+

The name of the module upon which this module depends.

slot

string

optional

1.0-1.5

The version slot of the module upon which this module depends; defaults to main. Deprecated in 1.5, removed in 1.6.

export

boolean

optional

1.0+

Specify whether this dependency is re-exported by default; if not specified, defaults to false.

services

enum

optional

1.0+

Specify whether this dependency’s services [1] are imported and/or exported. Possible values are none, import, or export; defaults to none.

optional

boolean

optional

1.0+

Specify whether this dependency is optional; defaults to false.

In addition, the module element supports the following nested elements:

Table 13. Child elements of the module dependency element
Element Minimum Maximum Version(s) Description

imports

0

1

1.0+

A path filter used to restrict what paths are imported from the dependency.

exports

0

1

1.0+

A path filter used to restrict what imported paths are re-exported from this module.

Example of adding an explicit exclude for a dependency
<dependencies>
    <module name="org.jboss.example">
        <imports>
            <exclude-set>
                <path name="org/jboss/example/tests"/>
            </exclude-set>
        </imports>
    </module>
</dependencies>

See the section on filter definition for more information about filters.

The system dependency element

The system element expresses a dependency which is satisfied by accessing paths and packages from the class loader which loaded JBoss Modules (this is usually the system’s application class loader). The element supports the following attributes:

Table 14. Attributes of the system dependency element
Attribute Type Use Version(s) Description

export

boolean

optional

1.0+

Specify whether this dependency is re-exported by default; if not specified, defaults to false.

It also contains nested elements as follows:

Table 15. Child elements of the system dependency element
Element Minimum Maximum Version(s) Description

paths

1

1

1.0+

Specify the list of paths (or packages, with . transformed to /) which are exposed by this dependency.

exports

0

1

1.0+

A filter which restricts the list of packages/paths which are re-exported by this module. If not specified, all paths are selected (does not apply if the export attribute on the system element is false or unspecified).

4.1.3. The permissions element

The permissions element contains the following child elements:

Table 16. Child elements of the permissions element
Element Minimum Maximum Version(s) Description

permission

0

unbounded

1.2+

A granted permission.

The nested permission element supports the following attributes:

Table 17. Attributes of the permission element
Attribute Type Use Version(s) Description

permission

string

required

1.2+

The qualified class name of the permission to grant.

name

string

optional

1.2+

The permission name to provide to the permission class constructor.

actions

string

optional

1.2+

The (optional) list of actions, required by some permission types.

Warning
See JBoss Modules and the Java security manager for important information on using the security manager.

4.2. The root module-alias element

A module alias descriptor defines a module which is simply another name for a second module. The root element is called module-alias and supports the following attributes:

Table 18. Attributes of the module-alias element
Attribute Type Use Version(s) Description

name

string

required

1.0+

The name of the module. This name must match the name of the module being loaded.

slot

string

optional

1.0-1.5

The version slot. If not specified, defaults to main. Deprecated in 1.5; not supported module descriptors in the urn:jboss:module:1.6 namespace or later.

target-name

string

required

1.0+

The name of the module to which this alias refers.

target-slot

string

optional

1.0-1.5

The version slot of the module to which this alias refers. If not specified, defaults to main. Deprecated in 1.5; not supported module descriptors in the urn:jboss:module:1.6 namespace or later.

4.3. Filters

Many elements in the XML descriptor format support embedding a filter element. The name of the filter element varies by context, but they always support the same content.

Table 19. Child elements of a filter element
Element Minimum Maximum Version(s) Description

include

0

unbounded

1.0+

A path specification to include.

exclude

0

unbounded

1.0+

A path specification to exclude.

include-set

0

unbounded

1.0+

An exact path set to include.

exclude-set

0

unbounded

1.0+

An exact path set to include.

4.3.1. Path specifications

A path specification is a special pattern or "glob" which is compared against incoming path names to determine if they match the specification. JBoss Modules paths are a sequence of names separated by a literal forward slash /. A path specification allows wildcard characters to take the place of part of the path, and is comprised of a sequence of path elements.

Table 20. Attributes of a path specification
Element Minimum Maximum Version(s) Description

name

0

unbounded

1.0+

A path string, which possibly includes one or more wildcards.

The following wildcards are supported:

Table 21. Supported wildcard expressions of a path specification
Pattern Version(s) Description

?

1.0+

Match a single character.

*

1.0+

Match a single path component.

**

1.0+

Match all the remaining path components.

4.3.2. Path sets

A path set is a fixed set of literal path names. Path sets do not support wildcards, however, they are more efficient than path specifications for large numbers of paths because an entire set can be checked in one operation, as opposed to path specifications which must be checked one pattern at a time.

The path set is comprised of a sequence of path elements.

Table 22. Path set child elements
Element Minimum Maximum Version(s) Description

path

0

unbounded

1.0+

One path found in this set.

Table 23. Attributes of the path set path element
Element Minimum Maximum Version(s) Description

name

0

unbounded

1.0+

A literal path string.

4.4. Native Libraries

When using the default file system-backed module loader, each module defined in the module repository has an additional resource root automatically added to it solely for the purposes of supporting native libraries in a module. This resource root recognizes a special directory in each module root named lib.

The module class loader will search for native libraries by encoding the current detected platform into a directory name, appending it to the path of the lib directory, and testing the resultant directory for a matching native library file. For example, imagine a module named org.foobar.gizmo which contains a native library which runs on Linux for Intel 32- and 64-bit processors. It would have a module directory structure similar to this:

Directory structure of a module with native libraries
org/
└─ foobar/
   └─ gizmo/
      └─ main/
         ├─ module.xml
         ├─ gizmo-1.0.jar
         └─ lib/
            ├─ linux-i686/
            │  └─ libgizmo.so
            └─ linux-x86_64/
               └─ libgizmo.so

In this case, the appropriate libgizmo.so will automatically be located. On platforms without a corresponding library, no library will be loaded.

The platform string is in the form <osname>-<cpuname>. The following values may be used for the OS name:

Allowed native operating system names
  • linux

  • macosx

  • win

  • os2

  • solaris

  • mpeix

  • hpux

  • aix

  • os390

  • freebsd

  • openbsd

  • netbsd

  • irix

  • digitalunix

  • osf1

  • openvms

  • ios

  • unknown

The following values are recognized for the CPU name:

Allowed native CPU architecture names
  • sparcv9

  • sparc

  • x86_64

  • i686

  • x32

  • ppc64

  • ppc

  • armv4

  • armv4t

  • armv5

  • armv5t

  • armv5t-iwmmx

  • armv5t-iwmmx2

  • armv6

  • armv7a

  • aarch64

  • parisc64

  • parisc

  • alpha

  • mips

  • unknown

5. JAR repository module descriptors

When JARs are loaded by the JAR repository loader, the metainformation of the module is stored within its META-INF/MANIFEST.MF file.

5.1. Main manifest attributes

The following attributes are supported in the main section of the manifest:

Table 24. Main manifest attributes
Attribute Use Version(s) Specified By Description

Dependencies

optional

1.0+

JBoss Modules

A comma-separated list of module dependency specifications, as described in The Dependencies manifest attribute.

Class-Path

optional

1.0+

Standard JAR

A whitespace-separated list of paths (relative URLs) of JARs that this JAR depends on.

Main-Class

optional

1.0+

Standard JAR

The name of the main class of this JAR module.

Module-Version

optional

1.7+

JBoss Modules

The version string to use for the module represented by this JAR.

Extension-List

unsupported

1.7+

Standard JAR

Not presently supported; this functionality is expected to be removed from Java.

Note
As of JBoss Modules version 1.7 and later, each JAR listed in the Class-Path is loaded as a separate module. Prior to 1.7, the class path was collapsed into a single module with multiple resources.

5.1.1. The Dependencies manifest attribute

The Dependencies manifest attribute specifies a comma-delimited list of module dependencies to add to the JAR module. The module dependency may be located within the JAR itself, or it may be loaded from the filesystem repository by way of the module path.

The dependency attribute has the following overall syntax:

Dependency attribute grammar
dependencies    = module-name *( " " modifier ) *( "," module-name *( " " modifier ) )

module-name     = ; any valid module name

modifier        = "optional" / "export" / "services"

The following modifiers are supported:

Table 25. Dependency manifest attribute modifiers
Modifier Use Version(s) Description

optional

optional

1.0+

Specify that the dependency is optional.

export

optional

1.0+

Specify that the paths imported from the dependency should be re-exported to modules which import this module.

services

optional

1.7+

Specify that the given dependency’s ServiceLoader-based services should be imported into this module.

Note
Some implementations (such as WildFly) support additional flags which are not listed here. Flags which are not recognized are silently ignored.
5.1.1.1. Adding the Dependencies attribute using Maven

Below is an example of how the Dependencies attribute can be added to a projects Maven pom.xml.

Maven snippet to add Dependencies to the manifest
<plugins>
   <plugin>
      <artifactId>maven-jar-plugin</artifactId>
      <version>2.3.1</version>
      <configuration>
         <archive>
            <manifestEntries>
               <Dependencies>org.some.module, org.another.module</Dependencies>
            </manifestEntries>
         </archive>
     </configuration>
   </plugin>
</plugins>

5.2. JAR Permissions

As of JBoss Modules 1.7, support has been added to allow JAR module permissions to be specified using a standard Java EE META-INF/permissions.xml file. If this file is found within the JAR, then that file is read and the permissions that are listed therein are used as the static permission set of the module’s protection domain. The set of permissions may be additionally restricted, depending on the execution environment.

Note
While the permissions.xml format is based on the Java EE standard, JBoss Modules does not require that the root element be qualified with a namespace or version attribute.
Example of a META-INF/permissions.xml file
<?xml version="1.0" encoding="UTF-8"?>

<permissions>
    <permission>
        <class-name>java.util.PropertyPermission</class-name>
        <name>org.mycompany.*</name>
        <actions>read</actions>
    </permission>
    <permission>
        <class-name>java.io.FilePermission</class-name>
        <name>/opt/application/data/-</name>
        <actions>*</actions>
    </permission>
</permissions>
Warning
See JBoss Modules and the Java security manager for important information on using the security manager.

5.3. Nested module repository

JARs which are launched on the command line (or programmatically established as modules) can optionally include a private, nested repository.

The root of this repository is the modules directory within the root of the archive. Within this directory, files are laid out exactly as described in the Filesystem module repository section, with one difference: JAR file content is not supported, so during the assembly of such a repository, the nested JAR must be extracted. Note that Maven artifact resources are supported.

Note
Prior to JBoss Modules 1.7, only the primary JAR given on the command line or as the main argument to JarModuleFinder could contain embedded modules, and they were not private to that JAR, being visible to other JARs in the transitive Class-Path. In 1.7 and later, every JAR included in the transitive Class-Path can have its own embedded module repository; JarModuleFinder has been deprecated in favor of the more robust FileSystemClassPathModuleFinder.

6. Running JBoss Modules

In order to actually use a modular application, JBoss Modules has to be bootstrapped in some way. Most commonly, it will be launched directly from the command line, passing one or more command line arguments.

The command line structure depends on the type of modular application being executed. However, for all modes, the structure of the command line is like this:

Command line syntax
java [ <jvm option>... ] -jar jboss-modules.jar [ <option>... ] <program spec> [ args... ]

The syntax of the program spec is as follows:

Table 26. Supported execution modes
Switch Name Argument(s) Version Description

<none>

module

<module-name>

1.0+

Run the named module’s main-class.

<none>

module+class

<module-name>/<class-name>

1.7+

Run the named class in the named module.

-jar

JAR

<file-name>

1.0+

Run the given JAR file name as a module.

-cp
-classpath

class path

<path-spec> <class-name>

1.0+

Build a set of modules representing the class path, and run the given class name (implies -class, hence the extra argument). The set is delimited by the standard path separator for the platform.

-class

class

<class-name>

1.0+

Run the given class name, which must be found in JBoss Modules or the platform class path.

Note
Prior to JBoss Modules 1.7, the JAR and class path modes were implemented using a single module for the entire class path. Since 1.7, each item is now a separate module.

The supported options are as follows:

Table 27. Supported command line options
Option Argument Modes Version Description

-help

<none>

all

1.0+

Display a summary of supported command line options, and exit.

-version

<none>

all

1.0+

Display the version of JBoss Modules, and exit.

-mp
-modulepath

<path-spec>

all

1.0+

Specify the set of paths to scan in order to find modules in the filesystem repository. The set is delimited by the standard path separator for the platform.

-dep
-dependencies

<module-specs>

class, class path

1.0+

Specify a comma-delimited list of dependencies to add to the given class path modules.

-secmgr

<none>

all

1.2+

Indicate that the application should be loaded with the security manager active, discovering it from the target module, and falling back to the default security manager implementation.

-secmgrmodule

<module-name>

all

1.2+

Indicate that the application should be loaded with the security manager active, loading it from the given module.

-debuglog

<none>

all

1.0+

Enable trace logging to the console at startup.

Note
See the section JBoss Modules and the Java security manager for more information about running a JBoss Modules-based application within a security manager.

7. JBoss Modules and the Java security manager

JBoss Modules is designed to be able to easily run applications within a security manager. The primary benefit of the security manager is to prevent applications from being exploited for malicious purposes, by allowing each module to define a maximum permission set that it must adhere to. This makes it more difficult for an attacker to cause a program to perform actions which were not intentionally allowed.

Warning
Using the security manager is not guaranteed to provide safety when running untrusted code and should never be used for this purpose. Only the operating system can provide a sufficient level of safety in such a scenario. In particular, if the JAR module repository loader is used in this manner, the JAR should first be examined for embedded permissions.xml files as well as <permissions> stanzas within any embedded module descriptors.

7.1. Security manager basics

The mechanism by which this is accomplished is through the specification of a protection domain when module classes are defined. The protection domain is comprised of a code source and a set of permissions which are to be granted to the module. The code source in turn is assembled from a URL in combination with an array of code signers.

Protection domains can be static or dynamic. A static protection domain has a fixed set of permissions and can be evaluated quickly. A dynamic protection domain requires that each access be re-checked against the installed system java.security.Policy to determine what permissions apply to that access.

The protection domain which is assigned to each class in each module is determined by the corresponding module loader. All the module loaders which are included in JBoss Modules use the same general strategy: permissions are extracted from the module definition, and used to assemble a static protection domain whose code source is the location of the resource loader which loaded the class in question. Code signers are extracted from signed JARs in a standard way.

7.1.1. Permission checking

Permissions are most commonly checked by an application, library, or the JDK using a block of code like this:

Permission checking example
public class MyClass {
    // [ ... ]

    /**
     * Permission instance to use for our permission check.
     */
    static final Permission PERM_TO_CHECK = new RuntimePermission("getClassLoader");

    // [ ... ]

    /**
     * Perform an operation.
     *
     * @throws SecurityException if a security manager is installed and the
     *     caller does not have the {@code getClassLoader} {@link RuntimePermission}
     */
    public void performOperation() throws SecurityException {
        // we can only check if there is a security manager installed
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // here's the actual permission check; throws an exception on failure
            sm.checkPermission(PERM_TO_CHECK);
        }
        // now it's safe to perform the operation

        // [ ... ]
    }

    // [ ... ]
}

7.1.2. Access control context

The access control context is what determines the permission set that is applicable at the point where the permission is checked. It is comprised of a list of all the protection domains that are in effect for a given calling context, which in turn is derived from the combination of the current thread’s call stack and any inherited context that was set either when the thread was created or explicitly by way of a special method call.

The currently effective access control context can be captured by calling the static AccessController.getContext() method, which returns an instance of AccessControlContext which represents the access control context in effect when that method was called.

The effective permission set used for access checks on a given access control context is the intersection of all the permissions granted to each protection domain which is a part of the access control context. This means that calling a method can never grant additional permissions.

This can be a problem if a class must perform an action for which it is authorized but its caller is not. In such a situation, the class performing the action must use the doPrivileged method of the java.security.AccessController class.

7.1.3. AccessController.doPrivileged()

When a different set of permissions is required by a given program unit, the doPrivileged method must be called. This method can run an action which returns a value, or an action which returns a value and throws an exception, under a different access control context than the one that is effective when the method was called.

7.1.3.1. Running an action with full privileges

The most common form of doPrivileged usage is to run a given action with the full privileges of the class which calls the doPrivileged method. This is accomplished like this, building on the permission checking example above:

Basic doPrivileged example
public class MyClass {
    // [ ... ]

    /**
     * Permission instance to use for our permission check.
     */
    static final Permission PERM_TO_CHECK = new RuntimePermission("getClassLoader");

    // [ ... ]

    /**
     * Perform an operation.
     *
     * @throws SecurityException if a security manager is installed and the
     *     caller does not have the {@code getClassLoader} {@link RuntimePermission}
     */
    public void performOperation() throws SecurityException {
        // we can only check if there is a security manager installed
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // here's the actual permission check; throws an exception on failure
            sm.checkPermission(PERM_TO_CHECK);
        }
        // now it's safe to perform the operation
        // we need extra permissions to do our work!
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            Void run() {
                // perform the operation with full privileges
                // [ ... ]
                return null;
            }
        });
    }

    // [ ... ]
}

Note that we are only performing the doPrivileged call after we’ve checked another permission first.

Warning
It is very important that the doPrivileged method never be used in this way unless it can be shown that the only paths to that method call all introduce a permission check of their own which mediates access to that operation. This is necessary in order to avoid the possibility that such a call path would be able to circumvent one or more other access checks.
7.1.3.2. Running an action with restricted permissions

It is sometimes the case that a method must perform an action with some limited set of privileges. There exists a variation of doPrivileged which accepts a variable number of permissions which are to form the upper bound of permissions. Here is an example:

Restricted doPrivileged example
    // [ ... ]

    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        Void run() {
            // [ ... ]
            return null;
        }
    }, null, new FilePermission("/some/restricted/path/-", "read"));

    // [ ... ]

Note that the second parameter should be left null in this case.

7.1.3.3. Running an action with a saved access control context

Sometimes it is necessary for a method to perform an action using an access control context which was captured previously. For example, a class may capture the access control context during its constructor and use it for its operations, so that the privileges that were in effect when the object was constructed are granted to any users of that object’s operation.

This can be accomplished using one of the doPrivileged variants which accept an AccessControlContext as a parameter.

Warning
Anyone can construct an access control context with as many (or as few) restrictions as they like! Therefore it is very important that secure code must never accept an access control object, or directly use such an object that was accepted, from untrusted sources. It is generally best to only use contexts that you have captured yourself using AccessController.getContext() directly.

7.2. Security manager discovery

When starting with the -secmgr flag, JBoss Modules will attempt to discover a security manager implementation to use from the module of the main class. The java.util.ServiceLoader SPI mechanism is used after that module is loaded (but before it is run) to find and instantiate the security manager to use. If no security manager instance is found or could be instantiated, then the default system SecurityManager implementation is installed.

Note that the -secmgrmodule <module-name> flag works in a similar manner, except that the SPI mechanism searches the given module instead of the boot module.

7.3. Best practices

Giving a full analysis of security manager operation in Java is well beyond the scope of this guide. However, there are a few basic techniques that are worth iterating which will help avoid some common pitfalls.

Some security manager best practices
  • Always perform a permission check before running a publicly accessible method that employs one or more full-privileged or restricted-privileged doPrivileged call(s).

  • Never accept an access control context from untrusted code; always use AccessController.getContext() from an unprivileged context to acquire a caller’s context.

  • Consider using restricted-permission doPrivileged calls when possible.

  • Use constant permission objects whenever possible for permission checks and restricted-permission doPrivileged calls.

  • It is tempting to skip doPrivileged calls when a security manager is not installed; however, this can cause problems when a security manager is installed later in execution, which can happen in some container environments, particularly if such a call is expected to be long-running or if the body of the call may call an operation which captures the access control context for later use.

8. Working with the JBoss Modules API

Writing applications which take advantage of the capabilities of JBoss Modules require usage of the API. This section outlines the important constructs of the API and how to use them.

8.1. Modules and module class loaders

Every module is backed by a Module instance and a corresponding ModuleClassLoader instance. You can navigate from one to the other using the Module.getClassLoader() and ModuleClassLoader.getModule() methods. Note however that the Module.getClassLoader() method is governed by the getClassLoader RuntimePermission in security manager environments.

The ModuleClassLoader instance is what is used by the JDK to load and link classes for a given module. It can also be used to directly load resources and classes using the standard ClassLoader API.

The Module instance can be used to load exported classes, resources, and services from the module.

8.2. Module loaders and module finders

The ModuleLoader class is responsible for locating, loading, and linking modules. The loadModule() method can be used to find a module by name, returning its Module instance on success. In addition, the module loader for a given class or class loader may be found using the static forClass() and forClassLoader() utility methods.

A module loader may delegate the task of locating modules to one or more implementations of the ModuleFinder interface, which in turn must find the module and construct its specification.

8.2.1. The boot module loader

During initialization of JBoss Modules, the boot module loader is established. This is generally a module loader corresponding to a filesystem-backed module repository. The boot module loader is typically used to load from a base set of modules which is bundled with a modular application. It can be determined by calling the static Module.getBootModuleLoader() method.

8.2.2. The context class loader

The thread context class loader (also known as the TCCL) is used to identify the class loader of the application being run. When JBoss Modules starts up, the TCCL is initialized to the module containing the main class.

8.3. Implementing your own module loaders and finders

Plugin and deployment based systems which have a need to implement custom behavior must generally implement their own module loaders and module finders.

In most cases, custom module loading behavior can be achieved wholly by implementing ModuleFinder and using it with an instance of the base ModuleLoader class.

An example of a custom module finder
/**
 * This module loader loads module content from a plugin directory.  The
 * module name is the name of the JAR minus its extension (if any).
 */
public final class PluginModuleFinder implements ModuleFinder {
    private final Path basePath;

    public PluginModuleFinder(Path basePath) {
        if (basePath == null) throw new IllegalArgumentException("null basePath");
        this.basePath = basePath;
    }

    public ModuleSpec findModule(String name, ModuleLoader delegateLoader)
        throws ModuleLoadException
    {
        // 8< --- 8<
        // (construct and return the module specification)
        // 8< --- 8<
    }
}
Note
The ModuleFinder interface has two findModule methods, both of which are marked default. This is because earlier versions of the API used the ModuleIdentifier class to locate modules. New implementations of ModuleFinder should implement the findModule method which accepts a String name, and disregard the other (deprecated) method, which may be removed in a future release.

8.3.1. Finding and specifying a module

The custom module finder must do the work of locating the actual module, given its name. The module finder must construct a ModuleSpec for the module being built. This can be done by way of a ModuleSpec.Builder, instantiated by the ModuleSpec.builder() static method. The module specification builder has methods to set the module name, its contents, and its dependencies, and to construct the final immutable ModuleSpec instance.

In our simple plugin implementation, the module name is just the name of the JAR file without its .jar extension. We also want to take some precaution to prevent clever hackers from escaping our plugin path using an absolute path or .. path segments.

Finding and specifying the module
    public ModuleSpec findModule(String name, ModuleLoader delegateLoader)
        throws ModuleLoadException
    {
        // Make sure nobody escapes using a .. in the plugin name
        name = PathUtils.relativize(PathUtils.canonicalize(name));
        Path jarPath = basePath.resolve(name + ".jar");
        if (Files.exists(jarPath)) {
            ModuleSpec.Builder builder = ModuleSpec.build(name);
            // 8< --- 8<
            // (fill in the module specification)
            // 8< --- 8<
            ModuleSpec moduleSpec = builder.create();
            return moduleSpec;
        }
        return null;
    }
8.3.1.1. Resource specifications

Most (but not all) modules include some kind of content. This can be in the form of a JAR file, filesystem data, or simply some static in-memory content.

In our example we want to use the JAR file content for our plugin. In this example we will use the NIO.2 JAR filesystem API to provide the content.

Providing the module content
            // Add the module JAR content
            URI uri = URI.create("jar:" + jarPath.toUri());
            FileSystem fs;
            try {
                fs = FileSystems.newFileSystem(uri, Collections.emptyMap());
            } catch (IOException e) {
                throw new ModuleLoadException(e);
            }
            final Path rootPath = fs.getRootDirectories().iterator().next();
            builder.addResourceRoot(
                ResourceLoaderSpec.createResourceLoaderSpec(
                    ResourceLoaders.createPathResourceLoader("root", rootPath)
                )
            );
8.3.1.2. Dependency specifications

Modules cannot function without dependency specifications. The module’s own content as well as the content of dependencies will only be visible to the module through adding dependencies.

The order of dependencies is significant. Since the module’s own content must be added, its position in the order determines whether the module’s class loader will behave in a parent-first, a child-first, or a hybrid manner.

Including the module’s own content

The module’s own content can be included by adding a dependency using a specification acquired from the no-argument DependencySpec.createLocalDependencySpec() method. Let’s add it in to our example.

Providing the module content dependency
            // Add the module's own content
            builder.addDependency(DependencySpec.OWN_DEPENDENCY);
Depending on other modules

Adding a dependency on another module within the same module loader is done using the ModuleDependencySpecBuilder class in this way:

A simple module dependency specification
DependencySpec dep = new ModuleDependencySpecBuilder()
    .setName("the-dependency-name")
    .build();
Tip
In versions of JBoss Modules prior to 1.7, dependencies on other modules were created using the DependencySpec.createModuleDependencySpec() method.
Depending on modules from other module loaders

Adding a dependency on a module from another module loader is done by using the setModuleLoader method on ModuleDependencySpecBuilder. The dependency will be looked up from that module loader instead of the module’s own loader.

Tip
In versions of JBoss Modules prior to 1.7, dependencies on modules from other module loaders was achieved using a form of the DependencySpec.createModuleDependencySpec() method which includes a ModuleLoader typed parameter. In 1.7 and later, this method is deprecated.

This is useful when there is more than one way to declare a dependency, and each type must come from a different namespace. For example, file system JAR references might come from a FileSystemClassPathModuleFinder-based ModuleLoader, whereas dot.separated.names might come from a LocalModuleLoader.

This strategy can be used not only for adding dependencies on modules in different name spaces, but also to add a dependency on your own module, even if you do not know your module’s name or even the module loader that loaded it. Here’s an example:

Adding your own module as a dependency
            // Add my own module as a dependency
            final Module myModule = Module.forClass(getClass());
            builder.addDependency(
                new ModuleDependencySpecBuilder()
                    .setModuleLoader(myModule.getModuleLoader())
                    .setName(myModule.getName())
                    .build()
            );
Tip
The ModuleFinder interface’s findModule method accepts a ModuleLoader which represents the module loader for delegation. If this module loader is used in a dependency specification, it is the same as if the simple form of createModuleDependencySpec is used.
Note
Module loaders which implement "fall-through" or "layered" delegation, where some modules in a given namespace might be loaded from one module loader and others might be loaded from a "parent" layer, should not use this delegation strategy. Instead, the module loader delegation approach should be used.
Depending on content from the JDK

In some cases it is necessary to create a dependency to make system content visible. This can be done using a "local" dependency specification, for example:

DependencySpec spec = DependencySpec.createSystemDependencySpec(
    Collections.singleton("javax/smartcardio")
);

The given set should contain all the packages (in pathname form) that should be included in the dependency. No other paths will be visible to the module being built, unless they come from other dependencies.

Depending on content from non-module sources

In rare cases it is necessary to create a dependency on some other class loader or source. This is accomplished using the createLocalDependencySpec methods which accept a LocalLoader instance. These methods also require a set of path names which are to be delegated to the given dependency.

8.3.2. The complete example

The complete simple custom module loading example
package example.plugins;

import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;

import org.jboss.modules.DependencySpec;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleDependencySpecBuilder;
import org.jboss.modules.ModuleFinder;
import org.jboss.modules.ModuleLoader;
import org.jboss.modules.ModuleSpec;
import org.jboss.modules.ModuleLoadException;
import org.jboss.modules.PathUtils;
import org.jboss.modules.ResourceLoaderSpec;
import org.jboss.modules.ResourceLoaders;

/**
 * This module loader loads module content from a plugin directory.  The
 * module name is the name of the JAR minus its extension (if any).
 */
public final class PluginModuleFinder implements ModuleFinder {
    private final Path basePath;

    public PluginModuleFinder(Path basePath) {
        if (basePath == null) throw new IllegalArgumentException("null basePath");
        this.basePath = basePath;
    }

    public ModuleSpec findModule(String name, ModuleLoader delegateLoader)
        throws ModuleLoadException
    {
        // Make sure nobody escapes using a .. in the plugin name
        name = PathUtils.relativize(PathUtils.canonicalize(name));
        Path jarPath = basePath.resolve(name + ".jar");
        if (Files.exists(jarPath)) {
            ModuleSpec.Builder builder = ModuleSpec.build(name);
            // Add all JDK classes
            builder.addDependency(
                DependencySpec.createSystemDependencySpec(
                    PathUtils.getPathSet(null)
                )
            );
            // Add the module's own content
            builder.addDependency(DependencySpec.OWN_DEPENDENCY);
            // Add my own module as a dependency
            final Module myModule = Module.forClass(getClass());
            builder.addDependency(
                new ModuleDependencySpecBuilder()
                    .setModuleLoader(myModule.getModuleLoader())
                    .setName(myModule.getName())
                    .build()
            );
            // Add the module JAR content
            URI uri = URI.create("jar:" + jarPath.toUri());
            FileSystem fs;
            try {
                fs = FileSystems.newFileSystem(uri, Collections.emptyMap());
            } catch (IOException e) {
                throw new ModuleLoadException(e);
            }
            final Path rootPath = fs.getRootDirectories().iterator().next();
            builder.addResourceRoot(
                ResourceLoaderSpec.createResourceLoaderSpec(
                    ResourceLoaders.createPathResourceLoader("root", rootPath)
                )
            );
            ModuleSpec moduleSpec = builder.create();
            return moduleSpec;
        }
        return null;
    }
}

The example above is intended to be compiled into a JAR and launched either as a command-line -jar to jboss-modules.jar, or as a module itself.

8.3.3. Delegating to another module loader

The default behavior of the loadModule method of the ModuleLoader class is to search each of its ModuleFinder instances for modules of a given name. If the module is not found, then a ModuleNotFoundException is thrown.

In cases where (for example) your module loader is acting as a "layer" over another module loader, this behavior can be modified by overriding the preloadModule(String) method. The implementation must return a module, if one is found, or null if the module loader cannot find a module with the given name.

Note
Module loaders which must handle multiple name spaces should not use this delegation strategy. For example, the filesystem class path loader can load modules either by file name (/foo/bar/baz.jar) from the file system module loader, or by RDN (org.jboss.modules) from the local module loader. The name spaces do not overlap and dependencies for each are declared in separate ways. The best way to achieve this kind of delegation is to use the dependency-based approach instead.

To search for modules in the module loader’s own module finder set, the protected method loadModuleLocal should be used. This method returns null if the module finder set does not contain a module, and throws a ModuleLoadException if the module exists but failed to be loaded for some reason (including, but not limited to, a missing dependency).

To search other module loaders for a module, your module loader must use the protected static method preloadModule(String, ModuleLoader). The delegate module loader is passed in as the second parameter.

Since JBoss Modules 1.7, there is a DelegatingModuleLoader class which extends ModuleLoader and accepts a ModuleLoader and an array of ModuleFinder instances, and implements a the common use case of a child-first (delegate-last) loading policy, where the module finders are searched first before delegating to the module loader.

8.3.4. Module class loader names

Since JBoss Modules 1.6, module loaders can assign a name to the class loaders of the modules which they load. The name will appear in stack traces and, in some cases, can appear in log messages and other diagnostics when running under Java 9 or later.

By default, the name of the module class loader is set to the module name and version.

Note
Future versions of JBoss Modules may change the default, particularly as JBoss Modules introduces direct support for JPMS integration. The name of class loaders is purely diagnostic, and should not be relied upon for program flow control.

You can customize the class loader name used by your module loader by extending the ModuleLoader class and overriding its org.jboss.modules.ModuleLoader.getModuleDescription method. The name can be set to any arbitrary string.

8.3.5. Using the provided module loaders and finders

JBoss Modules includes several useful module loaders and finders which it uses for its standard operation. These loader implementations are available for reuse.

8.3.5.1. The local filesystem module loader

The local filesystem module loader is implemented in two parts. The bulk of the functionality exists in the LocalModuleFinder class. Instances can be constructed using the constructor(s) found on that class, which specify the file system paths to use as the module roots (or module path) and optionally a filter which may be applied to exclude certain paths from consideration as modules.

The module finder may be instantiated and used by itself in any module loader, or it may be used by way of the LocalModuleLoader class, which also has a close method which can be called to release any resources used by the module finder instance.

The boot module loader, which is accessible by way of the Module.getBootModuleLoader() method, is generally a LocalModuleLoader-based loader by default, unless it is explicitly overridden.

Tip
To reuse the modules from the module path, it is recommended to use the boot module loader rather than instantiating multiple module loaders over the same paths, in order to avoid extra memory usage and potentially hard-to-debug ClassCastException and similar occurrences.
8.3.5.2. The file system class path module loader

The filesystem (class path) module loader is used to load JAR content directly as modules, including support for JAR- or path-embedded module repositories.

This functionality is provided by the FileSystemClassPathModuleFinder class. The constructor for this class accepts Supplier instances which yield module loaders for "regular" module dependencies (like org.jboss.modules) and "extension" module dependencies (which are specified in the JAR file specification, but are not presently supported by any JBoss Modules module loader or module finder implementation). These module loaders are used as delegates for the corresponding dependencies. Class path dependencies are always resolved internally.

8.3.6. Custom ModuleClassLoader subclasses

Some special environments require that the class loader be a subtype of ModuleClassLoader. For example, OSGi class loaders must implement a specific interface.

The module class loader construction process can be intercepted by providing an implementation of ModuleClassLoaderFactory to the ModuleSpec.Builder. Implementations of this interface accept an opaque configuration object, which must be passed verbatim to the constructor of the ModuleClassLoader class by the subclass constructor’s super() call.

8.4. Resource loaders

The content of a module is determined by its resource loaders. Resource loaders are defined by implementing the ResourceLoader interface. A resource loader is responsible for providing resources, classes, and packages, and may also be used to locate native libraries.

Each resource loader has a root name, returned by the getRootName() method of ResourceLoader. The root name is used to identify a particular resource root when a module has more than one, and should be unique per module. It is permissible to return an empty string "" in the case where only one resource loader is present in a module.

The resource loader must also be able to produce its complete set of paths via the ResourceLoader.getPaths() method. These paths are always /-separated. This method is normally called only once per module that the resource loader is included in.

The resource loader may optionally implement the getLocation() method, which provides a URI to display for the output of management (JMX) operations. This is sometimes useful for diagnostic purposes.

8.4.1. Finding resources

The ResourceLoader.getResource(String) method finds resources by name. If the resource is not found, null is returned; otherwise, an instance of the Resource interface is returned.

The Resource interface in turn defines several operations.

The getName() method returns the resource name, and usually should reflect the exact name that was sent in to the ResourceLoader.getResource(String) method.

The getURL() method returns a java.net.URL instance which contains the location of the resource. This URL object may be used by application code to read the resource content, so it should either be an exact location, or its URLStreamHandler should be capable of providing the resource content.

The getSize() method should return the size of the resource, if it is possible to determine. If the size is indeterminate, for whatever reason, then 0 should be returned.

The openStream() method returns a non-null InputStream that efficiently yields the content of the resource. The caller of this method is responsible for closing the stream. The resource must be capable of opening the stream multiple times, and it must support multiple streams open at the same time from the same resource.

8.4.2. Finding classes

The ResourceLoader.getClassSpec(String) method finds classes by name. If the class is found then an object of type ClassSpec must be returned, otherwise null is returned.

The class bytes may be set on the class specification instance by calling setBytes() with a byte[], or by calling setByteBuffer() with a ByteBuffer. One of these two methods must be called for the class load operation to succeed.

Note
Support for ByteBuffer class bytes was added in JBoss Modules 1.7.

The class specification must also have a code source set on it. The setCodeSource() method may be called to set the code source.

8.4.3. Finding packages

The ResourceLoader.getPackageSpec(String) method finds information about a package. The implementation of this method must return a PackageSpec for known packages, or null if the package is not known.

The attributes of the PackageSpec class correspond with information that normally comes from the MANIFEST.MF of a JAR, including the legacy specification and implementation version information (if any), and the package seal base URL (if the package is sealed).

8.4.4. Finding native libraries

A resource loader has the ability to provide native library locations to the module. Native libraries found in this way are directly accessible to the module that includes the resource loader.

Tip
If your resource loader must support native libraries, consider extending the native resource loader.

To provide native library support for a module, implement the ResourceLoader.getLibrary(String) method. The implementation of this method should usually call System.mapLibraryName(String) to determine the correct native library name. The result of the method call should be the absolute path of the location of the library on the file system. In other words, the result of this method may not be a JAR path, an NIO.2 non-filesystem Path, etc.

The static NativeLibraryResourceLoader.getArchName() method may be used to acquire a string which may be used to help locate the correct platform native library.

Warning
It is not recommended for general purpose applications and libraries to use this mechanism to implement any kind of automatic native library extraction or downloads from JAR files or other sources. Restricted OS environments, such as SELinux, may forbid linkage to shared libraries that are written to disk by user applications.

8.4.5. Resource subloaders

As of JBoss Modules 1.7, a resource loader may optionally support the ability to construct a subloader. This capability may be used when (for example) a module loader supports more than one module in the same JAR archive.

To provide the ability to produce sub-loaders, override the ResourceLoader.createSubloader() method. This method accepts two arguments which specify the relative path and the name of the new resource loader. If a name or path is not valid, or the resource loader does not support subloaders, this method returns null; otherwise, it returns the new nested ResourceLoader instance.

8.4.6. Iterable resource loaders

As of JBoss Modules 1.2, a module loader may optionally implement the IterableResourceLoader interface. Iterable resource loaders have an additional method called iterateResources which returns an Iterator over the Resource instances of the loader.

The iterateResource method accepts two parameters: a start path (as a string), and a flag indicating that the iteration should (or should) not be recursive.

8.4.7. Using the provided resource loaders

Like module loaders, JBoss Modules includes several resource loader implementations that may be reused.

8.4.7.1. The Path resource loader

The NIO.2 Path construct is supported using the Path resource loader. The static ResourceLoaders.createPathResourceLoader() method may be invoked to create a new instance of the Path resource loader. This method accepts two parameters: the name of the resource root, and the Path that represents the base path of the resource loader’s content.

Tip
In JBoss Modules 1.4 and earlier, only file resource loaders were supported. The ResourceLoaders.createFileResourceLoader() method was used to create instances of this resource loader using a File instead of a Path. Since 1.5, the method is now implemented as a special case of the Path resource loader.
Tip
Since Java 7, the JDK has included an NIO.2-based JAR FileSystem implementation which may be used with this resource loader instead of using the JAR resource loader (see the plugin example). However, using the JAR FileSystem currently consumes more CPU and memory resources than JarFile does; in addition, the Path resource loader does not fully support JAR code signer verification. For cases where performance is more important than convenience, or where code signing support is required, it is recommended to use the JAR resource loader instead.
8.4.7.2. The JAR file resource loader

A special resource loader which specifically supports instances of JarFile is available. To create instances of this resource loader, call the createJarResourceLoader method of the ResourceLoaders class. The method accepts the resource root name and the JarFile instance, and optionally, a relative path within the JAR where the content root should be located.

8.4.7.3. The native resource loader

The NativeLibraryResourceLoader class may be used directly or extended in order to provide module content which includes native libraries on the file system. The constructor of this class accepts a single File argument that represents the root location of the loader’s content, which may include native libraries. The location is available from the getRoot() method of that class, and it is also used for the URI value returned by getLocation().

Native libraries are returned in the manner described by the Native Libraries section.


1. For an introduction to the Java language’s service provider interface mechanism, refer to: http://download.oracle.com/javase/tutorial/sound/SPI-intro.html