The Holy Java

Building the right thing, building it right, fast

Using Ivy with pom.xml

Posted by Jakub Holý on January 26, 2011

It’s possible to use Apache Ivy with dependencies defined in pom.xml instead of its native ivy.xml but you will need to apply some workarounds and you’re loosing access to some functionality that you might (or might not) need.

The problem is that in a POM you can provide only a subset of settings available in ivy.xml and that Ivy understands only a subset of POM’s syntax.

The information here is based mostly on Ivy 2.1.0.

Disclaimer: I’m no Ivy expert and there are certainly better ways to achieve what I did. Also newer versions of Ivy may be better.

Limitations of pom.xml usage

Ivy understands only a subset of POM’s syntax: dependencies, dependencies of plugins or st. like that, parent module.

To learn exactly what parts of a pom.xml are processed by Ivy check the two main classes responsible for that (version 2.1.0): PomModuleDescriptorParser.java and PomReader.java.

Issues & solutions

Referring to a parent POM

In general you can user a parent POM, for example do declare properties that you then use e.g. in dependecy declarations, but there are few issues with parent POMs:

  1. Ivy ignores the relativePath element
  2. Ivy ignores the packaging=pom on the parent module

Ivy ignores the relativePath element

You can specify a parent project in your POM. Maven allows you to provide absolute or relative system path to the pom like this:

<parent>
	<groupId>com.ibm.education</groupId>
	<artifactId>lms-root-pom</artifactId>
	<version>1.0-SNAPSHOT</version>
	<relativePath>../lms.build/lms-root-pom/pom.xml</relativePath>
</parent>

But Ivy will ignore the relativePath and will only try to find it via a resolver (which might be OK for you but wasn’t for me).

Workaround
  1. Declare a special local file-system resolver for the parent pom
  2. Configure modules in ivysettings so that this resolver is used only for the parent pom module

The special FS resolver for the parent pom:

<ivysettings>
  <resolvers>
    <filesystem name="local-lms.build" force="true" descriptor="required">
      <ivy pattern="${ivy.settings.dir}/[module]/pom.xml" />
      <artifact pattern="${ivy.settings.dir}/[module]/emptyJarToSatisfyIvy.jar" />
    </filesystem>
   ...
  </resolvers>
</ivysettings>

(ivy.settings.dir is automatically set by Ivy based on the file attribute of the ivy:settings task; just make sure to use file= and not url=)

Module declaration:

<ivysettings>
	...
	<modules>
		<module organisation="com.ibm.education" name="lms-root-pom" resolver="local-lms.build"/>
		...
	</modules>
</ivysettings>

File structure:

  • lms.build
    • lms-root-pom
      • pom.xml
      • emptyJarToSatisfyIvy.jar (see below)
  • your-dependant-module
    • pom.xml

Ivy ignores the packaging=pom on the parent module

Ivy ignores the packaging=pom on the parent module and will always try to find a .jar for it, thus wasting precious time. The workaround is to create a fake, empty .jar, for example via echo “” > emptyJarToSatisfyIvy.jar. You can see it above in the lms-root-pom file structure and resolver’s configuration.

Publishing pom.xml to a Maven repository and respecting the dependencies of your own modules

I suppose you want to publish your own modules to a Maven repository manager such as Nexus or Artifactory. And you also want to publish module’s pom.xml with its dependencies and when you have another module depending on this one, you want Ivy to be aware of the (transitive) dependencies from the pom.

Publishing the pom.xml

Normally Ivy publishes only the single artifact <module name>.<packaging>. If you used ivy.xml you could declare additional artifacts in its <publications>/<artifact> however pom.xml gives you no such possibility.

Fortunately, since Ivy 2.2.0, it’s possible to declare the additional artifacts with the <artifact> element also under the Ant ivy:publish task:

<ivy:publish resolver="shared-snapshot"
	pubrevision="${ivy.revision}" forcedeliver="true"
	status="integration" overwrite="true">

	<artifacts pattern="${artifactspattern}" />
	<artifacts pattern="pom.xml" />

	<!-- Additional artifacts to publish (since Ivy 2.2.0): -->
	<artifact name="${ivy.module}" ext="pom" type="pom" />
</ivy:publish>

It’s essential that the name of the POM artifact is <artifactId>-<version>.pom, otherwise it won’t be recognized as the artifact’s POM when retrieving it. It is achieved by using the Ivy-provided property ivy.module and ext=pom. The attributes of ivy:publish are mostly unimportant, I’ve them like this for this is used for publishing snapshots.

Configuring Ivy to fetch the POM and respect the dependencies

Ivy will automatically respect dependencies in a POM but it must know that it should look for this file. To do that you must use the ibiblio resolver to retrieve artifacts from the repository. And, of course, there must be an <artifactId>-<version>.pom file next to the main .jar. But it doesn’t support publishing (at least so I believe) and therefore you also need to declare an URL resolver for publishing of your artifacts:

<ivysettings>
    <property name="upload.root.url" value="http://e25ciwas020.toronto.ca.ibm.com:8081/nexus/content/repositories" />
    ...
    <resolvers>
        <chain name="shared">
        	<ibiblio name="shared-snapshot-retrieval" m2compatible="true" root="${upload.root.url}/snapshots" />

	        <url name="shared-snapshot" m2compatible="true">
	          <artifact pattern="${upload.root.url}/snapshots/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]" />
	        </url>
        </chain>
    </resolvers>
</ivysettings>

To check it, look into /.ivy2/cache// – there should be ivy-.xml .original, which is actually a renamed pom.xml and ivy-<version>.xml, generated from that.

Especially look there into ivydata-<version>.xml, it contains information about the artifact’s metadata etc. In the ideal case it is similar to:

#ivy cached data file for com.ibm.education#lms.ab.common;40.0.0-SNAPSHOT
#Wed Jan 26 12:11:17 CET 2011
artifact\:lms.ab.common\#jar\#jar\#-869122099.is-local=false
artifact.resolver=shared-snapshot-retrieval
artifact\:lms.ab.common\#pom.original\#pom\#783440563.location=http\://e25ciwas020.toronto.ca.ibm.com\:8081/nexus/content/repositories/snapshots/com/ibm/education/lms.ab.common/40.0.0-SNAPSHOT/lms.ab.common-40.0.0-SNAPSHOT.pom
artifact\:lms.ab.common\#pom.original\#pom\#783440563.is-local=false
artifact\:lms.ab.common\#jar\#jar\#-869122099.location=http\://e25ciwas020.toronto.ca.ibm.com\:8081/nexus/content/repositories/snapshots/com/ibm/education/lms.ab.common/40.0.0-SNAPSHOT/lms.ab.common-40.0.0-SNAPSHOT.jar
resolver=shared-snapshot-retrieval
artifact\:ivy\#ivy\#xml\#1489462886.is-local=false
artifact\:ivy\#ivy\#xml\#1489462886.location=http\://e25ciwas020.toronto.ca.ibm.com\:8081/nexus/content/repositories/snapshots/com/ibm/education/lms.ab.common/40.0.0-SNAPSHOT/lms.ab.common-40.0.0-SNAPSHOT.pom

Mapping of configurations (scopes)

While Ivy let you define which dependencies should be fetched in which situation including the transitive one so that you can easily declare that a dependency’s “provided” dependencies should be respected during compilation and testing, with pom.xml you lose the ability to declare these configuration mappings and you have to live with the defaults. This means for example that a dependency’s dependencies with the scope=provided are always ignored. The solution is to use only the scope=compile for dependencies in your modules/artifacts that should be reused and manually filter out the dependencies you don’t want to include in your binary (e.g. a .war).

Conclusion

It’s possible to use Ivy 2.2.0 with Maven POMs but you should carefully explore the limitations of this approach and check them against your requirements. Good luck!

About these ads

Sorry, the comment form is closed at this time.

 
%d bloggers like this: