This topic contains a reference about the dependency management support in IBM® WebSphere® sMash. Dependency management allows a module to declare dependencies on other modules, and also ensures that the correct revisions of those dependencies are used.
A module represents an artifact, which can be packaged as a JAR file or a ZIP file. WebSphere sMash makes use of Apache Ivy for the dependency management feature. You declare the dependencies that your module needs in an XML file called an Ivy file. You can then use the WebSphere sMash command-line to resolve your module's dependencies. Resolve means searching a chain of repositories to find a module that matches the dependency you declared. The search will start with modules in the local repository that WebSphere sMash command-line creates on your system, and then searches remote repositories such as the WebSphere sMash package repository. When a match is found on a remote repository, the module is downloaded into the local repository. A resolve can not be successful until all required modules are found in the local repository.
A repository is simply a collection of artifacts stored in a structured format that can be searched using Ivy. Repositories are typically accessed through file system or HTTP interfaces. Repositories can contain different revisions of modules, so you can declare which specific revision you need.
When you first install WebSphere sMash, a local repository is created on your system to contain modules. All downloaded modules from remote repositories are placed into your local repository. The local repository is searched for matching dependencies first, and if the dependency can not be found locally, then the configured remote repositories are searched. If a match that satisfies the requested revision is found locally, then the search process stops and the module in the local repository is used. If a later revision of a module is available on a remote repository, then the module group commands should be used to update the local repository.
There is another local repository that can be used to find modules. This repository is a workspace repository and consists of peer modules. A peer is defined as a module that has a common parent directory. The workspace resolver can be used to resolve peer modules. The resolve command has more information about proving modules for the workspace resolver.
The WebSphere sMash command-line can also search remote repositories for modules if the module can't be found in the local repositories. Remote repositories are typically file servers with an HTTP interface. The remote repositories can be provided using either an Ivy format (ivy.xml), or a Maven format (a POM file). WebSphere sMash repositories use the Ivy format. Maven is a build utility that has similar dependency management capabilities to Ivy, but the artifacts are described using a POM file.
One of the features of dependency management is the ability for a package to declare transitive dependencies. When the dependency is included, the transitive dependencies are automatically resolved. There are times, however, where there is the need to have finer control over the artifacts that are included. One such case is when a conflict between two dependencies occurs. Another could just be the desire to exclude a certain jar. Ivy offers two solutions, one that could be viewed as fine-grained and another more coarse-grained. The ivy report, found in the reports directory of a module, is a great source to determine why artifacts are included, but the report is only generated in the case where there are no conflicts.
The fine-grained solution allows excluding specific transitive dependencies. This solution allows picking and choosing which artifacts to exclude. The excludes solution is the preferred solution for a WebSphere sMash module.
<dependency name="commons-beanutils" org="commons-beanutils" rev="1.7.0"> <exclude module="commons-logging"/> </dependency>
The coarse-grained solution is to exclude all transitives. This solution does require explicit dependency declarations to include the required transitives dependencies, but this solution allows complete control of which artifacts are included.
<dependency name="commons-lang" org="commons-lang" rev="2.3" transitive="false" />
The programming model in WebSphere sMash is to allow each module to declare its dependencies on other artifacts and modules. The dependency element in the ivy.xml file allows declaring the module and the revisions that can be used and contains the following attributes:
The sMash modules are published to an ivy repository; each module is published with a corresponding ivy.xml file. Modules can declare a dependency on both ivy modules and maven artifacts. Maven artifacts are also described by an XML file, but in the case of a maven artifact the file is a POM (Project Object Module) file. Ivy can resolve maven artifacts by parsing the POM file and generating an ivy.xml file. A dependency on a maven artifact can be added to an ivy file by determining the information in the POM file for the dependency element. The following shows the maven elements to use to create the ivy dependency element.
There are several different patterns to how the module revisions can be declared. The most straight forward is to declare the revision as an exact revision. Typically, this type of declaration is for a maven artifact where updates are not anticipated or wanted. The other pattern for revisions is dynamic revisions. The dynamic revision allows updating the module to use a newer revision as they become available using the zero update command.
The local repository needs to be updated with the new modules. The typical commands used are zero modulegroup update, which will retrieve the new modules from the configured remote repositories. Once the local repository has been updated, then resolve the module again with zero update.
The simplest use of revision is to specify the exact revision can be specified in the dependency line in the ivy.xml. An example of an exact revision would be:
<dependency name="derby" org="org.apache.derby" rev="10.3.2.1"/>
If you want to lock the module to a specific revision, and have no immediate need to update at a later time, then specify the exact revision. This will assure that even if a newer revision is available through the repositories, that the module will use a specific revision.
Dynamic revisions are more flexible in that as the new modules became available the module only needs to be resolved again using the zero update command. The revision strategy used in WebSphere sMash is latest, which will sort all revisions found, apply the dynamic revision pattern and return the latest revision that satisfies the dynamic revision.
There are two ways to specify dynamic revisions, a range and a start with. The WebSphere sMash revision declarations make use of the range. In most of the modules you will see the following:
<dependency name="zero.core" org="zero" rev="[1.0.0.0,2.0.0.0["/>
This range will resolve any revision of zero.core that is greater than or equal to 1.0.0.0, and less than 2. The notation uses [, ], (, ).
The second dynamic revision is a starts with pattern and a +. The match will try to find the latest revision that starts with the specified pattern. So for example, 1.0+ will match the latest revision that starts with revision 1.0. Dojo is an example of when you may want to use the starts with dynamic revision.
<dependency name="dojo" org="dojo" rev="1.1.+"/>
Assume that a module is developed is using Dojo revision 1.1 and also assume that Dojo 1.2 will introduce breaking changes. The module wants to declare a dependency on Dojo revision 1.1 and greater but not including 1.2. In this case the dynamic revision 1.1.+ could be used.
There are some other choices here. The dynamic range [1.1,1.2[ would also have worked. Also note that 1.1+ would have worked assuming that Dojo never releases a revision 1.10 and greater. So 1.1.+ is a safer choice to lock the dynamic revisions to a 1.1 revision.
When the developer decides that the module can be upgraded to Dojo 1.2 the the dynamic revision can then be
changed to 1.2.+
and the module re-resolved.
WebSphere sMash uses standard ZIP files for packaging. If you are going to package your module to share with others, you should update your ivy.xml file to include the following information about the package:
To create a WebSphere sMash package with the command-line interface, change to your module's directory and type zero package. A file with the naming pattern module-revision.zip will be created in the module's export directory.
If you want to make your package available as a dependency for other modules on your system, you can publish it to your local repository using zero publish. Once a package has been published to the local repository, other modules can declare it as a dependency. To verify that your package has been published, you can use the zero modulegroup list command to view the current module group.
The dependency management feature will resolve the dependencies for a module. The dependencies for a module are declared in the .zero/private/resolved.properties file and used for the virtual directories feature of WebSphere sMash. The resolve command will also create the classpath for a module. WebSphere sMash uses the system classloader for loading classes except for groovy classes and scripts, which are dynamically loaded by the GroovyClassLoader.
There are two directories that are added to the classpath under the module's home directory, the classes and lib directories. The classes directory is the target directory for the compiled java source files located in java. The lib directory is the location for adding third-party JAR libraries that cannot be resolved by the command-line zero resolve.
Ideally, you should try to find any libraries that you depend on in a repository, either a WebSphere sMash or a maven repository. This allows the sharing of modules through the repository and the resolve to pick up newer revisions as they become available. However, sometimes you will not be able to resolve the dependency from a remote repository. For example, you may need to use a licensed library that is not available in the repository. In this case, you can copy the JAR files into the lib directory under your module home directory. The resolve command is required to update the classpath in the resolved.properties file with the new JAR file.
Conventions are also used for determining the native libraries directories for the java.library.path property. You can add native libraries into an appropriate subdirectory under the lib directory that has the name of the target platform. For example, to add a native .so library for the operating system linux on an x86 architecture, you would copy the .so file into the /lib/x86/linux directory. The start command also uses the natives property in the resolved.properties file to set the java.library.path.
The operating system name is obtained from the system property os.name
. The file
osNameMappings.properties provides the default mappings for the os.name, similar to Ant's family.
So for example, Windows 95 and Windows XP are both mapped to windows. If a mapping for the os.name
value is not found, then the os.name value is returned.
The architecture type is a little more complicated. The base architecture type is obtained
from the system property os.arch
. The same type of mappings is provided through
osArchMappings.properties file. So for example i386, x86_64 and i686 are all mapped to x86
by default. The actual architecture type is determined from the base type and the bit size of
the JVM, not the machine. The system property sun.arch.data.model
is used to
determine the JVM bit size.
The architecture type for a JVM whose bit size is determined to be 32 would perform a look up of x86.32 in the properties file and get the value x86. The same lookup on a JVM with a bit size of 64 would lookup x86.64 which has a value of x86_64. If the bit size can not be determined then the base architecture type is used. If a mapping for the os.arch value is not found, then the os.arch value is returned.
The native library path is determined at resolve time, and a property natives is set in the .zero/private/resolved.properties file. The natives path is expected to exist under the lib directory of the module, lib/${architecture}/${os_name}. If the path exists then the natives property is set.