Resolving problem when Maven repo contains POM but not Jar

Using multiple maven repositories seems to be broken. To reproduce my problem I’ve created a very simple java-project with just a single HelloWorld.java.

The build.gradle looks like this:

apply plugin: 'java'
  repositories {
      maven {
    url 'http://repo1.maven.org/maven2'
    }
          maven {
     url 'http://repository.jboss.org/nexus/content/groups/public-jboss'
    }
      }
  dependencies {
       compile group: 'javax.jms', name: 'jms', version: '1.1'
}

The call to gradle assemble fails:

Dieters-MacBook-Pro:gradle_test rehdie$ gradle
assemble
:compileJava
  FAILURE: Build failed with an exception.
  * What went wrong:
Could not resolve all dependencies for configuration ':compile'.
> Artifact 'javax.jms:jms:1.1@jar' not found.
  * Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
  BUILD FAILED

If I remove the first repository (which does not contain the requested jar-file) from the build.gradle, the build succeeds!

IMHO this is a bug or did I miss something? Any help would be appreciated.

My environment:

Mac OSX 10.7.4 (Lion) Java 1.6.0_31 Gradle Version: 1.0-rc-3

Dieter

If you want Gradle to look for a POM in one repository, and the corresponding Jar in another repository, you’'ll have to do:

repositories {
    url 'http://repo1.maven.org/maven2'
    artifactUrls 'http://repository.jboss.org/nexus/content/groups/public-jboss'
  }

You’re hitting http://issues.gradle.org/browse/GRADLE-2034, or a variant of it.

If Gradle finds a POM file in a repository, it expects to find the declared artifacts in the same repository. This is not the case for ‘javax.jms:jms:1.1’ in Maven Central.

You can workaround this issue by constructing a composite repository, as Peter suggests, or by placing ‘repository.jboss.org’ higher in your list of repositories than Maven Central.

I’ve read the docs, but i didn’t work. Seems, that there are some dependencies with the pom-file in repo1 and the jar in repo2 and some other dependencies with pom in repo2 and jar in repo1.

I configured the artifactUrls for repo1 only. After adding an artifactsUrl to repo2 (pointing to repo1) it works.

thanks for your help Dieter

That workaround doesn’t work for a local repo. We’re trying to move gently from maven to gradle, but we keep hitting this bug when resolving artifacts from mavenLocal(). I tried the suggested workaround by doing this:

maven {
        url mavenLocal().url
        artifactUrls "<our nexus repo url>"
    }

However, gradle doesn’t like that:

org.gradle.api.artifacts.ResolveException: Could not resolve all dependencies for configuration ':testCompile'.
 at org.gradle.api.internal.artifacts.ivyservice.ErrorHandlingArtifactDependencyResolver.wrapException(ErrorHandlingArtifactDependencyResolver.java:47)
 at org.gradle.api.internal.artifacts.ivyservice.ErrorHandlingArtifactDependencyResolver.access$000(ErrorHandlingArtifactDependencyResolver.java:26)
 at org.gradle.api.internal.artifacts.ivyservice.ErrorHandlingArtifactDependencyResolver$BrokenResolvedConfiguration.rethrowFailure(ErrorHandlingArtifactDependencyResolver.java:127)
...
Caused by: java.lang.IllegalArgumentException: URI scheme is not "file"
 at org.gradle.api.internal.externalresource.transport.file.FileTransport.convertToPath(FileTransport.java:46)
 at org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory$ListeningRepositoryTransport.convertToPath(RepositoryTransportFactory.java:96)
 at org.gradle.api.internal.artifacts.repositories.MavenResolver.addArtifactLocation(MavenResolver.java:81)
...

It looks like for any given repository, the scheme of the “artifactUrls” must match that of the “url”?

That’s correct. You cannot mix http and file urls in a single repository.

Is there a reason you cannot simply put ‘mavenLocal’ last in your list of repositories? If that doesn’t work, can you explain why?

There are two reasons. The less vital one is that you use a local repo because it’s faster and you don’t have to wait for a download. More importantly, however, consider when you’re building some projects with maven and some with gradle. If you update a maven project and install a snapshot of it locally, which you want to use from your gradle project, then you’re out of luck if mavenLocal() is last because it will grab an old snapshot from a remote repo. This problem puts you in the unfortunate position of having to leave mavenLocal() out of the repo list and add it in manually only when you need it.

Using ‘mavenLocal’ to avoid downloads is not valid at all: Gradle’s dependency resolution will copy the jar from the mavenLocal repository anyway, after verifying that it matches a remote artifact (using checksums). So there will be no download involved. But by adding mavenLocal your build file is less portable, since it’s easy to depend on an artifact that is in your local Maven repo, but not in somebody else’s.

Choosing local vs remote snapshots is more valid use case: it is unfortunate that Maven does not give you any way to separate artifacts that are generated locally from artifacts that are simply cached for convenience. It is also unfortunate that Gradle does not (yet) let you specify a repository to apply for only a particular configuration - this would let you add mavenLocal but only use it for resolving locally built artifacts.

But the second scenario is more complex: sometimes you want to use a newer snapshot available remotely in preference to your local version. Since there is no way for us to compare these 2 snapshots (local vs remote), then you need to do it when you are running the build. For example, you could conditionally include the ‘mavenLocal’ (ahead of the corporate publications, but after 3rd party dependencies) based on a condition in your build. You can use a build property (‘gradle -PuseLocal’) or a special task (‘gradle buildLocal’) to make this work.

I pretty much agree on the first part (avoiding downloads), though I think it still has some relevance when you’re talking about easing into gradle from maven.

On the problem of needing to choose a more up-to-date snapshot from a remote, maven users are already familiar with it, and you have –update-snapshots for that, so a similar mechanism for a gradle build shouldn’t be that onerous. That still doesn’t fix the problem at hand, though, which is that if my local repo has a pom without its attendant jar, it breaks gradle.