Using namespaces


Now that you have seen how simple it is to create your own repository from an existing one, you may wonder how you can handle more complex cases, like when the source and destination repositories don't follow the same naming conventions.


On the road to a professional repository

In this section, you will learn how to build a professional repository. What is a professional repository? Our vision is to say that a good quality repository must follow clear rules about projects naming and must offer correct, useable, configurations and verified project descriptors. In order to achieve those goals, we think that you have to build your own repository.
We have seen in the previous example, that we could use some public repositories to begin to build our own repository. Nevertheless, the result is not always the expected one, especially concerning the naming rules used.

This problem is pretty normal when you have an existing repository, and want to benefit from large public repositories which do not follow the same naming conventions. It also shows up because many public repositories do not use a consistent naming scheme. For example, why don't all the apache commons modules use the org.apache.commons organization? Well.. for historical reasons. But if you setup your own repository, you may not want to suffer from the mistakes of history.

Fortunately, Ivy has a very powerful answer to this problem: namespaces.

Using namespaces

If you look at the repository built with the previous tutorial, you will see exactly what we were talking about: all apache commons modules use their own name as their organization.

So let's see what Ivy can do using namespaces (we will dig into details later):
[ivy@apache:/ivy/build-a-ivy-repository]$ ant maven2-namespace
Buildfile: /ivy/build-a-ivy-repository/build.xml

load-ivy:

init-ivy:

maven2-namespace:
[ivy:install] :: Apache Ivy 2.3.0 - 20130110142753 :: http://ant.apache.org/ivy/ ::
[ivy:install] :: loading settings :: file = /ivy/build-a-ivy-repository/settings/ivysettings-advanced.xml
[ivy:install] :: installing apache#commons-lang;1.0 ::
[ivy:install] :: resolving dependencies ::
[ivy:install] 	found apache#commons-lang;1.0 in libraries
[ivy:install] :: downloading artifacts to cache ::
[ivy:install] downloading http://repo1.maven.org/maven2/commons-lang/commons-lang/1.0/commons-lang-1.0.jar ...
[ivy:install] .................. (62kB)
[ivy:install] .. (0kB)
[ivy:install] 	[SUCCESSFUL ] apache#commons-lang;1.0!commons-lang.jar (234ms)
[ivy:install] downloading http://repo1.maven.org/maven2/commons-lang/commons-lang/1.0/commons-lang-1.0-javadoc.jar ...
[ivy:install] ......... (170kB)
[ivy:install] .. (0kB)
[ivy:install] 	[SUCCESSFUL ] apache#commons-lang;1.0!commons-lang.jar(javadoc) (468ms)
[ivy:install] :: installing in my-repository ::
[ivy:install] 	published commons-lang to /ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/jars/commons-lang-1.0.jar
[ivy:install] 	published commons-lang to /ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/javadocs/commons-lang-1.0.jar
[ivy:install] 	published ivy to /ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/ivys/ivy-1.0.xml
[ivy:install] :: install resolution report ::
[ivy:install] :: resolution report :: resolve 0ms :: artifacts dl 702ms
	---------------------------------------------------------------------
	|                  |            modules            ||   artifacts   |
	|       conf       | number| search|dwnlded|evicted|| number|dwnlded|
	---------------------------------------------------------------------
	|      default     |   1   |   1   |   1   |   0   ||   2   |   2   |
	---------------------------------------------------------------------

BUILD SUCCESSFUL
Total time: 2 seconds

Now if we look at our repository, it seems to look fine.
$ find /ivy/build-a-ivy-repository/myrepository/advanced -type f -print
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/ivys/ivy-1.0.xml
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/ivys/ivy-1.0.xml.md5
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/ivys/ivy-1.0.xml.sha1
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/jars/commons-lang-1.0.jar
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/jars/commons-lang-1.0.jar.md5
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/jars/commons-lang-1.0.jar.sha1
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/javadocs/commons-lang-1.0.jar
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/javadocs/commons-lang-1.0.jar.md5
/ivy/build-a-ivy-repository/myrepository/advanced/apache/commons-lang/javadocs/commons-lang-1.0.jar.sha1

We can even have a look at the commons-lang ivy file in our repository:
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="1.0">
<info organisation="apache"
module="commons-lang"
revision="1.0"
status="integration"
publication="20051124062021"
namespace="ibiblio-maven2"
/>

...
Alright, we see that the organisation is now 'apache'. But where did Ivy pick this up?

How does this work ?

Actually, Ivy uses the same repository as before for the source repository, with only one difference: the namespace parameter:
<ibiblio name="libraries" 
root="${ibiblio-maven2-root}"
m2compatible="true"
namespace="maven2"
/>
A namespace is defined by a set of rules. These rules are based on regular expressions and tell Ivy how to convert data from the repository namespace to what is called the system namespace, i.e. the namespace in which Ivy runs most of the time (Note: the Ivy cache always uses the system namespace).

For the namespace we call maven2, we have declared several rules. Below is one of the rules:

rule handling the imported apache maven1 projects

<rule>	<!-- imported apache maven1 projects -->
<fromsystem>
<src org="apache" module=".+"/>

<dest org="$m0" module="$m0"/>
</fromsystem>
<tosystem>
<src org="commons-.+" module="commons-.+" />
<src org="ant.*" module="ant.*" />
...
<src org="xmlrpc" module="xmlrpc" />

<dest org="apache" module="$m0"/>
</tosystem>
</rule>
Note about regular expressions usage : In order to distinguish matching regular expressions found in organization, module, and revision, the notation Ivy uses prefixes the matching regular expression with the letters 'o', 'm' and 'r'.
$o0 : the whole matching value in the organization attribute
$o1 : the first matching expression group that was marked in the organization attribute
...
The same applies for modules : $m0, $m1, ...
and for revisions : $r0, $r1, ...
To understand namespaces,
  • fromsystem : we define here that the projects defined in the system namespace under the organization called "apache" are transformed into the destination namespace into projects whose organization is named with the module name, whatever the revision is. For example, the project apache#commons-lang;1.0 in the system namespace will be translated into commons-lang#commons-lang;1.0 in the maven2 resolver namespace.
  • tosystem : we define here the reverse mapping, i.e. how to translate apache projects from maven 2 repo into apache projects in the system namespace. The rule used here tells Ivy that all projects matching commons-.+ (see it as java regular expression) for their organization name and module name are transformed into projects whose organisation is apache with the module name as it was found. The same kind of rule is defined for others apache projects like ant, etc.
OK, you should now get the idea behind namespaces, so go ahead and look at the ivysettings-advanced.xml file in this example. You can test the installation of a module and its dependencies using namespaces.

Run
ant maven2-namespace-deps
and you will see the resulting repository is cleaner than the first one we built.

From our experience, investing in creating a namespace is worth the time it costs if you often need to add new modules or revisions of third party libraries in your own repository, where naming rules already exist or are rather strict.