Maven PAR Plugin 1.0.0.M1

Ben Hale

Shortly after the introduction of SpringSource dm Server (Application Platform at the time), Thorsten Maus created a Maven plugin for creating PAR files. This was a great community contribution and we even used it in the Getting Started Guide for dm Server 1.0.x.

As dm Server's 2.0 milestones have progressed we've been adding new functionality to the PAR file. The most interesting feature is that now a PAR file can contain more than just JAR files. With the introduction of OSGi RFC 66, the dm Server has deprecated Web Modules in favor of the standard Web Container files using a .war extension.  Because of this, the PAR plugin needed to be updated to support these different types as dependencies.  Thorsten graciously agreed to donate his code to SpringSource and I'm pleased to announce the 1.0.0.M1 release of the Apache Licensed Maven PAR Plugin.

Usage

Use of the plugin has changed only slightly and can now default all of the information needed.

Repository

To get the plugin, you'll need to reference the SpringSource Enterprise Bundle Repository as a <pluginRepository/>

<pluginRepositories>
    <pluginRepository>
          <id>com.springsource.repository.bundles.milestone</id>
          <name>SpringSource Enterprise Bundle Repository - SpringSource Bundle Milestones</name>
          <url>http://repository.springsource.com/maven/bundles/milestone </url>
     </pluginRepository>
</pluginRepositories>

Build Plugin

Next you will need to set your packaging type to par and add the plugin to your build plugin list.

<build>
     <plugins>
          <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-par-plugin</artifactId>
               <version>1.0.0.M1</version>
          </plugin>
     </plugins>
</build>

By default all of the PAR manifest headers are populated using defaults from the POM but can be overridden with configuration.

Header POM Element
Application-SymbolicName ${project.artifactId}
Application-Name ${project.name}
Application-Description ${project.description}
Application-Version ${project.version}
<build>
     <plugins>
          <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-par-plugin</artifactId>
               <version>1.0.0.M1</version>
               <configuration>
                   <applicationSymbolicName>override.symbolic.name</applicationSymbolicName>
                   <applicationName>Override Name</applicationName>
                   <applicationDescription>Override Description</applicationDescription>
                   <applicationVersion>1000.0.0.override</applicationVersion>
               </configuration>
          </plugin>
     </plugins>
</build>

There is also a <fullyQualifiedName/> configuration tag that selects between ${groupId}.${artifactId}-${version}.${type} and ${artifactId}.${type} for the file names of the files contained within the PAR file. This value defaults to false.

PAR

Next, add your list of dependencies. The plugin packages all of the direct dependencies listed in the POM. It does not care about scopes or transitive dependencies.

<dependencies>
    <dependency>
        <groupId>com.springsource.dmserver</groupId>
        <artifactId>greenpages.app-solution</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>com.springsource.dmserver</groupId>
        <artifactId>greenpages.jpa-solution</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>com.springsource.dmserver</groupId>
        <artifactId>greenpages.db-solution</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>com.springsource.dmserver</groupId>
        <artifactId>greenpages.web-solution</artifactId>
        <version>${project.version}</version>
        <type>war</type>
    </dependency>
</dependencies>

Build

Run mvn package.

[INFO] [par:par]
[INFO] Assembling Artifacts for PAR 'solution/greenpages/target/greenpages-solution-1.0.0.SNAPSHOT.par'
[INFO]   Added 'greenpages.app-solution.jar'
[INFO]   Added 'greenpages.jpa-solution.jar'
[INFO]   Added 'greenpages.db-solution.jar'
[INFO]   Added 'greenpages.web-solution.war'

The Future

Please clone the source code and give me feedback in the comments and suggestions at the dm Server JIRA.

Installing STS into Eclipse 3.5

Christian Dupuis

Today marks a big day for Eclipse and for everybody involved with the Eclipse ecosystem: Congratulations to the Eclipse folks for releasing Eclipse 3.5 aka Galileo. You can read more about the release at http://www.eclipse.org/galileo/. I encourage everybody to download Eclipse 3.5 today and try it out.

Since SpringSource is strongly committed to Eclipse and we are building our SpringSource Tool Suite on top of the Eclipse technology stack, I'm excited to report that STS can be installed and used with 3.5. Here are some instructions to get started:

  1. Download and install the "Eclipse IDE for Java EE Developers" package
  2. Disable all pre-configured update sites on the "Install/Update -> Available Software Sites" page from the Eclipse preferences
  3. Import the linked update site bookmark file into your Eclipse. This will configure all the nightly snapshot update sites for STS you'll need. Use the "Import…" button on the "Install/Update -> Available Software Sites" page from the Eclipse preferences
  4. Install all features from the update site labeled "AJDT" and restart
  5. Install at least the Jira Connector feature from the update site labeled "Mylyn Extras" and restart
  6. Install all features from the update site labeled "Spring IDE Update Site" and restart
  7. Install all features from the update site labeled "SpringSource dm Server Tools Update Site" and restart
  8. Install all features from the update site labeled "SpringSource Tool Suite Update Site" and restart
  9. Enjoy STS in Eclipse 3.5

I hope that you find these steps helpful. If you encounter any installation problems following the instructions feel free to raise a JIRA.

We'll release STS 2.1.0.RC1 based on Eclipse 3.4 and 3.5 very soon. So stay tuned. Oh, before I forget: STS is free!

Update: Added another step to disable all pre-configured update sites before importing and installing. Also fixed a setting in the bookmark file.

OSGi Test Stubs 1.0.0.M1

Ben Hale

I'm pleased to announce the 1.0.0.M1 release of SpringSource's OSGi Test Stubs.  These stubs offer a way to unit test complex OSGi Framework interactions without needing a full OSGi container.

The Problem

As the dm Server team has been developing, we found that one of the biggest problem areas for testing for us was in BundleActivators.  Our BundleActivators do quite a bit of publishing services to the service registry as well as consuming services using ServiceTrackers.  These kinds of tasks involve many interwoven calls to BundleContexts, Bundles, ServiceRegistrations, and ServiceReferences.  In the beginning, these activators were simple enough that not much unit testing was done on them, and we depended on integration tests to catch any bugs that were introduced.  As time went on though, the activators became more complex and unit testing became a more pressing need.  We started using EasyMock for these tests, but found that they were very complex, hard to maintain, and most importantly hard to understand.

@Test
public void startAndStop() throws Exception {
    BundleActivator bundleActivator = new DumpBundleActivator();
    BundleContext context = createMock(BundleContext.class);
    Filter filter = createMock(Filter.class);

    String filterString = "(objectClass=" + DumpContributor.class.getName() + ")";

    expect(context.createFilter(filterString)).andReturn(filter);
    context.addServiceListener((ServiceListener)anyObject(), eq(filterString));
    expect(context.getServiceReferences(DumpContributor.class.getName(), null)).andReturn(new ServiceReference[0]).atLeastOnce();

    ServiceRegistration generatorRegistration = createMock(ServiceRegistration.class);
    ServiceRegistration summaryRegistration = createMock(ServiceRegistration.class);
    ServiceRegistration jmxRegistration = createMock(ServiceRegistration.class);
    ServiceRegistration threadRegistration = createMock(ServiceRegistration.class);
    ServiceRegistration heapRegistration = createMock(ServiceRegistration.class);

    expect(context.registerService(eq(DumpGenerator.class.getName()), isA(StandardDumpGenerator.class), (Dictionary<?,?>)isNull())).andReturn(generatorRegistration);
    expect(context.registerService(eq(DumpContributor.class.getName()), isA(SummaryDumpContributor.class), (Dictionary<?,?>)isNull())).andReturn(summaryRegistration);
    expect(context.registerService(eq(DumpContributor.class.getName()), isA(JmxDumpContributor.class), (Dictionary<?,?>)isNull())).andReturn(jmxRegistration);
    expect(context.registerService(eq(DumpContributor.class.getName()), isA(ThreadDumpContributor.class), (Dictionary<?,?>)isNull())).andReturn(threadRegistration);
    expect(context.registerService(eq(DumpContributor.class.getName()), isA(HeapDumpContributor.class), (Dictionary<?,?>)isNull())).andReturn(heapRegistration);

    generatorRegistration.unregister();
    summaryRegistration.unregister();
    jmxRegistration.unregister();
    threadRegistration.unregister();
    heapRegistration.unregister();

    context.removeServiceListener((ServiceListener)anyObject());

    replay(context, filter, generatorRegistration, summaryRegistration, jmxRegistration, threadRegistration, heapRegistration);

    bundleActivator.start(context);
    bundleActivator.stop(context);

    verify(context, filter, generatorRegistration, summaryRegistration, jmxRegistration, threadRegistration, heapRegistration);
}

The Solution

It quickly became clear that maintaining code that looked like this wasn't going to work in the long term. As many users know, Spring has long had an incredibly useful set of test stubs and it became clear that we needed something similar for OSGi.

Creating a set of test stubs is a delicate balancing act, especially when it comes to an API as complex as the OSGi Framework. On one hand, you need the implementation to be simple enough that you are not likely to introduce bugs and you can allow the user to specify the behavior and return values of the calls. On the other hand, you need a sophisticated enough implementation that complex objects (such as the ServiceTracker) can get expected behavior when they call the stub.

With all this in mind, I set off implementing test stubs for BundleContext, Bundle, ServiceReference, and ServiceRegistration. To get an idea of what kind of difference these test stubs make, here's the previous test after converting it to use the stubs.

@Test
public void startAndStop() throws Exception {
    BundleActivator bundleActivator = new DumpBundleActivator();
    StubBundleContext bundleContext = new StubBundleContext().addFilter(new ObjectClassFilter(DumpContributor.class));

    bundleActivator.start(bundleContext);
    assertServiceListenerCount(bundleContext, 1);
    assertServiceRegistrationCount(bundleContext, DumpGenerator.class, 1);
    assertServiceRegistrationCount(bundleContext, DumpContributor.class, 4);

    bundleActivator.stop(bundleContext);
    assertCleanState(bundleContext);
}

As you can see this test is now much simpler to read and maintain, but most importantly it's more understandable. The basic building block of this test is the StubBundleContext. This context is passed into the DumpBundleActivator's start call where services are registered. But it's the assertions where things really get interesting.

Using the StubBundleContext, it is possible for the user to assert everything they'd need to for testing. However, the test stubs package also includes an OSGiAssert class that makes typical assertions more readable. In this case, you can see that after calling start we want to have one ServiceListener registered, one DumpGenerator service registered, and four DumpContributor services registered. After calling stop we want to make sure that everything is cleaned up and the system is left in a clean state.

The Future

There are many more ways to manipulate the stub types as well as some more assertions for common test cases. I should warn that what is available now is by no means exhaustive. I'm always looking for user requirements on ways that these stubs can be improved and assertions that can be added. Please download the package or clone the source code and give me feedback in the comments and suggestions at the dm Server JIRA.

Latest comments across all posts

Recent Team Posts

Iwein Fuld

post Messaging meets OSGi at OSGi DevCon Europe

Yesterday I was speaking at OSGi DevCon Europe about using Spring Integration and dm Server to tackle common problems in large applications in a pragmatic way. Before and after my session I talked to various people that liked to have a little more information about these ideas. This blog will give an overview of what [...]


Rob Harrop

post Modular Web Applications with SpringSource Slices

I've talked in the past about providing support for truly modular applications, and I'm pleased to announce that you can now access the early prototype code of SpringSource Slices.
Building and Installing

You can access the source code from our Git repository:

git clone git://git.springsource.org/slices/slices.git

To build a packaged version of Slices simply run ant clean jar package [...]


Ben Hale

post dm Server Repository Content via JMX

In the dm Server 2.0.0.M1 release we added support for shared repositories. As a followup to this new functionality, we've added support for browsing those repositories via JMX.
Connecting to a running dm Server instance with a JMX client now shows you a Repository group. Under this group, each configured repository (with the exception [...]

Older Posts

Exploring Roo's Architecture

Deploying WARs to the OSGi Web Container is now even easier

Using Spring BlazeDS Integration 1.0

Cloning in dm Server 2.0 M3