Thursday, 28 February 2008

Wednesday, 27 February 2008

London Mayoral Candidates and the National Identity Register

In late January or early February, I canvassed the candidates of major parties for the May 2008 election of the London Mayor, asking them to come out publicly with a statement that they would not co-operate with any national identity register (ID Cards) scheme if elected. I wrote:

I would like to know where the [your party here] candidate in the 2008 London Mayoral election stands regarding the national identity register. Will (s)he resist pressure to withhold services from people who do not have an identity card? The government intends to make the "voluntary" scheme effectively compulsory by making it impossible to get a new passport, driving licence etc. without an ID card. If London refuses to play the game, it will be doing its citizens a great service.

The Green party candidate, Siân Berry, responded with alacrity, saying that she had already signed the No2ID pledge in 2004 and was well known for her stand against the scheme (see http://camden.greenparty.org.uk/news/newsidcards.html and other sites). She goes on:

"I would not allow any services for Londoners to depend on the scheme, and would urge Londoners to resist the ID system individually as well.

You may also be interested in our Census Alert campaign to stop Lockheed Martin being given the contract to run the UK Census in 2011. This is a campaign I set up last year after finding out they were on the final shortlist, and there are now politicians from a wide range of parties supporting the cause. See www.censusalert.org.uk for more details."


So that's a result then.

The Liberal Democrat party's candidate, Brian Paddick, sent me a personal letter by snail mail, which arrived today, in which he states that he is totally opposed to the ID cards. From a former police officer, this comes as a nice surprise (except to those who have been following his campaign closely) and is in line with the national party's line as represented by Nick Clegg (see http://www.youtube.com/user/LibDem). Nick Clegg has publicly pledged to refuse to be registered, even if it means being taken to court.

Sadly, Brian Paddick did not go so far as to pledge that London would refuse to participate in any coercion by the Government to force people to join the scheme.

I didn't get any response from either the Conservative or (New?) Labour candidates or their representatives.

However, Conservative candidate Boris Johnson is on record as opposing the scheme - see http://www.youtube.com/watch?v=kZAAzSzleWk.

Only Labour candidate Ken Livingstone is really letting the side down. According to an Andrew Marr interview mentioned on the NO2ID site, he actually supports the ID card scheme - see http://boardreader.com/t/Articles_Publications_150279/Andrew_Marr_Show_Ken_Livingstone_Intervi_19360.html.

I hope this helps you when it comes to making the decision in May!

Friday, 22 February 2008

Further background information about Maven PDE builds

I was asked in a comment to publish the settings.xml file for my Maven installation, because the correspondent was not able to build the example project successfully.

Our project makes use of a local Maven proxy server to mirror the combined contents of the Maven Central repository, various snapshot repositories and the project's own built artifacts. In the Maven conf directory, we have therefore installed the following settings.xml file, which reflects this arrangement:
<settings>
<servers>
<server>
<id>project-repo</id>
<username>...</username>
<password>...</password>
</server>
</servers>
<profiles>
<profile>
<id>main-profile</id>
<properties>
<!-- settings.localRepository is set by the USER settings.xml -->
<!-- Maven working directory -->
<maven.work.dir>${settings.localRepository}/..</maven.work.dir>
<!-- Clover working directory: clover license is expected in ${clover.work.dir}/license -->
<clover.work.dir>${maven.work.dir}/clover-work</clover.work.dir>
<!-- Additional plugin provider path for Eclipse target platform -->
<plugin.target.dir>${maven.work.dir}/plugin_target_dir</plugin.target.dir>
<!-- project sites will be deployed to ${site.dir} with mvn site:deploy -->
<site.dir>${maven.work.dir}/maven_site</site.dir>
</properties>
<repositories>
<repository>
<id>central</id>
<name>Internal Mirror of Central Repositories</name>
<url>http://....:9999/repository/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Internal Mirror of Central Plugins Repository</name>
<url>http://....:9999/repository/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>main-profile</activeProfile>
</activeProfiles>
</settings>

In the .m2 directory under the user's home directory, we install a minimal settings.xml, which simply indicates where to find the local Maven repo on your hard disk. However, I have customised my copy so that I can do builds when not connected to the company network but with access to the Internet. This shows you where you can obtain things like the PDE Maven plugin snapshot.
<settings>
<localRepository>C:\data\...\maven_repo</localRepository>
<profiles>
<profile>
<id>publicRepos</id>
<activation>
<property>
<name>searchPublicRepos</name>
<value>true</value>
</property>
</activation>
<repositories>
<repository>
<id>codehausSnapshots</id>
<name>Codehaus Snapshots</name>
<releases>
<enabled>false</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
<url>http://snapshots.maven.codehaus.org/maven2</url>
<layout>default</layout>
</repository>
<repository>
<id>mavenCentral</id>
<name>Maven Central</name>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
<url>http://repo1.maven.org/maven2/</url>
<layout>default</layout>
</repository>
<repository>
<id>mavenSnapshots</id>
<name>Maven Snapshots</name>
<releases>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
<url>http://people.apache.org/repo/m2-snapshot-repository/</url>
<layout>default</layout>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>codehausSnapshots</id>
<name>Codehaus Snapshots</name>
<releases>
<enabled>false</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
<url>http://snapshots.maven.codehaus.org/maven2</url>
<layout>default</layout>
</pluginRepository>
<pluginRepository>
<id>mavenCentral</id>
<name>Maven Central</name>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
<url>http://repo1.maven.org/maven2/</url>
<layout>default</layout>
</pluginRepository>
<pluginRepository>
<id>mavenSnapshots</id>
<name>Maven Snapshots</name>
<releases>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
<url>http://people.apache.org/repo/m2-snapshot-repository/</url>
<layout>default</layout>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>

As I mentioned in passing in my original post, you also have to make sure that you (a) correct the version number of two dependencies in the pde-maven-plugin's POM, and (b) upload all the Eclipse plugins that the pde-maven-plugin needs to your local repository or to your project repository, using the eclipse:to-maven goal.

I have actually set up a Maven project to do upload Eclipse plugins repeatably and reliably in case we need to clean up and rebuild the project repository (e.g. after an upgrade of Eclipse). Here's the POM:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>....</groupId>
<artifactId>devclipse-to-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Eclipse plugins to Maven Artifacts converter</name>
<url>....</url>
<description>....</description>

<!-- ******************************************************************
Inherit from project grandparent pom
****************************************************************** -->
<parent>
<groupId>....</groupId>
<artifactId>configuration</artifactId>
<version>1.8</version>
</parent>

<!-- ******************************************************************
Environment Information
****************************************************************** -->
<scm>
<connection>
scm:svn:http://..../devclipse_to_maven/
</connection>
<developerConnection>
scm:svn:http://..../devclipse_to_maven/
</developerConnection>
<tag>HEAD</tag>
<url>
http://..../devclipse_to_maven/
</url>
</scm>

<!-- ******************************************************************
Build configuration
****************************************************************** -->
<properties>
<eclipse.source.dir>${env.M2_HOME}/../eclipse</eclipse.source.dir>
<eclipse.ext.dir>${env.M2_HOME}/../ext/eclipse</eclipse.ext.dir>
<eclipse.usr.dir>${env.M2_HOME}/../usr/eclipse</eclipse.usr.dir>
<eclipse.target.dir>${project.build.directory}/eclipse</eclipse.target.dir>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>copy-plugins-and-features</id>
<phase>process-resources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks description="Grab the features and plugins from
the Eclipse SDK that have been found empirically to
be required as part of the target platform. We will
upload them to the Maven repository in the next phase.">
<mkdir dir="${eclipse.target.dir}/plugins"/>
<copy todir="${eclipse.target.dir}/plugins">
<fileset dir="${eclipse.source.dir}/plugins">
<include name="com.ibm.icu_*.jar"/>
<include name="org.eclipse.ant.core_*.jar"/>
<include name="org.eclipse.compare_*.jar"/>
<include name="org.eclipse.core.commands_*.jar"/>
<include name="org.eclipse.core.contenttype_*.jar"/>
<include name="org.eclipse.core.databinding_*.jar"/>
<include name="org.eclipse.core.expressions_*.jar"/>
<include name="org.eclipse.core.filebuffers_*.jar"/>
<include name="org.eclipse.core.filesystem_*.jar"/>
<include name="org.eclipse.core.jobs_*.jar"/>
<include name="org.eclipse.core.net_*.jar"/>
<include name="org.eclipse.core.resources_*.jar"/>
<include name="org.eclipse.core.runtime.compatibility.auth_*.jar"/>
<include name="org.eclipse.core.runtime_*.jar"/>
<include name="org.eclipse.core.variables_*.jar"/>
<include name="org.eclipse.debug.core_*.jar"/>
<include name="org.eclipse.equinox.app_*.jar"/>
<include name="org.eclipse.equinox.common_*.jar"/>
<include name="org.eclipse.equinox.preferences_*.jar"/>
<include name="org.eclipse.equinox.registry_*.jar"/>
<include name="org.eclipse.help_*.jar"/>
<include name="org.eclipse.jdt.core_*.jar"/>
<include name="org.eclipse.jdt.debug_*/**/*"/>
<include name="org.eclipse.jdt.launching_*.jar"/>
<include name="org.eclipse.jface.databinding_*.jar"/>
<include name="org.eclipse.jface.text_*.jar"/>
<include name="org.eclipse.jface_*.jar"/>
<include name="org.eclipse.osgi_*.jar"/>
<include name="org.eclipse.rcp.source.win32.win32.x86_*/**/*"/>
<include name="org.eclipse.swt.win32.win32.x86_*.jar"/>
<include name="org.eclipse.swt_*.jar"/>
<include name="org.eclipse.team.core_*.jar"/>
<include name="org.eclipse.team.ui_*.jar"/>
<include name="org.eclipse.text_*.jar"/>
<include name="org.eclipse.ui_*.jar"/>
<include name="org.eclipse.ui.console_*.jar"/>
<include name="org.eclipse.ui.editors_*.jar"/>
<include name="org.eclipse.ui.forms_*.jar"/>
<include name="org.eclipse.ui.ide_*.jar"/>
<include name="org.eclipse.ui.navigator_*.jar"/>
<include name="org.eclipse.ui.navigator.resources_*.jar"/>
<include name="org.eclipse.ui.views_*.jar"/>
<include name="org.eclipse.ui.views.properties.tabbed_*.jar"/>
<include name="org.eclipse.ui.workbench_*.jar"/>
<include name="org.eclipse.ui.workbench.texteditor_*.jar"/>
<include name="org.eclipse.update.configurator_*.jar"/>
<include name="org.eclipse.update.core_*.jar"/>
<include name="org.eclipse.update.ui_*.jar"/>
</fileset>
</copy>
<mkdir dir="${eclipse.target.dir}/features"/>
<copy todir="${eclipse.target.dir}/features">
<fileset dir="${eclipse.source.dir}/features">
<include name="none_*"/>
</fileset>
</copy>
<copy todir="${eclipse.target.dir}/plugins"
failonerror="false">
<fileset dir="${eclipse.ext.dir}/plugins">
<include name="org.polarion.team.svn.client.javahl.win32_*/**/*"/>
<include name="org.polarion.team.svn*.jar"/>
<include name="org.sf.easyexplore_*.jar"/>
</fileset>
</copy>
<copy todir="${eclipse.target.dir}/features"
failonerror="false">
<fileset dir="${eclipse.ext.dir}/features">
<include name="none_*"/>
</fileset>
</copy>
<copy todir="${eclipse.target.dir}/plugins"
failonerror="false">
<fileset dir="${eclipse.usr.dir}/plugins">
<include name="none_*"/>
</fileset>
</copy>
<copy todir="${eclipse.usr.dir}/features"
failonerror="false">
<fileset dir="${eclipse.ext.dir}/features">
<include name="none_*"/>
</fileset>
</copy>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>installation</id>
<phase>install</phase>
<configuration>
<eclipseDir>${eclipse.target.dir}</eclipseDir>
</configuration>
<goals>
<goal>to-maven</goal>
</goals>
</execution>
<execution>
<id>deployment</id>
<phase>deploy</phase>
<configuration>
<eclipseDir>${eclipse.target.dir}</eclipseDir>
<deployTo>${distributionManagement.repository.id}::default::${distributionManagement.repository.url}</deployTo>
</configuration>
<goals>
<goal>to-maven</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<!-- ******************************************************************
Project Information
****************************************************************** -->
<organization>
<name>....</name>
<url>http://..../</url>
</organization>

<developers>
<developer>
<id>ihu</id>
<name>Immo H&uuml;neke</name>
<email>ihu@zuhlke.com</email>
<url>http://aspsp.blogspot.com/</url>
<organization>Z&uuml;hlke Engineering</organization>
<organizationUrl>http://www.zuhlke.com/</organizationUrl>
<roles>
<role>architect</role>
<role>developer</role>
</roles>
<timezone>0</timezone>
<properties>
<picUrl>http://www.spaconference.org/cgi-bin/wiki.pl/?mugimmohuneke.jpg</picUrl>
</properties>
</developer>
</developers>

<inceptionYear>2008</inceptionYear>

</project>

Hope this helps. If you still have problems, please e-mail me your Maven output - I may be able to guess where it is going wrong.

Saturday, 16 February 2008

A Maven Eclipse RCP Product build at last!

My sincere thanks to René Gröschke and Patrick Paulin for putting me on the right track. These notes were developed using Eclipse RCP Developer (version 3.3.1.1 "Europa").

Below, I have documented the required steps in some detail, based largely on René's tutorial. Some gotchas to watch out for include:

  • Contrary to popular belief, it is not necessary to install the Maven 2 Integration feature in Eclipse - in fact we found it confusing and removed it again, preferring to invoke various Maven goals through External Tool configurations under the Run menu
  • The build.properties file is very literal in its interpretation of properties you specify. In particular, when specifying a pathname, you must use forward slashes even under Windows, you must not have spaces in the pathname and you cannot enclose it in quotation marks. And most particularly, you cannot use symbolic references to any other property or environment variable (although the template supplied with the Eclipse PDE build plug-in misleads you here by suggesting that you can use ${user.home} as part of the buildDirectory property) - otherwise you will see the dreaded error message: Content Provider not set for Site: "{0}".
  • You must specify a target platform in the base location that has had the Eclipse RCP Delta Pack installed, otherwise you may be able to build using PDE but you won't generate an executable.

It is recommended to download both the RCP Runtime for your expected target platform architecture(s) and the RCP Delta Pack. Install each target platform RCP runtime into its own location - e.g. if you have installed Eclipse in C:\tools\eclipse, I would suggest unzipping the RCP Runtime for 32-bit Windows on Intel x86 processors to C:\tools\eclipse\win32.win32.x86\eclipse. Unzip the delta pack into each of the targets. Let it overwrite all clashing filenames. Afterwards, if you are short of space, you can go through each Eclipse target platform and safely delete any plugins or features that don't correspond to the selected target architecture.

Having installed the target platform(s), it is probably a good idea to set the default Eclipse workspace target platform to the one for the type of workstation you are running on - e.g. if you're using a 32-bit Windows / Intel x86 computer, select Window -> Preferences -> Plugin Development -> Target Platform and browse to C:\tools\eclipse\win32.win32.x86\eclipse (vary this path depending on the location of your Eclipse target platform). This ensures that native Eclipse builds and product exports will correspond as closely as possible to those built by the PDE in "headless" mode.

Another thing you need to do is to doctor the POM of the pde-maven-plugin (snapshot) so that it doesn't depend on outdated versions of org.eclipse.pde.core and org.apache.maven.shared.maven-osgi. Here is one way to do it:

    <dependency>
<groupId>org.eclipse.pde</groupId>
<artifactId>core</artifactId>
<version>[3.3.0,4.0.0)</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-osgi</artifactId>
<version>[0.1.9,)</version>
</dependency>

Make sure you catch all copies of the POM. You can do this in your local Maven repository after a first attempt to build, or more permanently on your project repository. You can use the eclipse:to-maven goal to get Eclipse plugins from the target platform folder into your local and/or project repository.

Let's work through a complete though simple example of creating an Eclipse RCP application that can be built from the command line using Maven. Broadly, this has the following stages:

  1. Create the RCP application
  2. Create a product configuration file
  3. Create a build.properties file
  4. Make a Maven POM
  5. Run it and test the result

Steps A-C are identical to what you would do if you wanted to create a project capable of supporting headless PDE builds. The only concessions to Maven are the layout of the source and output directories and the fact that we do not specify an eclipseLocation property in the build.properties file - this is instead configured in the Maven POM as the eclipseInstall parameter.

Now, are you sitting comfortably in front of your Eclipse IDE? Then follow the bouncing ball...

A. Create the RCP application

  1. Start a new project: File -> New -> Project -> Plug-In Project
  2. Set project name = "com.breskeby.tutorial.rcp.withMaven"
  3. Uncheck "Default location"
  4. Specify a location outside the Eclipse workspace, e.g. C:\data\eclipseProjects\plugins\com.breskeby. tutorial.rcp.withMaven
  5. Set Source folder = src/main/java (following the Maven convention)
  6. Set Output folder = target/classes
  7. Next
  8. Check "Generate an Activator" and "This plugin will contribute to the UI"
  9. Check "Create an RCP Application"
  10. Next
  11. Select the template "Hello RCP"
  12. Next
  13. Change the application window title to "Hello RCP with Maven"
  14. Finish
  15. If asked, decide whether to open the Plugin Developer perspective (it probably is not worth it at this stage)
  16. Test the new plugin by clicking "Launch an Eclipse Application"

B. Create a product configuration file

  1. File -> New -> Other -> Product Configuration
  2. Parent folder = com.breskeby.tutorial.rcp.withMaven
  3. Filename = withmaven.product
  4. Check "Create a configuration file"
  5. Finish - the product overview is displayed
  6. Set Product Name = "RCP with Maven Example"
  7. Product ID: click New...
  8. Accept default values except Product Application
  9. Set Application = "com.breskeby.tutorial.rcp.withMaven"
  10. Finish
  11. Check "Product Configuration based on plug-ins"
  12. Click on the Configuration tab and click "Add..."
  13. Select com.breskeby.tutorial.rcp.withMaven and click OK
  14. Click "Add required plug-ins"
  15. Save the product configuration
  16. Test by right-clicking on the withmaven.product file in the package explorer and selecting "run as..." -> "Eclipse Application"

C. Create a build-properties file

  1. Right-click the project folder com.breskeby.tutorial.rcp.withMaven and select New -> Folder: name the folder "buildConfiguration"
  2. In Windows Explorer or equivalent, find your Eclipse installation and navigate down to eclipse/plugins/org.eclipse.pde.build_*/template/headless-build. Copy build.properties from this file to the clipboard. Paste it into your new buildConfiguration folder in Eclipse. The asterisk in the above path stands for the version number of the Eclipse PDE builder. Make a note of it, as you'll need it in step D
  3. Open the build.properties copy in an Eclipse text editor and click on the build.properties tab. Change the following properties:
    1. product=withmaven.product
    2. archivePrefix=withMaven
    3. configs=win32, win32, x86 or change depending on your desired target(s) - but René has noted that only one target architecture can be built at a time
    4. buildDirectory=[project directory]/target/eclipse.build (substitute the pathname of your project directory - see step A4 above - but use forward slashes)
    5. buildId=withMaven
    6. base=C:/tools/eclipse/win32.win32.x86 (depends on where you installed the target platform and which configuration you specified in configs - item c above). The base directory must contain a folder called "eclipse", which contains folders named "features" and "plugins"
    7. pluginPath=.
    The plugin path is the list of places to be searched for features and plugins needed during the build. In this case, the current working directory (i.e. the project directory) is fine, since the product build first creates the plugin in a plugins subdirectory.

D. Make a Maven Project Object Model (POM)

  1. Right-click the project folder com.breskeby.tutorial.rcp.withMaven and select New -> File: name the file "pom.xml"
  2. Into the new file, copy the template XML below
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.breskeby.tutorials.rcp</groupId>
    <artifactId>withMaven</artifactId>
    <packaging>zip</packaging>
    <name>RCP With Maven Example</name>
    <version>1.0-SNAPSHOT</version>
    <description>A Simple Product PDE Example</description>
    <build>
    <plugins>
    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>pde-maven-plugin</artifactId>
    <version>1.0-alpha-2-SNAPSHOT</version>
    <extensions>true</extensions>
    <configuration>
    <eclipseInstall>${env.M2_HOME}/../eclipse</eclipseInstall>
    <pdeProductFilename>withmaven.product</pdeProductFilename>
    <pdeBuildVersion>3.3.2.R331_v20071019</pdeBuildVersion>
    </configuration>
    </plugin>
    </plugins>
    </build>
    </project>
  3. Modify the configuration section as follows:
    1. eclipseInstall - this is the path to your Eclipse RCP Developer installation (e.g. C:\tools\eclipse in the above example). Because my current project follows a convention about installing all tools in one place, I was able to use a property for this as shown above - your mileage may vary
    2. pdeBuildVersion - insert the version number copied from the pathname of the folder whose name begins "org.eclipse.pde.build_" (see step C2 above)

E. Run the build and test the result

  1. Make sure all edited files have been saved
  2. Ensure that your settings.xml is configured such that the required Maven snapshot repository is searched for plugins
  3. Select the pom.xml file in the package explorer
  4. Set up and use an external tool configuration to invoke Maven or run it from a command-line in the project directory: mvn clean install
  5. The result turns up in [project directory]/../../I.withMaven/withMaven-[architecture].zip - unzip it, open the resulting folder and double-click the executable you have just built!

I hope that you have found this useful. If you run into snags, please check that you have followed all the above steps to the letter before contacting me (if you leave a comment on this blog I should be notified by e-mail).

Tuesday, 12 February 2008

The art of designing APIs

In the restaurant last night, I was discussing with two colleagues why open-source software efforts such as Maven sometimes go awry. One reason is that APIs get changed, which really hacks off some of their users. I mentioned that I had seen Joshua Bloch give a really useful talk about the design of APIs about four years ago. If his guidelines were followed more widely, perhaps it wouldn't be necessary for APIs or their semantics to change so often.

You can still get hold of Joshua's presentation slides and my notes of his talk at the SPA Conference web site. Use the local link [u1] to access the slides.

Joshua Bloch contributes to the Official Google Research Blog.

Monday, 4 February 2008

Maven dependency version ranges are broken

I've been tearing my hair out trying to get Maven to build Eclipse RCP products. A lot of the problems I have had, as it turns out, are to do with Maven POMs that express dependencies on Eclipse artifacts with a range of versions.

Because you can't always update all your Eclipse plugins at once, it might seem to make sense to express dependencies on them not as a fixed version number but as a range. The following is a typical example (/org/eclipse/jdt/launching/3.3.0-v20070510/launching-3.3.0-v20070510.pom):

<project>
...
<groupid>org.eclipse.jdt</groupid>
<artifactid>launching</artifactid>
<name>Java Development Tools Launching Support</name>
<version>3.3.0-v20070510</version>
...
<dependencies>
<dependency>
<groupid>org.eclipse.core</groupid>
<artifactid>resources</artifactid>
<version>[3.3.0,4.0.0)</version>
</dependency>
...
</dependencies>
...
</project>

Unfortunately this only works where the version number of the actual artifact in the repository conforms to one of two patterns:

A.B.C-D

or

A.B.C-qualifier

(it is unclear whether the qualifier itself is subject to any formatting rules). In all other cases, Maven compares the artifact version number to the upper and lower bound of the range as a simple string, which actually means that version 3.3.0-v20070604 sorts below 3.3.0 and thus does not match the expression in the above example.

I may be able to fix this for individual POMs within our project repository and get the example build running, but that is not a general-purpose solution. Before long, someone on our project will bring in a dependency on some Eclipse plugin, which is obtainable from the Maven repository at Maven Central but only with a POM that contains a version range in one of its transitive dependencies.

Unfortunately, the majority of Eclipse plugins are likely to be in the Maven repository with this kind of version range dependency. The reason is that they get put there using the maven-eclipse-plugin (to-maven goal), which does not resolve OSGi Require-bundle version ranges when converting them to Maven dependencies. The latest version is meant to honour a property "resolveVersionRanges" (default is false), but I have not been able to get this to work.

And here's why - Carlos has removed the line of code that invoked the resolveVersionRanges routine.