What is a Module Pack

A module pack is a package that contains one or more modules, that can be used as a template for creating new modules with the create-module command.

Module packs are built to support renaming of their components both at the ivy module level and the Sentinel component level (i.e. feature/mapper names). They also support Java package renamespacing. In simple cases support for this is automatic, however in specialised cases a special transformation plugin will be required to create the module pack.

Module Pack Structure

Module packs are simply zip files containing ordinary Sentinel SDK modules. The top level directory within a module pack may or may not contain an SDK module, and can include any number of nested sub-folders that themselves have SDK modules within.

These are some examples of what can be in a module pack:

Simple module pack containing one top-level module
 my-module-pack.zip
  ├─ src/
  │  └─ ...
  ├─ ivy.xml
  ├─ build.xml
  └─ module.properties
Module pack containing multiple modules
 my-module-pack.zip
  ├─ feature1
  │  ├─ src/
  │  │  └─ ...
  │  ├─ ivy.xml
  │  ├─ build.xml
  │  └─ module.properties
  └─ feature2
     ├─ src/
     │  └─ ...
     ├─ ivy.xml
     ├─ build.xml
     └─ module.properties
Module pack containing a top level module with sub-modules
 my-module-pack.zip
  ├─ src/
  │  └─ ...
  ├─ ivy.xml
  ├─ build.xml
  ├─ module.properties
  ├─ profile
  │  ├─ src/
  │  │  └─ ...
  │  ├─ ivy.xml
  │  ├─ build.xml
  │  └─ module.properties
  └─ library
     ├─ src/
     │  └─ ...
     ├─ ivy.xml
     ├─ build.xml
     └─ module.properties

Using Module Packs

Viewing Available Module Packs

A list of available module packs can be viewed in sdkadm by using the list-modules command, specifying the module-pack tag:

> list-modules +module-pack

This will show all of the currently indexed module packs.

Creating Modules from Module Packs

Creating new modules from module packs is done with the create-module command in the sdkadm, specifying a directory to place the new modules in, and the ID of the module pack to use:

> create-module new-module-dir opencloud#sentinel-sip-example#sentinel-sip/{majorversion};{majorversion}.{minorversion}

Alternatively you can specify a module pack file instead of a module ID:

> create-module new-module-dir /path/to/module-pack.zip

This is useful if you have module pack file that you want to use, but it is not indexed.

For more details on how to use the create-module command, see: Creating a new module.

Creating a Module Pack

The number of steps required to create a module pack increases with the complexity of the module pack. This section will outline how to set up a basic module pack, and then move on to cover additional steps required for various cases.

Special Requirements for Module Packs

Due to the way module packs are handled in sdkadm, there can be special requirements for any source code that is to be included in a module pack. These requirements are enforced when the module pack is built, if the module pack fails to meet them, then the build will fail. It is possible to disable to build-time enforcement of requirements by setting the following Ant property when building the module pack:

module.pack.verify.disabled=true

Currently there is only one enforced requirement, outlined below.

Wildcard Imports

Currently the only requirement on module pack source is that wild-card import statements be avoided (i.e. import statements ending with a *). This is necessary as wildcard imports creates ambiguities that can interfere with module pack renamespacing.

So avoid statements like this:

import com.mysdk.feature.*;

Instead, use explicit imports:

import com.mysdk.feature.FeatureClass;
import com.mysdk.feature.FeatureConfig;
import com.mysdk.feature.FeatureStats;

Publishing the Module Pack

In the most basic case, creating a module pack from a module requires only a few modifications to the original module in order to get it to publish a module pack file:

1. Add a new artifact to the publications in the module’s ivy.xml

The new artifact is required to have have type="module-pack", ext="zip", and conf="module-pack"; and conventionally should have name="${ivy.module}-module-pack".

So the new publication should look like this:
<artifact name="${ivy.module}-module-pack" type="module-pack" ext="zip" conf="module-pack"/>

2. Ensure that <default-package-module-pack/> is included in the build.xml for the module

It should be placed within the do-build target. It is often included in the build.xml for a module regardless of whether the module actually publishes a module pack, so it may already be present.

An example of a typical do-build target with module pack support:

<target name="do-build">
    <init-extensions/>
    <sentinel-annotation-processing/>

    <default-module-build/>
    <default-module-create-artifacts/>
    <default-package-module-pack/>
</target>

These two steps are enough to create a module pack with a module in the top level directory of the module pack. The resulting module pack will also contain any sub-modules within the top level module, however an additional step is required to allow dependencies between these modules to function correctly.

Dependencies Within Module Packs

Modules in a module pack need to be built to be portable across different SDKs. This means they require more rigorously defined module dependencies than normal. For this reason it is important to check over the dependencies defined in the ivy.xml file for each of the modules in the module pack. There are three categories of module dependencies that you might encounter:

  1. Dependencies on modules from outside your SDK.

  2. Dependencies on modules within your SDK that will not be included in the module-pack.

  3. Dependencies on modules within your SDK that will be included in the module-pack.

Dependencies on modules outside your SDK

This includes things such as third-party libraries and module from any other SDK or product that you might produce. Generally these dependencies won’t require any changes as they are always external to the SDK that the module that depends on them is in.

Dependencies on modules in your SDK but outside of the module pack

Normally when defining a dependency on a module that is in the same SDK as the module that depends on it, there are a few things that can be taken for granted. The first being that the module and its dependency will always be built and released together, so using latest.integration or latest.${ivy.status} for the dependency revision is reasonable. The second being that both modules are on the same branch so the branch name can be omitted from the dependency. Both of these assumptions can cause problems when a module pack is used to a create new module in a different SDK.

The best practice solution to this is to define properties in your SDK’s deps.properties file for each and every module that is depended on by another module. This allows the dependency to have different values for branch and revision depending on properties set at the SDK level.

So for example, say you have this dependency on another module in your SDK:

<dependencies>
    <dependency    org="my-org"    name="my-sdk-support"    rev="latest.integration"    conf="self -> api"/>
</dependencies>

You would go through the following steps:

1. Add/modify the branch property to the dependency using a new property as the value:

<dependencies>
    <dependency    org="my-org"    name="my-sdk-support"    rev="latest.integration"    branch="${my-sdk-support.ivy.branch}"    conf="self -> api"/>
</dependencies>

2. Modify the rev property for the dependency to use a new property as the value:

<dependencies>
    <dependency    org="my-org"    name="my-sdk-support"    rev="${my-sdk-support.ivy.revision}"    branch="${my-sdk-support.ivy.branch}"    conf="self -> api"/>
</dependencies>

3. Define values for the new properties in your SDK’s deps.properties file

It is a good idea to go a step further and define these per-module properties in terms of broader per-SDK properties (this makes if far simpler to change the branch and revision across a product if the need arises):

my-sdk.ivy.branch=${branch.name}
my-sdk.ivy.revision=latest.${ivy.status}

my-sdk-support.ivy.branch=${my-sdk.ivy.branch}
my-sdk-support.ivy.revision=${my-sdk.ivy.revision}
Tip Defining the properties as [module-name].ivy.branch and [module-name].ivy.revision has an added benefit in that if your product is correctly indexed then these properties will automatically be added to release.properties in your product’s published SDK.

Dependencies on modules in your SDK and in the module pack

If a module pack contains multiple modules, it is likely that at least some of those modules will depend on each other. In these cases it is necessary to modify the dependencies in the affected modules' Ivy files to ensure that new modules created from the module pack depend on each other, rather than the original modules that the module pack was created from.

So for example, say you had a module pack that contained a feature module and a profile module, and the feature module depended on the profile:

<dependencies>
    <dependency    org="my-org"    name="my-profile"    rev="latest.${ivy.status}"    conf="self -> api"/>
</dependencies>

You would go through the following steps:

1. Replace the value of org for the dependency with ${sdk.ivy.org}

<dependencies>
    <dependency    org="${sdk.ivy.org}"    name="my-profile"    rev="latest.${ivy.status}"    conf="self -> api"/>
</dependencies>

2. Add/modify the branch property for the dependency with the value ${branch.name}

<dependencies>
    <dependency    org="${sdk.ivy.org}"    name="my-profile"    rev="latest.${ivy.status}"    branch="${branch.name}"    conf="self -> api"/>
</dependencies>
Tip sdk.ivy.org and branch.name are by default set in the sdk.properties file at the root of an SDK.

Controlling Module Pack Contents

By default a module pack will contain the module that publishes it, and any sub-modules within that module’s directory tree. It is possible to tweak this with a series of properties that can be added to the module.properties file in the root directory of the module that publishes the pack.

The available properties are:

Property Description

module.pack.include.path

Comma separated list of files/directories to include in the module pack (in addition to the contents of the base directory.

module.pack.exclude.path

Comma separated list of files/directories to exclude from the module pack.

module.pack.basedir

Root directory of the module pack, all subdirectories of this directory will be included in the module pack unless otherwise excluded.

Tip All file/directory paths used in these properties should be made relative to the base directory of the module that publishes the module pack.

Custom Transformations

Sometimes a module pack may have an unusually complex structure or code that means that the default systems used to create new modules from a module pack cannot create working modules on their own. For this case, module packs support inclusion of transformers. Transformers are essentially custom pieces of java code that can be included in a module pack that will be automatically run when the module pack is used to create new modules. They are useful for doing things such as moving files and modifying source code and configuration files.

Transformers must implement the com.opencloud.modulepack.transformer.SimpleModulePackTransformer interface and source files must be placed inside a directory called module-pack-transformer in the top level directory of the module pack. The complied transformer code will be packaged in a jar file named transformer.jar in the top level of the module pack archive. The custom code will be invoked as part of the SDK’s create-module command. There is support for querying the user for additional configuration information.

SimpleModulePackTransformer Interface

Implementors of the SimpleModulePackTransformer require two methods: getConfigurationOptions which is used to query for configuration information from the user, and transform which does the actual transformation.

getConfigurationOptions Method

Full Signature
List<SimpleConfigurationOption> getConfigurationOptions(File modulePackDir)

This method is used by the create-module command to retrieve a list of configuration options that need to be queried from the user. The sole parameter modulePackDir is the directory that the modules are being created in, and corresponds to the top-level directory in the module-pack itself. An implementation of this method simple needs to return a list of com.opencloud.modulepack.transformer.SimpleConfigurationOption. One SimpleConfigurationOption object is required for each value that is needed from the user. They are created using this constructor:

public SimpleConfigurationOption(String configKey, String description, String defaultValue)

The parameters should be used as follows:

Parameter Description

configKey

Unique key to be used to access the configuration value from the configuration map.

description

Message to be used to prompt the user for the required value.

defaultValue

Default value to be used if the user enters nothing, or if the user chooses to use all default values.

transform Method

Full Signature
void transform(File modulePackDir, Map<String, String> configuration) throws TransformerException

This method is used to do the actual transformation steps required by the module pack. The details of the implementation will depend entirely on the particular requirements of the module pack. If there is a fatal error during the transformation, the method should throw a TransformerException.

The method takes two parameters:

Parameter Description

modulePackDir

The directory that the modules are being created in, and corresponds to the top-level directory in the module-pack itself.

configuration

A String to String Map of configuration keys and their associated values.

Testing a New Module Pack

After creating a module pack it is a good idea to test it in a clean SDK instance to ensure that it is genuinely self-contained. This can be done by starting sdkadm in the SDK and using the following command:

create-module my-module-pack-test path/to/my-module-pack.zip

It is a good idea to use non-default values for all of the configuration values that are prompted for (especially when it comes to renamespacing the package!) as this will reveal any issues cause by hardcoded values or that might require a custom transformation.

After creating the new module(s), change to the directory that contains them (my-module-pack-test in the example above) and run the following command on the command line (not in sdkadm):

ant publish-local-branch

If this builds successfully then your new module pack works!

Previous page Next page