This section explains Modules Packs — what they are and how to create them. |
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:
my-module-pack.zip ├─ src/ │ └─ ... ├─ ivy.xml ├─ build.xml └─ module.properties
my-module-pack.zip ├─ feature1 │ ├─ src/ │ │ └─ ... │ ├─ ivy.xml │ ├─ build.xml │ └─ module.properties └─ feature2 ├─ src/ │ └─ ... ├─ ivy.xml ├─ build.xml └─ module.properties
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 com.opencloud.rhino.sentinel-sip#sentinel-sip-example;{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:
-
Dependencies on modules from outside your SDK.
-
Dependencies on modules within your SDK that will not be included in the module-pack.
-
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}
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>
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 |
---|---|
|
Comma separated list of files/directories to include in the module pack (in addition to the contents of the base directory. |
|
Comma separated list of files/directories to exclude from the module pack. |
|
Root directory of the module pack, all subdirectories of this directory will be included in the module pack unless otherwise excluded. |
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
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 |
---|---|
|
Unique key to be used to access the configuration value from the configuration map. |
|
Message to be used to prompt the user for the required value. |
|
Default value to be used if the user enters nothing, or if the user chooses to use all default values. |
transform Method
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 |
---|---|
|
The directory that the modules are being created in, and corresponds to the top-level directory in the module-pack itself. |
|
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!