Search code examples
mavenosgiintegration-testingpax-exam

Pax Exam 4 and multiple Maven repositories not working


I'm trying to run a very basic Pax Exam 4 unit test but one that needs to access multiple Maven repositories (not Maven Central). Here's the code:

@RunWith(PaxExam.class)
public class ExamTest {
    @Inject
    private BundleContext bundleContext;

    @Configuration
    public Option[] config() {        
        return options(
            repositories(
                repository("http://maven.wso2.org/nexus/content/groups/wso2-public").id("wso2"),
                repository("http://nexus.codehaus.org/snapshots").id("nexus.public.repo").allowSnapshots(),
            ),
            mavenBundle("commons-httpclient.wso2", "commons-httpclient").version("3.1.0.wso2v2"),
            mavenBundle("org.codehaus.woodstox", "stax2-api").version("3.0.1-SNAPSHOT"),

            cleanCaches(),
            junitBundles()
        );
    }

    @Test
    public void testInjection() {
        Assert.assertNotNull(bundleContext);
        Bundle[] bundles = bundleContext.getBundles();
        for (Bundle bundle : bundles) {
            System.out.println(bundle.getSymbolicName() + ", state = " + bundle.getState());
        }
    }
}

The actual test is trivial but just for testing purposes so never mind that (the repos and libraries are also chosen just for testing purposes). The problem is that the above does not work and Pax Exam complains that the repository URLs are invalid. Output when running this test is as follows:

[main] ERROR org.ops4j.pax.url.mvn.internal.AetherBasedResolver - invalid repository URLs
java.net.MalformedURLException: no protocol: +http://nexus.codehaus.org/snapshots/
    at java.net.URL.<init>(URL.java:585)
    at java.net.URL.<init>(URL.java:482)
    at java.net.URL.<init>(URL.java:431)
    at org.ops4j.pax.url.mvn.internal.config.MavenRepositoryURL.<init>(MavenRepositoryURL.java:191)
    at org.ops4j.pax.url.mvn.internal.config.MavenConfigurationImpl.getRepositories(MavenConfigurationImpl.java:303)
    at org.ops4j.pax.url.mvn.internal.AetherBasedResolver.selectRepositories(AetherBasedResolver.java:254)
    ...

As you can see, for some reason a "+" is prepended to the second URL and this causes an exception in MavenConfigurationImpl. The strange thing is, the first URL also has a "+" when I debug the code but that one gets stripped away by the Pax-code. The second one however does not get stripped away and then causes the MalformedURLException when the string is passed to the MavenRepositoryURL constructor:

if (repositoriesProp != null && repositoriesProp.trim().length() > 0) {
    String[] repositories = repositoriesProp.split(REPOSITORIES_SEPARATOR);
    for (String repositoryURL : repositories) {
        repositoriesProperty.add(new MavenRepositoryURL(repositoryURL.trim()));
    }
}

Now this looks like a bug to me but I can't really believe that such a basic option (being able to handle multiple Maven repos) does not work so I'm probably doing something wrong. So, my question: how does one enable Pax Exam to download maven bundles from multiple Maven repositories?

Also: everything works fine if you only add 1 repository and it doesn't matter if you use the repositories()-method or not. The result is the same if you use the repository() method multiple times like this:

@Configuration
    public Option[] config() {        
        return options(
            repository("http://maven.wso2.org/nexus/content/groups/wso2-public").id("wso2"),
            repository("http://nexus.codehaus.org/snapshots").id("nexus.public.repo").allowSnapshots(),         
            mavenBundle("commons-httpclient.wso2", "commons-httpclient").version("3.1.0.wso2v2"),
            mavenBundle("org.codehaus.woodstox", "stax2-api").version("3.0.1-SNAPSHOT"),

            cleanCaches(),
            junitBundles()
        );
    }

Below a snippet of the POM showing the dependencies (and versions) I'm using:

<dependencies>        
    <dependency>
        <groupId>org.osgi</groupId>
        <artifactId>org.osgi.core</artifactId>
        <version>4.3.1</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.10</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.ops4j.pax.exam</groupId>
        <artifactId>pax-exam-container-native</artifactId>
        <version>4.4.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.ops4j.pax.url</groupId>
        <artifactId>pax-url-aether</artifactId>
        <version>2.3.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.ops4j.pax.url</groupId>
        <artifactId>pax-url-link</artifactId>
        <version>2.3.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.ops4j.pax.url</groupId>
        <artifactId>pax-url-classpath</artifactId>
        <version>2.3.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.ops4j.pax.exam</groupId>
        <artifactId>pax-exam-junit4</artifactId>
        <version>4.4.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.geronimo.specs</groupId>
        <artifactId>geronimo-atinject_1.0_spec</artifactId>
        <version>1.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.ops4j.pax.exam</groupId>
        <artifactId>pax-exam-link-assembly</artifactId>
        <version>4.4.0</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.eclipse.tycho</groupId>
        <artifactId>org.eclipse.osgi</artifactId>
        <version>3.7.0.v20110613</version>
        <scope>test</scope>
    </dependency> 
</dependencies>

Solution

  • It's just a bug.

    The Pax Exam regression tests for the repository() option currently don't use more than one repository, and apparent nobody else ever did. (In fact, the preferred way of handling multiple external repositories is using a repository manager set up as mirror, so that explains why this feature may not be quite so basic at all.)

    The documentation of the Pax URL mvn: protocol handler is a bit too vague about the syntax of the system property org.ops4j.pax.url.mvn.repositories. It mentions a leading plus sign to indicate that the given repositories shall be used in addition to the repositories from Maven settings.xml.

    Pax Exam currently prepends a plus to each repository, but Pax URL expects at most one plus for the entire list.

    As a workaround, you can replace your repository options by a system property option like this:

    systemProperty("org.ops4j.pax.url.mvn.repositories").value("+repo1,repo2")
    

    Note: "repo1" and "repo2" are the actual URLs of your repositories.