Dependencies and repositories are central to Maven. Most people approach Maven as a tool in a very large stack of technology and they might not have the time to dig into the details. You might be using Eclipse, Maven, Nexus, Hudson, in addition to the various libraries and servers that are involved in your development environment, and Maven's dependency management is often so effective at hiding the details of repositories and dependencies that you take some of the complexity behind the scenes for granted. This post explains how dependencies and repositories work together for people who are interested some of the details that make dependency management in Maven "just work".
First of all, we need to know the default repository layout, which affects the repository path of each artifact. The related Maven source code is easy to understand, here it is:
private static final char PATH_SEPARATOR = '/'; private static final char GROUP_SEPARATOR = '.'; private static final char ARTIFACT_SEPARATOR = '-'; public String pathOf( Artifact artifact ) { ArtifactHandler artifactHandler = artifact.getArtifactHandler(); StringBuilder path = new StringBuilder( 128 ); path.append( formatAsDirectory( artifact.getGroupId() ) ).append( PATH_SEPARATOR ); path.append( artifact.getArtifactId() ).append( PATH_SEPARATOR ); path.append( artifact.getBaseVersion() ).append( PATH_SEPARATOR ); path.append( artifact.getArtifactId() ).append( ARTIFACT_SEPARATOR ).append( artifact.getVersion() ); if ( artifact.hasClassifier() ) { path.append( ARTIFACT_SEPARATOR ).append( artifact.getClassifier() ); } if ( artifactHandler.getExtension() != null && artifactHandler.getExtension().length() > 0 ) { path.append( GROUP_SEPARATOR ).append( artifactHandler.getExtension() ); } return path.toString(); } private String formatAsDirectory( String directory ) { return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR ); }
Take TestNG as an example, assume the artifact coordinate is groupId=org.testng, artifactId=testng, version=4.8, classifier=jdk15, and packaging=jar, the above code runs like this:
Now we know how Maven gets the repository path from artifact coordinate, but this is not the whole story. What if the artifact does not exist in local repository? what if the version of artifact is SNAPSHOT? How does Maven know where and how to get the latest SNAPSHOT?
When Maven resolves a dependency from a repository, these are the basic steps it follows:
A few notes:
In step 4 and 6: 'when it's required' means release/snapshot must be enabled for the repository, and repository updatePolicy is satisfied, or force update is enabled by -U options.
In step 5: In maven 2, LATEST will be constructed to the real latest version, no matter it's snapshots or not, but in maven 3, only the latest non-snapshot version will be used.
in step 4: a typical groupId/artifactId/maven-metadata.xml is like this:
<!--?xml version="1.0" encoding="UTF-8"?--> org.sonatype.nexus nexus 1.4.2-SNAPSHOT 1.4.0 1.3.5 1.3.6 1.3.7-SNAPSHOT 1.4.0-SNAPSHOT 1.4.0 1.4.0.1-SNAPSHOT 1.4.1-SNAPSHOT 1.4.1.1-SNAPSHOT 1.4.2-SNAPSHOT 20091214221557
All the available versions in this directory are listed in order. The latest element points to the latest version, and the release element points to the latest release version. With the help of this file, Maven knows what the latest release version is.
In step 6: a typical groupId/artifactId/version/maven-metadata.xml is like this:
<!--?xml version="1.0" encoding="UTF-8"?--> org.sonatype.nexus nexus 1.4.2-SNAPSHOT 20091214.221414 13 20091214221558
The timestamp and buildnumber of the latest snapshot is listed, so Maven can understand what the latest snapshot artifact file is. In this case, it is nexus-1.4.2-20091214.221414-13.pom.
Now that you are familiar with the process Maven uses to resolve dependencies, calculate dependency paths, and resolve conflicts you can refer to this post whenever you have an unexpected failure resolving a dependency.