Auto-Provisioning OSGi Features: Automatic Versioning

Introduction

Welcome to the third and final blog-post in the “Auto-Provisioning Features” series. For previous posts:

Part One: The Basics
Part Two: Deployment and Installation

This post will cover the final step in auto-provisioning the features. Up until now we have required manual intervention to ensure that the “features.xml” reflected the most current release versions of each component. For some teams, this may be acceptable, as they may want fine control over which version goes out at any given time. Other teams, however, might prefer that the latest version simply go out without any intervention. That’s where this last post comes in.

What is our goal?

Say you’re developing a features that uses version 1.0.1 of our bundle, named “bundle-one”. Now say that your decision, as a team, is to use the latest version of the “bundle-one” bundle. Normally this is where a version range comes in really handy in Maven. Unfortunately, Maven doesn’t allow you to call the “releases” plugin with version-ranges in your dependencies.

So what do we do?

If you recall from our previous posts, we have a property in our pom.xml describing the current version:

    <properties>
        <bundle-one.version>1.0.1</bundle-one.version>

Since we can’t put a version range in this property (because of the releases plugin). What is our goal? For this number to update. Automatically. Without intervention.

versions-maven-plugin

Interestingly enough, there is a plugin in the wild that does exactly what we want. The “versions-maven-plugin” provides us with an “update-properties” goal which will search our POM for properties which represent versions, search all available repositories for newer versions, and update the properties for us automatically! Perfect.

The only problem is that you cannot call this plugin from within your POM. So why don’t we just call it as a target?

mvn clean versions:update-properties deploy

Actually, this almost works, but not quite. The problem is, the “versions” plugin updates the properties by rewriting the pom.xml file, but “deploy” runs using the already-loaded project properties. Since “deploy” does not reload the pom, it doesn’t see the fact that “bundle-one.version” now has a newer version, and deploys using the older version.

Now that’s still not necessarily a problem. Simply execute it in two commands:

mvn clean versions:update-properties
mvn deploy

Yet again I’ll say – if that’s good enough for you, then stop reading now. You’re done. Unfortunately, our continuous integration server didn’t let us split the Maven commands like this.

To stop wasting time, our final solution was to fork and modify the versions plugin. We submitted a bugfix with our patch, but it was rejected because it caused problems in some edge-cases we hadn’t considered.

I’ll start by walking you through the guts of the modification. For full details, see the source in the example zip.

First, we’ll provide a property in UpdatePropertiesMojo.java which turns on “live-update” of properties:

    /**
     * Whether properties should be saved
     *
     * @parameter expression="${liveUpdateProperties}" defaultValue="false"
     */
    private Boolean liveUpdateProperties;

    /**
     * Allows extensions of this class to flip the live-update bit
     * @param value true will cause properties to update live
     */
    protected void setLiveUpdate(boolean value)
    {
        liveUpdateProperties = new Boolean(value);
    }

Next, the update method has to conditionally live-update:

            else if ( PomHelper.setPropertyVersion( pom, version.getProfileId(), property.getName(),
                                                    winner.toString() ) )
            {
                getLog().info( "Updated ${" + property.getName() + "} from " + currentVersion + " to " + winner );

                if(liveUpdateProperties.booleanValue())
                {
                    getProject().getProperties().setProperty(property.getName(), winner.toString());
                }
            }

Lastly, we provide a convenience-class. The real one has all of the required annotations, but I’ll just provide Java code here. Again, the example zip provides the full details:

public class UpdatePropertiesLiveMojo
    extends UpdatePropertiesMojo
{
    protected void update( ModifiedPomXMLEventReader pom )
        throws MojoExecutionException, MojoFailureException, XMLStreamException
    {
        setLiveUpdate(true);
        super.update(pom);
    }
}

Using live-versions-plugin:update-properties-live

Now, assuming all of the right annotations are in place, and the plugin has been built the appropriate way, you should have a “live-versions-plugin” ready to go. All that’s left is the right pom.xml magic to use it, so let’s dive right back into the XML jungle and finish this up.

Step one is to set up a property. We’ll be doing some defaults and profile-overrides similar to the trick during the previous blog-post:

    <properties>
        <bundle-one.version>1.0.1</bundle-one.version>
        <deployFileUrl>${project.distributionManagement.snapshotRepository.url}</deployFileUrl>
        <allowSnapshots>true</allowSnapshots>
    </properties>

Step two, call the plugin! The “plugin” call goes inside the plugins group inside of build:

            <plugin>
                <groupId>com.codequirks.maven</groupId>
                <artifactId>live-versions-plugin</artifactId>
                <version>1.3</version>
                <configuration>
			<allowSnapshots>${allowSnapshots}</allowSnapshots>
		</configuration>
                <executions>
                    <execution>
                        <phase>initialize</phase>
                        <goals>
                            <goal>update-properties-live</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

As you can see, we’ve used the “allowSnapshots” property we set up earlier. This allows the plugin to see snapshot versions of our dependencies. This is extremely helpful for development because it means we can be developing “bundle-one” in a snapshot version, and the “features.xml” produced will still see it.

Of course, that’s a very bad thing if we want to release, so let’s override:

        <profile>
            <id>release-mode</id>
            <properties>
                <deployFileUrl>${project.distributionManagement.repository.url}</deployFileUrl>
                <allowSnapshots>false</allowSnapshots>
            </properties>
        </profile>

This will override the “allowSnapshots” to be false. This means that, during release, only release versions of “bundle-one” will be allowed. Spiffy.

Modified pom file?

If you’re following along with the example source, you’ll notice there’s still a bit of XML we haven’t discussed.

        <profile>
			<id>development-mode</id>
			<activation>
				<activeByDefault>true</activeByDefault>
			</activation>
			<build>
				<plugins>
					<plugin>
						<groupId>com.point2.maven.plugins</groupId>
						<artifactId>versions-maven-plugin</artifactId>
						<version>1.3</version>
						<configuration>
							<allowSnapshots>${allowSnapshots}</allowSnapshots>
						</configuration>
						<executions>
							<execution>
								<phase>initialize</phase>
								<goals>
									<goal>revert</goal>
								</goals>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>        

If you’re curious what this XML is for, try removing it and rerunning a build.

I mentioned this earlier, but the versions plugin actually modifies the pom.xml to write in the proper version numbers. This is both a good thing, and a bad thing, in our use-case.

During release, this is great. It updates the version number to the latest released version, and that change gets committed to your source control management. This keeps the pom.xml closer to “up to date” and reduces confusion.

During development, however, it’s a problem. I’ll demonstrate by example.

Let’s say you have bundle-one version “1.0.1” specified. Let’s also say that version “1.1.1” has been released, but someone is also working on “1.1.1-SNAPSHOT”. We will assume both “1.1.1” and “1.1.1-SNAPSHOT” are in your distribution management. What happens?

During release, the plugin will overwrite with “1.1.1” and commit. Not a problem.
During development, the plugin will overwrite with “1.1.1-SNAPSHOT”, which is newer.

Now what happens if you commit that change?

Well, version “1.1.1-SNAPSHOT” is newer than “1.1.1”. That means that when the plugin looks for newer versions than “1.1.1-SNAPSHOT”, it won’t find any. Your pom.xml will not be modified, and Maven will complain about snapshot versions in a release.

This is the reason for the last profile. When on a developer’s work-station, you absolutely cannot commit the changed pom to your source control, or it will cause serious problems. If we simply allow the modified file to exist, someone will eventually commit it by accident. Yes, I’m very pessimistic about that.

The “profile” calls the our versions plugin, and asks it to “revert” the changes it has made to our pom.xml. This ensures nobody ever accidentally commits the change.

Now, unfortunately that was a lot of information, and for the sake of brevity I didn’t go into as much detail as I should have. Feel free to ask questions, or dig through the example:
Versioning.zip (via 2shared.com)

Advertisements

2 comments on “Auto-Provisioning OSGi Features: Automatic Versioning

  1. Pingback: Auto-Provisioning OSGi Features: Automatic Versioning « Developing Code, Connecting Industry

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s