package org.codehaus.mojo.repositorytools.discoverer;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.maven.archiva.discoverer.ArtifactDiscoverer;
import org.apache.maven.archiva.discoverer.DiscovererException;
import org.apache.maven.archiva.discoverer.DiscovererPath;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.manager.WagonManager;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.wagon.ConnectionException;
import org.apache.maven.wagon.ResourceDoesNotExistException;
import org.apache.maven.wagon.TransferFailedException;
import org.apache.maven.wagon.UnsupportedProtocolException;
import org.apache.maven.wagon.Wagon;
import org.apache.maven.wagon.authentication.AuthenticationException;
import org.apache.maven.wagon.authorization.AuthorizationException;
import org.apache.maven.wagon.repository.Repository;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.util.SelectorUtils;
import org.codehaus.plexus.util.StringUtils;
/*******************************************************************************
*
* @author Tom
* @plexus.component role="org.apache.maven.archiva.discoverer.ArtifactDiscoverer"
* role-hint="wagon"
*
*/
public class WagonDiscoverer extends AbstractLogEnabled implements
ArtifactDiscoverer {
private List kickedOutPaths = new ArrayList();
/**
* @plexus.requirement
*/
protected ArtifactFactory artifactFactory;
private List excludedPaths = new ArrayList();
/**
* @plexus.configuration default-value="true"
*/
private boolean trackOmittedPaths;
/**
* @plexus.requirement
*
*/
private WagonManager wagonManager;
/**
* Add a path to the list of files that were kicked out due to being
* invalid.
*
* @param path
* the path to add
* @param reason
* the reason why the path is being kicked out
*/
protected void addKickedOutPath(String path, String reason) {
if (trackOmittedPaths) {
kickedOutPaths.add(new DiscovererPath(path, reason));
}
}
/**
* Add a path to the list of files that were excluded.
*
* @param path
* the path to add
* @param reason
* the reason why the path is excluded
*/
protected void addExcludedPath(String path, String reason) {
excludedPaths.add(new DiscovererPath(path, reason));
}
/**
* Returns an iterator for the list if DiscovererPaths that were found to
* not represent a searched object
*
* @return Iterator for the DiscovererPath List
*/
public Iterator getKickedOutPathsIterator() {
// assert trackOmittedPaths;
return kickedOutPaths.iterator();
}
private void scan(Wagon wagon, String basePath, List collected) {
for (int i = 0; i < STANDARD_DISCOVERY_EXCLUDES.length; i++) {
if (SelectorUtils.matchPath(STANDARD_DISCOVERY_EXCLUDES[i], basePath)) {
return;
}
}
try {
List files = wagon.getFileList(basePath);
if (files.isEmpty()) {
collected.add(basePath);
} else {
basePath = basePath + "/";
for (Iterator iterator = files.iterator(); iterator.hasNext();) {
String file = (String) iterator.next();
scan(wagon, basePath + file, collected);
}
}
} catch (TransferFailedException e) {
throw new RuntimeException(e);
} catch (ResourceDoesNotExistException e) {
// is thrown when calling getFileList on a file
collected.add(basePath);
} catch (AuthorizationException e) {
throw new RuntimeException(e);
}
}
protected List scanForArtifactPaths(ArtifactRepository repository,
List blacklistedPatterns, String[] includes, String[] excludes) {
List collected;
try {
Wagon wagon = wagonManager.getWagon(repository.getProtocol());
Repository artifactRepository = new Repository(repository.getId(),
repository.getUrl());
wagon.connect(artifactRepository);
collected = new ArrayList();
scan(wagon, "/", collected);
wagon.disconnect();
return collected;
} catch (UnsupportedProtocolException e) {
throw new RuntimeException(e);
} catch (ConnectionException e) {
throw new RuntimeException(e);
} catch (AuthenticationException e) {
throw new RuntimeException(e);
}
}
/**
* Returns an iterator for the list if DiscovererPaths that were not
* processed because they are explicitly excluded
*
* @return Iterator for the DiscovererPath List
*/
public Iterator getExcludedPathsIterator() {
// assert trackOmittedPaths;
return excludedPaths.iterator();
}
public void setTrackOmittedPaths(boolean trackOmittedPaths) {
this.trackOmittedPaths = trackOmittedPaths;
}
public Artifact buildArtifact(String path) throws DiscovererException {
List pathParts = new ArrayList();
StringTokenizer st = new StringTokenizer(path, "/\\");
while (st.hasMoreTokens()) {
pathParts.add(st.nextToken());
}
Collections.reverse(pathParts);
Artifact artifact;
if (pathParts.size() >= 4) {
// maven 2.x path
// the actual artifact filename.
String filename = (String) pathParts.remove(0);
// the next one is the version.
String version = (String) pathParts.remove(0);
// the next one is the artifactId.
String artifactId = (String) pathParts.remove(0);
// the remaining are the groupId.
Collections.reverse(pathParts);
String groupId = StringUtils.join(pathParts.iterator(), ".");
String remainingFilename = filename;
if (remainingFilename.startsWith(artifactId + "-")) {
remainingFilename = remainingFilename.substring(artifactId
.length() + 1);
String classifier = null;
// TODO: use artifact handler, share with legacy discoverer
String type;
if (remainingFilename.endsWith(".tar.gz")) {
type = "distribution-tgz";
remainingFilename = remainingFilename.substring(0,
remainingFilename.length() - ".tar.gz".length());
} else if (remainingFilename.endsWith(".zip")) {
type = "distribution-zip";
remainingFilename = remainingFilename.substring(0,
remainingFilename.length() - ".zip".length());
} else if (remainingFilename.endsWith("-test-sources.jar")) {
type = "java-source";
classifier = "test-sources";
remainingFilename = remainingFilename.substring(0,
remainingFilename.length()
- "-test-sources.jar".length());
} else if (remainingFilename.endsWith("-sources.jar")) {
type = "java-source";
classifier = "sources";
remainingFilename = remainingFilename.substring(0,
remainingFilename.length()
- "-sources.jar".length());
} else {
int index = remainingFilename.lastIndexOf(".");
if (index >= 0) {
type = remainingFilename.substring(index + 1);
remainingFilename = remainingFilename.substring(0,
index);
} else {
throw new DiscovererException(
"Path filename does not have an extension");
}
}
Artifact result;
if (classifier == null) {
result = artifactFactory.createArtifact(groupId,
artifactId, version, Artifact.SCOPE_RUNTIME, type);
} else {
result = artifactFactory.createArtifactWithClassifier(
groupId, artifactId, version, type, classifier);
}
if (result.isSnapshot()) {
// version is *-SNAPSHOT, filename is *-yyyyMMdd.hhmmss-b
int classifierIndex = remainingFilename.indexOf('-',
version.length() + 8);
if (classifierIndex >= 0) {
classifier = remainingFilename
.substring(classifierIndex + 1);
remainingFilename = remainingFilename.substring(0,
classifierIndex);
result = artifactFactory.createArtifactWithClassifier(
groupId, artifactId, remainingFilename, type,
classifier);
} else {
result = artifactFactory.createArtifact(groupId,
artifactId, remainingFilename,
Artifact.SCOPE_RUNTIME, type);
}
// poor encapsulation requires we do this to populate base
// version
if (!result.isSnapshot()) {
throw new DiscovererException(
"Failed to create a snapshot artifact: "
+ result);
} else if (!result.getBaseVersion().equals(version)) {
throw new DiscovererException(
"Built snapshot artifact base version does not match path version: "
+ result
+ "; should have been version: "
+ version);
} else {
artifact = result;
}
} else if (!remainingFilename.startsWith(version)) {
throw new DiscovererException(
"Built artifact version does not match path version");
} else if (!remainingFilename.equals(version)) {
if (remainingFilename.charAt(version.length()) == '-') {
classifier = remainingFilename.substring(version
.length() + 1);
artifact = artifactFactory
.createArtifactWithClassifier(groupId,
artifactId, version, type, classifier);
} else {
throw new DiscovererException(
"Path version does not corresspond to an artifact version");
}
} else {
artifact = result;
}
} else {
throw new DiscovererException(
"Path filename does not correspond to an artifact");
}
} else {
throw new DiscovererException(
"Path is too short to build an artifact from");
}
return artifact;
}
/**
* Standard patterns to exclude from discovery as they are not artifacts.
*/
private static final String[] STANDARD_DISCOVERY_EXCLUDES = { "bin/**",
"reports/**", ".index", ".reports/**", ".maven/**", "**/*.md5",
"**/*.MD5", "**/*.sha1", "**/*.SHA1", "**/*snapshot-version",
"*/website/**", "*/licenses/**", "*/licences/**", "**/.htaccess",
"**/*.html", "**/*.asc", "**/*.txt", "**/*.xml", "**/README*",
"**/CHANGELOG*", "**/KEYS*" };
private List scanForArtifactPaths(ArtifactRepository repository,
List blacklistedPatterns) {
return scanForArtifactPaths(repository, blacklistedPatterns, null,
STANDARD_DISCOVERY_EXCLUDES);
}
public List discoverArtifacts(ArtifactRepository repository,
List blacklistedPatterns, ArtifactFilter filter)
throws DiscovererException {
// if ( !"file".equals( repository.getProtocol() ) )
// {
// throw new UnsupportedOperationException( "Only filesystem
// repositories are supported" );
// }
List artifacts = new ArrayList();
List artifactPaths = scanForArtifactPaths(repository,
blacklistedPatterns);
for (Iterator i = artifactPaths.iterator(); i.hasNext();) {
String path = (String) i.next();
try {
Artifact artifact = buildArtifactFromPath(path, repository);
if (filter.include(artifact)) {
artifacts.add(artifact);
} else {
addExcludedPath(path, "Omitted by filter");
}
} catch (DiscovererException e) {
addKickedOutPath(path, e.getMessage());
}
}
return artifacts;
}
/**
* Returns an artifact object that is represented by the specified path in a
* repository
*
* @param path
* The path that is pointing to an artifact
* @param repository
* The repository of the artifact
* @return Artifact
* @throws DiscovererException
* when the specified path does correspond to an artifact
*/
public Artifact buildArtifactFromPath(String path,
ArtifactRepository repository) throws DiscovererException {
Artifact artifact = buildArtifact(path);
if (artifact != null) {
artifact.setRepository(repository);
artifact.setFile(new File(repository.getBasedir(), path));
}
return artifact;
}
}