Contributing to the SpringSource Enterprise Bundle Repository

The SpringSource Enterprise Bundle Repository (EBR) houses the open source libraries Spring integrates with such as Hibernate, Log4j, and Freemarker. Spring links with these libraries to compile, and ships them in its "-with-dependencies" release distributions. All JARs housed in this repository are also OSGi bundles, enabling Spring to run in an OSGi environment as well as traditional Java SE and EE environments.

The Spring community is empowered to contribute additions to the Enterprise Bundle Repository. Contribute an addition when:

  1. A new version of a library Spring supports has been released, and you would like Spring to upgrade to that version
  2. You have identified an open source library the Spring community would find useful, and you would like Spring to support it

To foster community contributions to the EBR, the Spring team has created a process for the community to follow. This document describes the process.

Assess the fit of a library you wish to contribute

A candidate addition to the repository should either be an updated version of a library Spring already supports, such as Hibernate and Freemarker, or a new open-source library you would like Spring to support in the future.

Create a JIRA for your contribution

To contribute to the EBR, create a new issue at https://issuetracker.springsource.com/browse/EBR of type New Artifact. Include the following in your JIRA:

  1. For the Summary, enter the name and version of the library you wish to contribute; for example, "Apache DBCP 1.2"
  2. For the Description, provide:

    • A link to the project homepage
    • A link to the binary and source JARs
    • A link to the dependency information you referred to complete the bundle Manifest template. For example, you may have referred to the project's Maven POM. Or you may have referred to the project's website or documentation.
    • A link to the bundles already in the EBR this library will depend on. Find bundles by searching the repository. If a dependent bundle does not yet exist in the repository, link to the JIRA issue contributing the missing bundle.
  3. Finally, as an Attachment, add the Manifest template the Spring team should use to create, validate, and certify the OSGi bundle.

Assistance with several of these items is provided below.

Step 1: Locate binary and source JARs

The first step is to locate the binary and source JARs for the library you wish to contribute. There are several common places to look:

A source JAR can be more difficult to locate. Not all projects publish source JARs. In this case, if the raw source files can be located, download them directly and jar them up. Depending on the license for the project, a source JAR may be required for redistribution.

Step 2: Create the bundle Manifest template

A scaffolding tool has been provided to help you create a Manifest template when contributing a new bundle to the repository. A Manifest template is required to generate a OSGi bundle from the project's binary JAR. Follow the steps in this section to create a Manifest template using the tool.

First, download the tool.
Extract the archive, and optionally add the extracted bin directory to your system's path.
From the directory you'd prefer output to be generated in, run bundlor-shell.sh

This will launch an interactive Spring Roo shell with the Bundlor addon installed. Other addons typically shipped with Roo are not included with this distribution. The Bundlor addon will allow you to execute commands to quickly scaffold a new Manifest template.

After running bundlor-shell.sh, you should see:

~/scratch $ ./bundlor-shell.sh
    ____  ____  ____  
   / __ \/ __ \/ __ \ 
  / /_/ / / / / / / / 
 / _, _/ /_/ / /_/ /  
/_/ |_|\____/\____/   

Welcome to Spring ROO. For assistance press TAB or type "hint" then hit ENTER.
roo>

Next, execute the new bundle command to scaffold a Manifest template from the project's binary JAR. The command requires three arguments: the path to the binary JAR file, the symbolic name of the bundle, and the version of the bundle.

The following example illustrates execution of the new bundle command on version 3.0.0 of the Spring Framework Core module:

roo> new bundle -path /path/to/spring-core.jar -symbolicName org.springframework.core -version 3.0.0

The symbolicName must be unique and should typically be the base package of the module. For example, for the Spring Core JAR, "org.springframework.core" is the base package so it is used as the symbolicName.

The version must be an OSGi compatible version number. A OSGi version number consists of three numeric values, optionally followed by an alphanumeric value, all separated by "." s. While it may be common to refer to a JAR as having version "3.0", in an OSGi environment, the version number must be represented as "3.0.0" in order to satisfy the three numeric values. Any alpha character can be the optional fourth value; for example a "3.1.M1" becomes a "3.1.0.M1" OSGi version.

After executing the new bundle command, you should see the results of Roo scaffolding your Manifest template. For example:

Created ~/scratch/org.springframework.core/3.0.0
Created ~/scratch/org.springframework.core/3.0.0/org.springframework.core.jar
Managed ~/scratch/org.springframework.core/3.0.0/org.springframework.core.jar
Created ~/scratch/org.springframework.core/3.0.0/org.springframework.core.mf
Managed ~/scratch/org.springframework.core/3.0.0/org.springframework.core.mf
Created ~/scratch/org.springframework.core/3.0.0/notes.txt
Managed ~/scratch/org.springframework.core/3.0.0/notes.txt

Inside the current directory, three files are created:

  • {symbolicName}/{version}/{symbolicName}.jar - a copy of the binary jar named according to OSGi conventions
  • {symbolicName}/{version}/{symbolicName}.mf - the generated Manifest template
  • {symbolicName}/{version}/notes.txt - a log of Roo's activity

Step 3: Add version ranges

After executing the new bundle command, Bundlor generated you a Manifest template by analyzing the binary JAR and determining what packages it uses.

For spring-core, the generated template initially looks like this:

Import-Template: 
 edu.emory.mathcs.backport.java.util.concurrent;version="",
 javax.xml.transform;version="",
 org.apache.commons.attributes;version="",
 org.apache.commons.collections;version="",
 org.apache.commons.collections.map;version="",
 org.apache.commons.logging;version="",
 org.apache.log4j;version="",
 org.apache.log4j.xml;version="",
 org.aspectj.bridge;version="",
 org.aspectj.weaver;version="",
 org.aspectj.weaver.bcel;version="",
 org.aspectj.weaver.patterns;version="",
 org.w3c.dom;version="",
 org.xml.sax;version=""

The next step is to group package imports together where possible, then specify the correct version range for each remaining package import.

First, combine multiple package imports into a single import where possible. For example, in the Spring Core template above, both "org.apache.log4j" and "org.apache.log4j.xml" imports are part of the same bundle (Log4j) and will have the same version. Because of this, you can group them into one "org.apache.log4j.*" import as shown below:

Import-Template: 
 edu.emory.mathcs.backport.java.util.concurrent;version="",
 javax.xml.transform;version="",
 org.apache.commons.attributes;version="",
 org.apache.commons.collections.*;version="",
 org.apache.commons.logging;version="",
 org.apache.log4j.*;version="",
 org.aspectj.*;version="",
 org.w3c.dom;version="",
 org.xml.sax;version=""

Next, account for any system packages that should remain unversioned. For example, "org.xml.sax" is included in the standard JRE. Assign it and other JRE packages a version range of "0" as shown below:

Import-Template: 
 edu.emory.mathcs.backport.java.util.concurrent;version="",
 javax.xml.transform;version="0",
 org.apache.commons.attributes;version="",
 org.apache.commons.collections.*;version="",
 org.apache.commons.logging;version="",
 org.apache.log4j.*;version="",
 org.aspectj.*;version="",
 org.w3c.dom;version="0",
 org.xml.sax;version="0"

What's left? Well, from the remaning unversioned packages, you can determine this bundle has a dependency on:

  • Backport Util Concurrent
  • Apache Commons Attributes
  • Apache Commons Collections
  • Apache Commons Logging
  • Apache Log4j
  • AspectJ

It's time to put on your sleuth hat. You know the dependencies, you do not yet know the correct versions. Determining the appropriate version range requires a little digging. The best source of version information is an existing Maven POM or Ivy file. The Maven Central Repository is the largest source of dependency information, so check there first. Sometimes a Maven POM can be found inside the binary JAR in the META-INF directory, or in the project's SVN repository. Sometimes the project website and documentation is a good source of dependency information.

In general, the version specified in the POM or documentation forms the lower bound of the version range. The upper bound is often up to the next major version, but not including it. For example, the version of Commons Logging required by Spring Core is 1.1.1, so the version range for the org.apache.commons.logging package import becomes "[1.1.1, 2.0.0)".

To continue the example, A Maven POM exists for the Spring Core module, so lets fill in the version ranges from that:

Import-Template: 
 edu.emory.mathcs.backport.java.util.concurrent;version="[3.0.0, 4.0.0)",
 javax.xml.transform;version="0",
 org.apache.commons.attributes;version="[2.2.0, 3.0.0)",
 org.apache.commons.collections.*;version="[3.2.0, 4.0.0)",
 org.apache.commons.logging;version="[1.1.1, 2.0.0)",
 org.apache.log4j.*;version="[1.2.15, 2.0.0)",
 org.aspectj.*;version="[1.5.4, 2.0.0)",
 org.w3c.dom;version="0",
 org.xml.sax;version="0"

Any knowledge you collect about the required version ranges, including the source of the version information such as a POM, should be documented for future use in the notes.txt file created along side the template, and included in the JIRA description. It can be difficult to retrace old decisions without this information.

Step 4: Denote optional dependencies

It's common for a bundle to have both required and optional dependencies. Optional packages should be marked with ;resolution:=optional. It is difficult to know what packages are required vs optional without the source of this information. Typically the source used to determine the version ranges, such as a POM, also specifies if the package are required or optional. In the Spring Core example, all of the versioned packages are optional, except for Commons Logging, so we note that:

Import-Template: 
 edu.emory.mathcs.backport.java.util.concurrent;version="[3.0.0, 4.0.0)";resolution:=optional,
 javax.xml.transform;version="0",
 org.apache.commons.attributes;version="[2.2.0, 3.0.0)";resolution:=optional,
 org.apache.commons.collections.*;version="[3.2.0, 4.0.0)";resolution:=optional,
 org.apache.commons.logging;version="[1.1.1, 2.0.0)",
 org.apache.log4j.*;version="[1.2.15, 2.0.0)";resolution:=optional,
 org.aspectj.*;version="[1.5.4, 2.0.0)";resolution:=optional,
 org.w3c.dom;version="0",
 org.xml.sax;version="0"

All the metadata required to create the bundle from the Manifest template is now assembled.

Step 4: Test bundle creation

The last step is to actually build the bundle, to check the validity of your Manifest template. To do this, execute the run bundlor command. This command can be run at any time, with any binary JAR and Manifest template.

roo> run bundlor -jar /path/to/org.springframework.core.jar -template /path/to/org.springframework.core.mf -version 3.0.0
Created /path/to/target
Created /path/to/target/org.springframework.core-3.0.0.jar

Inspect the contents of the created bundle JAR and verify the accuracy of the generated OSGi Manifest entries in META-INF/Manifest.mf. Once you are confident your Manifest template is accurate, attach it to your JIRA issue when submitting your repository contribution. After submitting your JIRA, the Spring team will review your Manifest Template along with the other information you provided as part of the bundle certification process. Someone on the Spring team will notify you if any changes had to be made to the template during the certification process.

Step 5: Contribute any dependent bundles missing from the repository

In order for your new bundle to actually run, all of its dependencies also need to be available as bundles. Each dependency that is not already in the repository needs to be added before this bundle is considered complete. Go back to step 1 for every addition bundle needed. Sometimes this can lead to a deep chain of dependencies. The good news is that you only need to create each bundle once, and the process will be easier the next time.

If you are lucky, all the dependancies required for your bundle are already located in a repository. Once all dependencies (and their dependencies) are available as bundles move on to the next step.

Bundlor Shell Command Reference

New Bundle

new bundle

Scaffolding support for Bundlor manifest generation for an existing JAR. From the current directory, files are generated under {symbolicName}/{version}

attribute mandatory help
path true File path to the binary JAR
symbolicName true Bundle symbolic name
version true Bundle version number
displayName false Bundle display name
force false Allow overwriting of existing files

Add Source JAR

add source-jar

The source JAR for the active bundle. Can only run after "new bundle", the properties specified by that command are reused to make a binary JAR, source JAR pair. The path can directly specify a source JAR or a directory containing source files. If a directory is specified, the source JAR is automatically created from the files with hidden files and directories ignored.

attribute mandatory help
path true The path to the source JAR, or directory containing source files to be included
force false Force overwriting of existing files

Add License

add license

The license for the active bundle. Can only run after "new bundle", the properties specified by that command are reused to place the license file for that bundle.

attribute mandatory help
path true The path to the license file
force false Force overwriting of existing files

Run Bundlor

run bundlor

Run Bundlor for the given JAR and template manifest. The resulting bundle ./target/{symbolicName}-{version}.jar will be located in the same directory as the template manifest file.

attribute mandatory help
jar true File path to the binary JAR file
template true File path to the template manifest file
version true OSGi version for the bundle