Appendix C. Spring DM development with Ant and Ivy

published book

We consciously chose to use Maven 2 throughout this book, mainly because that’s the build tool we’re the most familiar with, but also because Spring DM uses it for its own building and testing. Nevertheless Ant remains a very popular build tool, so here we’ll provide enough information for you to get started with OSGi and Spring DM development using Ant.

The aim of this appendix is to reproduce part of what we did in chapter 3, but in an Ant and Ivy fashion: writing and packaging a Spring-powered bundle, then running the corresponding integration test. We won’t focus on pure Spring DM topics—you should already be familiar with the core principles of Spring DM before reading this appendix.

Why would you want to use Ant instead of Maven 2? There are many reasons, the first being that Ant is your favorite build tool! Generally speaking, Ant can address complex builds, because it lets the user handle very technical tasks, whereas Maven 2 has a more declarative approach.

We’ll start with the installation of Ant, and then dive into writing the bundle itself and compiling it with Ant. We’ll then cover how to create an OSGi bundle using Bnd, which integrates nicely with Ant. Apache Ivy will help us handle our dependencies, and we’ll see how to customize Spring DM’s testing framework to make it work with Ivy. We’ll finish by writing and running the integration test with Ant.

C.1. Installing Ant

Ant can be downloaded from its official website, http://ant.apache.org. It comes as an archive file that creates the directory apache-ant-1.8.1 when it’s extracted.

Note

We’re using Ant 1.8.1 here. For all Ant’s tasks to work properly, you should use a JDK (not a JRE). Java 1.5 or later is strongly recommended.

Next, you need to create an ANT_HOME environment variable that contains the directory where you installed Ant. To make the ant command available in your shell, add the ANT_HOME/bin directory to your PATH environment variable (use $ANT_HOME/bin if you’re using a Linux-based operating system or %ANT_HOME%\bin for Windows).

Note

You should also set the JAVA_HOME environment variable to the directory where your JDK is installed. The *_HOME variable should not contain quotes nor end with / or \.

You can check that Ant is correctly installed by running ant –version, which outputs information about the version of Ant:

Apache Ant version 1.8.1 compiled on April 30 2010

Now that we’re sure that Ant works, we’re ready to create a Spring DM bundle and the corresponding Ant build file.

C.2. Creating a Spring DM bundle with Ant

Creating our bundle will involve three steps:

1.  Creating the bundle’s files (a Java class and a Spring configuration file)

2.  Writing the Ant build file

3.  Using Bnd’s Ant task to package the project as an OSGi-compliant bundle

We’ll look at each step in turn.

C.2.1. Structure and content of the project

Listing C.1 shows the structure of the project, which uses the standard Maven 2 layout. The bundle’s Java class and Spring configuration file are displayed in bold.

Listing C.1. Spring DM bundle project structure

Listing C.2 shows the SpringDmSample class, which outputs a message on the console when an instance is created.

Listing C.2. The SpringDmSample class

The Spring configuration file is in the META-INF/spring directory, which Spring DM’s extender looks at when searching for an application context to bootstrap when the bundle is started. Our Spring application context will create an instance of SpringDm-Sample, which issues the message in listing C.2 on the console.

Listing C.3 shows the content of the Spring configuration file, springdm-sample.xml.

Listing C.3. The content of springdm-sample.xml

That’s it for the content of the project; let’s now see how to build it with Ant.

C.2.2. The Ant build file

An Ant build file is an XML file located at the root of the project and usually called build.xml. If you run the ant command in a directory that contains a build.xml file, Ant will use it by default, whereas you’ll have to provide the name of the file on the command line if it’s not called build.xml.

An Ant build file consists of a succession of targets that can run from compilation to packaging to the creation of test reports. Here we’re going to see how to compile our project and package it as a standard JAR.

First, we’ll create a build.xml file at the root of the project. Its content is shown in listing C.4.

Listing C.4. Ant build file for compiling and packaging as a standard JAR

The build file starts with the declaration of a set of properties . These properties are then used throughout the build file with the ${propertyName} syntax. The first target declared at is for cleaning the output directory. We then define the compilation target at , followed by the compilation itself and then the copying of resources to the build directory . The build file ends with the packaging of the project as a standard JAR at (note that this target depends on the compile target).

You can build a JAR archive by launching the following command at the root of the project:

ant package

This produces the JAR file in the target directory. But don’t we want to produce an OSGi-compliant bundle? The clean and compile targets are suitable for this, but we need to change the package target so that it uses a tool more appropriate to OSGi. In our case, we’re going to use Bnd.

C.2.3. Using Bnd with Ant to package the bundle

We introduced Bnd in chapter 3, where we used it inside a Maven 2 plug-in. We also used it as a command-line tool in chapter 6. The good news is that Bnd can also work as an Ant task!

We’ll look at how to make Bnd available to Ant builds and how to use the Bnd task inside a build file to produce an OSGi bundle.

Installing BND for Ant

Ant can support new tasks by adding the corresponding libraries to its classpath. There are several ways to do this, but the easiest is to copy the library into the ANT_HOME/lib directory.

You can download Bnd from its web page (http://www.aqute.biz/Code/Bnd). The Bnd JAR contains the Ant task, so you only have to copy it to the ANT_HOME/lib to make the Bnd task available for all your builds.

Let’s see now how to leverage this task.

Packaging the Bundle with the BND Task

Using the Bnd task in Ant consists of two steps: importing the task and then using it with the bnd XML element. That’s what listing C.5 illustrates.

Listing C.5. Using the Bnd Ant task to package an OSGi bundle

The package target is declared in the first line, the same way other targets are. But because the Bnd task isn’t an Ant core task, we need to define it using a properties file that the Bnd JAR contains.

We can then use the task with the bnd element. Bnd isn’t exactly a packaging tool: it scans the classpath (not a directory with class files) and gathers class files and resources into a JAR, following rules detailed in a configuration file (it also uses this file to generate the JAR manifest with the necessary OSGi metadata). That’s why we use the classpath attribute to tell Bnd to scan the output directory, and the files attribute to indicate a configuration file.

Note

Chapter 6 provides more details about Bnd’s features and how to write configuration files.

The next step is to write the build.bnd file that the files attribute is pointing at. This file contains instructions that Bnd uses to generate the JAR. Listing C.6 shows the file content.

Listing C.6. The Bnd configuration file (build.bnd)

Note that the Bnd configuration file can contain references to properties defined in the build file. We use this feature for defining the version of the bundle.

To package the bundle, launch the following command:

ant clean package

This produces a JAR in the same place as before, but this time it contains a manifest with the necessary OSGi metadata. You can try the bundle with the Equinox container we configured in chapter 3. Once the bundle is installed and started, the SpringDm-Sample should output a message to the console indicating that it has been created.

Testing the bundle manually—by installing it in an OSGi container—is cumbersome. That’s why we introduced you to Spring DM’s testing framework in chapter 3. The testing framework relies on Maven 2’s local repository to provision an embedded OSGi container that the integration test then bootstraps. This is the default behavior, but as we’re in an Ant appendix, we don’t have a local Maven 2 repository, so we need to override the default behavior. Fortunately, Spring DM provides enough hooks in its testing framework to do this—we’ll cover that in section C.4.

What we have to do immediately is retrieve the bundles we need for provisioning the OSGi container. For this we’ll use a popular dependency management tool that integrates nicely with Ant: Apache Ivy.

C.3. Provisioning with Apache Ivy

Apache Ivy (http://ant.apache.org/ivy/) is a powerful dependency manager that’s primarily used with Ant. What can Ivy help us with when developing a Spring DM bundle? It will download the bundles we need for our integration test. These bundles will then be used for compiling the project and provisioning the embedded OSGi container that the test framework launches.

We’ll see in the following sections how to set up Ivy for Ant, how to configure the repositories Ivy will use to retrieve dependencies, and how to declare the dependencies in a configuration file before retrieving them from Ant.

C.3.1. Installing Ivy for Ant

Ivy is launched through an Ant task, so we need to make this task available to our Ant installation. All we have to do is download the Ivy distribution and copy the Ivy JAR to ANT_HOME/lib, just as we did with Bnd. Once this is done, all Ivy’s tasks will be available to your Ant build.

We’ll only use the retrieve task here, but before we do this, we need to configure the repositories Ivy will use for retrieving the dependencies.

C.3.2. Configuring repositories for Ivy

Ivy can be configured with an ivysettings.xml file located in the same directory as the Ant build file. This file can contain many configuration options, but we’ll focus on resolvers.

In a nutshell, a resolver finds and downloads dependencies. We’ll use a chained resolver here, which successively tries several remote repositories to download dependencies. Listing C.7 shows the content of the ivysettings.xml file.

Listing C.7. Configuring repositories in the ivysettings.xml file

The default-chain resolver is set as the default resolver at and its definition starts at . The chain is made of three repositories that host only OSGi-compliant JAR files and the usual Maven 2 repository, IBiblio . Note that this repository will be queried only if none of the previous ones can return a dependency, because it’s the last element in the chain.

The next step consists of declaring the dependencies and modifying the Ant build files to make Ivy download them.

C.3.3. Retrieving Spring DM dependencies with Ivy

Ivy dependencies are declared in an ivy.xml file, located in the same directory as the Ant build file. Listing C.8 shows the structure of an Ivy dependency file.

Listing C.8. The structure of an Ivy dependency file

Note that with Ivy, a dependency is identified by its organization, its name, and its revision number. Ivy also has the notion of type of dependency, but we’ll only use JAR files here, which happens to be the default.

For brevity’s sake, we won’t include the whole set of dependencies needed for running our integration test; you can find the complete Ivy file in the code samples for this appendix. All you need to know is that these dependencies can be roughly divided into the following categories:

  • Spring DM (I/O, core, extender)
  • Spring Framework (core, lightweight container, AOP, and so on) and its dependencies
  • Logging libraries
  • Test libraries
  • OSGi (the Equinox container that the testing framework will bootstrap in the test)

Our project now has the following structure:

springdm-sample
src/
main/
java/
com/manning/springdmia/
SpringDmSample.java
resources/
META-INF/
spring/
springdm-sample.xml
build.bnd
build.xml
ivy.xml
ivysettings.xml

What we need to do next is to Ivy-enable our build file. Listing C.9 shows how to do so.

Listing C.9. Using Ivy in an Ant build file

Ivy comes with its own namespace that we declare in the root tag of the build file. This allows the use of the ivy:retrieve element in a target for retrieving dependencies.

Let’s run the resolve target to do so:

ant resolve

Here is part of the resulting console output:

Buildfile: build.xml

resolve:
[ivy:retrieve] :: Ivy 2.1.0 - 20090925235825 :: http://ant.apache.org/ivy/ ::
(...)
[ivy:retrieve] [SUCCESSFUL ]
org.eclipse.osgi#org.eclipse.osgi;3.5.1.R35x_v2009
0827!org.eclipse.osgi.jar (15141ms)
[ivy:retrieve] :: resolution report :: resolve 32266ms ::
artifacts dl 81828ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| default | 22 | 22 | 0 | 0 || 22 | 22 |
---------------------------------------------------------------------
[ivy:retrieve] :: retrieving :: com.manning.sdmia#springdm-sample
[ivy:retrieve] confs: [default]
[ivy:retrieve] 22 artifacts copied, 0 already retrieved (5181kB/234ms)

BUILD SUCCESSFUL

What did Ivy do? It downloaded the dependencies from the various remote repositories, copied them into a directory to cache them, and then copied them into a newly created lib directory in the project directory. The project now has the following structure (with the library directory and its content shown in bold):

springdm-sample
lib/
spring-osgi-io-2.0.0.M1.jar
spring-osgi-core-2.0.0.M1.jar
spring-osgi-extender-2.0.0.M1.jar
(...)
src/
main/
java/
com/manning/springdmia/
SpringDmSample.java
resources/
META-INF/
spring/
springdm-sample.xml
build.bnd
build.xml
ivy.xml
ivysettings.xml

We’re done with dependencies! It’s time to take advantage of these dependencies to write the integration test and run it using Spring DM’s testing framework.

C.4. Developing an integration test

As explained in chapter 3, Spring DM provides a test framework to run test cases in an embedded OSGi container. The test framework handles the bootstrapping of the OSGi container and turns the test into an on-the-fly bundle to run the test methods in an OSGi environment.

In section C.4.1 we’ll see how to write the test and how to compile it by modifying the Ant build file. As the Spring DM test framework uses the Maven 2 dependency management system by default, section C.4.2 shows how to customize it to make it work with our Ivy-managed dependencies. Section C.4.3 then covers how to launch the test with Ant.

C.4.1. Writing and compiling the integration test

We’ll stick to Maven 2’s project layout by putting tests in the src/test/java directory of our project. The project now has the following structure (with the test directory and its content shown in bold):

springdm-sample
lib/
(...)
src/
main/
java/
com/manning/springdmia/
SpringDmSample.java
resources/
META-INF/
spring/
springdm-sample.xml
test/
java/
com/manning/springdmia/
SpringDmSampleTest.java
build.bnd
build.xml
ivy.xml
ivysettings.xml

Listing C.10 shows the backbone of our integration test. The test mainly consists of checking that our bundle has been installed and correctly started.

Listing C.10. The bundle integration test

The test will be updated in section C.4.2 to reflect that we don’t rely on the Maven 2 local repository to provision the OSGi container that the test framework bootstraps. For now, we just need to compile the test. Listing C.11 shows the updates needed in the build file to compile the test.

Listing C.11. Compiling the test with Ant

Modifications start at , with the declaration of properties for the location of test source files and for the test output directory. We take advantage of our dependencies at , by defining a dedicated path for our classpath. Note that the test compilation target depends on the resolve target , which means that the dependencies will be retrieved before its execution. We refer at to the classpath we created, as the test uses the OSGi API and the Equinox JAR (in the dependencies) provides this.

Compilation time! Launch the following command:

ant compile-tests

The test is almost ready to run, but before we do that, we need to tune the Spring DM test framework a little to use the Ivy-managed dependencies.

C.4.2. Customizing Spring DM’s test framework to use Ivy’s dependencies

By default, the test framework uses the Maven 2 local repository to provision the embedded OSGi container that it then bootstraps. What does the test framework provision the container with? With Spring DM’s bundles (I/O, core, extender, and so on) and their corresponding dependencies (Spring Framework, logging, and the like). These bundles are referred to as framework bundles. It also provisions the container with the bundles that should be tested together with their own dependencies. Let’s call them application bundles.

The main issue we’re going to face is the way the test framework locates dependencies: we don’t want it to search the Maven 2 local repository, but rather the directory we copied the dependencies to. Fortunately, the test framework offers a hook to customize the way bundles are found: the ArtifactLocator. Here is the definition of the ArtifactLocator interface:

public interface ArtifactLocator {

Resource locateArtifact(String group, String id,
String version, String type);

Resource locateArtifact(String group, String id, String version);
}

With an ArtifactLocator, dependencies are identified by their group, ID, version, and type. Note the use of Spring’s resource abstraction.

We’ll provide a simple implementation that only uses the ID and the version to return a Resource from our lib directory. Listing C.12 shows the implementation of the LocalFileSystemIvyRepository.

Listing C.12. The ArtifactLocator for Ivy dependencies
Note

The LocalFileSystemIvyRepository is rather simple. Don’t hesitate to write an implementation that suits your needs better.

How do we use our LocalFileSystemIvyRepository in our integration test? This can be done by overriding the getLocator method, as shown in listing C.13.

Listing C.13. Using a custom ArtifactLocator in an integration test

Now, each time the testing framework needs a bundle, the request will be forwarded to our LocalFileSystemIvyRepository.

By default, Spring DM loads the necessary framework bundles and doesn’t load any application bundles. You can change this behavior by overriding the getTestFrameworkBundlesNames and getTestBundlesNames methods. You’re already familiar with getTestBundlesNames, because we overrode it to set the application bundles we wanted to install in the OSGi container in chapters 3 and 10. We also need to override the getTestFrameworkBundlesNames to make it consistent with our artifact locator, as shown in listing C.14.

Listing C.14. Customizing the set of framework bundles

We’re now done with the customization of the test framework: it uses our Ivy-managed dependencies instead of the Maven 2 local repository. We can’t say it’s been particularly easy, but Spring DM gives us enough hooks to achieve what we wanted!

Get ready for the last step: running the test with Ant.

C.4.3. Running the test with Ant

Ant has an optional task to launch JUnit tests, which is installed by default in the standard distribution. When it comes to running tests, Spring DM integration tests don’t differ from plain JUnit tests, as shown in listing C.15.

Listing C.15. Running the integration tests with Ant

The JUnit Ant task can generate reports—that’s why we define at a property for the directory where the reports should be generated. At we copy our bundle into the lib directory to make it available for provisioning (with our custom ArtifactLocator). The junit element at starts the JUnit task. It takes several options: formatters at (to set where the results of the tests should be printed–on the console and in XML files in our case), the classpath to use for launching the tests , and the batchtest element specifying the set of tests that should be launched .

We’re now ready to run the test with the following command:

ant clean test

The command will trigger the compilation of the SpringDmSample class, the bundle creation, the retrieval of dependencies, the test compilation, and the test launching. Here’s a part of the console output:

(...)
[junit] Running com.manning.sdmia.SpringDmSampleTest
[junit] Testsuite: com.manning.sdmia.SpringDmSampleTest
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 1,672 sec
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 1,672 sec
[junit] ------------- Standard Output ---------------
[junit] Spring DM sample created
[junit] ------------- ---------------- ---------------
[junit] Testcase: testIntegration took 1,609 sec

BUILD SUCCESSFUL

That’s it! We managed to run our Spring DM integration test with a 100 percent Ant solution!

C.5. Summary

Even though we favored Maven 2 throughout this book, we haven’t forgotten Ant users. Doing OSGi and Spring development with Ant isn’t only possible, it’s not too difficult at all.

The build file for the Spring DM sample is similar to that of any other project. Only the packaging task differs; for this we used Bnd to generate the OSGi metadata and create the bundle with the appropriate content. The biggest difference comes with integration tests, as Spring DM testing defaults to using the Maven 2 local repository for provisioning. Instead, we used Ivy to gather the necessary bundles and customize the test framework so that it used them from the project directory.

This gives you a shrink-wrapped solution for launching Spring DM-based integration tests but also enough knowledge to build your own solution. Also keep in mind that once the testing framework is correctly customized, running the integration tests is the same as for any other tests, because Spring DM tests are JUnit-based. Using Spring DM with Ant won’t force you to change your build habits, and most of your build files can remain the same.

sitemap
×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage