My friend Mark had an idea for a utility to search a list of your favourite repositories for a given dependency. So I thought I'd code up a version.
Maven Dependency Finder is a command line utility that takes a list of repositories and a dependency group id,artifact id and version, and returns the repositories which contain the JAR you want.
The utility reads a list of repositories from a file, and uses multiple threads to check for the required JARs, outputting the URLs where the JARs are hosted.
MavenDependencyFinder.java
package org.adrianwalker.maven;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public final class MavenDependencyFinder {
private static final int EXIT_FAILURE = -1;
private static final int EXIT_SUCCESS = 0;
private static final String FLAG_START = "-";
private static final String GROUP_FLAG = FLAG_START + "g";
private static final String ARTIFACT_FLAG = FLAG_START + "a";
private static final String VERSION_FLAG = FLAG_START + "v";
private static final String MAX_THREADS_FLAG = FLAG_START + "t";
private static final String REPOSITORY_FILE_FLAG = FLAG_START + "r";
private static final String HELP_FLAG = FLAG_START + "h";
private static final String DEFAULT_VERSION = "";
private static final List<String> DEFAULT_REPOSITORIES = Arrays.asList(new String[]{
"http://mirrors.ibiblio.org/pub/mirrors/maven2/"
});
private static final int DEFAULT_MAX_THREADS = 2;
private static final String ARTIFACT_VERSION_SEPERATOR = "-";
private static final String EMPTY_STRING = "";
private static final String HTTP_METHOD = "GET";
private static final String JAR_FILE_EXTENSION = ".jar";
private static final String PACKAGE_SEPERATOR = "\\.";
private static final int TIMEOUT_MILLISECONDS = 10000;
private static final String URL_SEPERATOR = "/";
public static void main(final String[] args) {
Map<String, String> parameters = readParameters(args);
if (parameters.isEmpty() || parameters.containsKey(HELP_FLAG)) {
printHelp();
System.exit(EXIT_SUCCESS);
}
if (!parameters.containsKey(GROUP_FLAG)) {
printError("Required parameter '-g' missing");
System.exit(EXIT_FAILURE);
}
if (!parameters.containsKey(ARTIFACT_FLAG)) {
printError("Required parameter '-a' missing");
System.exit(EXIT_FAILURE);
}
String groupId = parameters.get(GROUP_FLAG);
String artifactId = parameters.get(ARTIFACT_FLAG);
String version = DEFAULT_VERSION;
if (parameters.containsKey(VERSION_FLAG)) {
version = parameters.get(VERSION_FLAG);
}
int maxThreads = DEFAULT_MAX_THREADS;
if (parameters.containsKey(MAX_THREADS_FLAG)) {
maxThreads = Integer.parseInt(parameters.get(MAX_THREADS_FLAG));
}
List<String> repositories = DEFAULT_REPOSITORIES;
if (parameters.containsKey(REPOSITORY_FILE_FLAG)) {
String fileName = parameters.get(REPOSITORY_FILE_FLAG);
try {
repositories = readRepositoriesFile(fileName);
} catch (Throwable t) {
printError(String.format("Error reading file '%s'", fileName));
System.exit(EXIT_FAILURE);
}
}
findDependency(maxThreads, repositories, groupId, artifactId, version);
System.exit(EXIT_SUCCESS);
}
private MavenDependencyFinder() {
}
private static void findDependency(final int maxThreads, final List<String> repositories, final String groupId, final String artifactId, final String version) {
ExecutorService threadPool = Executors.newFixedThreadPool(maxThreads);
Map<String, Future<Boolean>> results = new HashMap<String, Future<Boolean>>();
for (String repository : repositories) {
Future<Boolean> isHostingDependancy = threadPool.submit(new CallableUrlFinder(repository, groupId, artifactId, version));
results.put(repository, isHostingDependancy);
}
Set<String> responses = new HashSet<String>();
while (results.size() > responses.size()) {
for(Entry<String, Future<Boolean>> entry : results.entrySet()) {
String repository = entry.getKey();
Future<Boolean> isHostingDependancy = entry.getValue();
if (!responses.contains(repository) && isHostingDependancy.isDone()) {
Boolean found;
try {
found = isHostingDependancy.get();
responses.add(repository);
} catch (Throwable t) {
found = false;
}
if (found) {
System.out.println(repository);
}
}
}
Thread.yield();
}
threadPool.shutdown();
}
private static Map<String, String> readParameters(final String[] args) {
int argsCount = args.length;
if (argsCount == 1 && args[0].equals(HELP_FLAG)) {
printHelp();
System.exit(EXIT_SUCCESS);
}else if (argsCount % 2 == 1) {
printError("Invalid number of arguments");
System.exit(EXIT_FAILURE);
}
Map<String, String> parameters = new HashMap<String, String>();
for (int i = 0; i < argsCount; i = i + 2) {
String flag = args[i];
if (!flag.startsWith(FLAG_START)) {
printError(String.format("Invalid flag '%s'", flag));
System.exit(EXIT_FAILURE);
}
String value = args[i + 1];
if (value.startsWith(FLAG_START)) {
printError(String.format("Invalid value '%s' for flag '%s'", value, flag));
System.exit(EXIT_FAILURE);
}
parameters.put(flag, value);
}
return parameters;
}
private static List readRepositoriesFile(final String fileName) throws IOException {
List<String> repositories = new ArrayList<String>();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(fileName));
String line;
while ((line = reader.readLine()) != null) {
repositories.add(line.trim());
}
} finally {
if (null != reader) {
reader.close();
}
}
return repositories;
}
private static void printHelp() {
String help = "Usage: mdf -g <group id> -a <artifact id> [-v <version>] [-r <filename>] [-t <threads>] [-h]\n\n"
+ " -g\tDependency group id\n"
+ " -a\tDependency artifact id\n"
+ " -v\tDependency version\n"
+ " -r\tRepository file\n"
+ " -t\tMaximum threads\n"
+ " -h\tHelp";
System.out.println(help);
}
private static void printError(final String message) {
System.out.println(message);
}
private static final class CallableUrlFinder implements Callable<Boolean> {
private final String repository;
private final String groupId;
private final String artifactId;
private final String version;
public CallableUrlFinder(final String repository, final String groupId, final String artifactId, final String version) {
this.repository = repository;
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
}
@Override
public Boolean call() throws Exception {
return isHostingDependency(repository, groupId, artifactId, version);
}
private boolean isHostingDependency(final String repository, final String groupId, final String artifactId, final String version) {
String dependencyUrl = gavToUrl(repository, groupId, artifactId, version);
HttpURLConnection connection = null;
try {
URL url = new URL(dependencyUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(HTTP_METHOD);
connection.setReadTimeout(TIMEOUT_MILLISECONDS);
connection.connect();
return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
} catch (Throwable t) {
return false;
} finally {
if (null != connection) {
connection.disconnect();
}
}
}
private String gavToUrl(final String repository, final String groupId, final String artifactId, final String version) {
/*
* Format:
*
* http://mirrors.ibiblio.org/pub/mirrors/maven2/log4j/log4j/1.2.13/log4j-1.2.13.jar
*
*/
StringBuilder builder = new StringBuilder();
builder.append(repository) //
.append(repository.endsWith(URL_SEPERATOR) ? EMPTY_STRING : URL_SEPERATOR) //
.append(groupId.replaceAll(PACKAGE_SEPERATOR, URL_SEPERATOR)) //
.append(URL_SEPERATOR).append(artifactId);
if (version != null && !version.isEmpty()) {
builder.append(URL_SEPERATOR) //
.append(version) //
.append(URL_SEPERATOR) //
.append(artifactId) //
.append(ARTIFACT_VERSION_SEPERATOR) //
.append(version).append(JAR_FILE_EXTENSION);
}
return builder.toString();
}
}
}
Build the project with:
mvn clean installto build a directory and zip assembly containing the executable JAR file, a batch file and shell script to run the utility from the command line.
Running the command with the -h flag shows the help for the utility:
Usage: mdf -g <group id> -a <artifact id> [-v <version>] [-r <filename>] [-t <threads>] [-h] -g Dependency group id -a Dependency artifact id -v Dependency version -r Repository file -t Maximum threads -h Help
To run the utility with a list of repositories and search for log4j:
mdf -g log4j -a log4j -r repositories.txt
outputs:
http://mirrors.ibiblio.org/pub/mirrors/maven2/ http://repository.jboss.org/maven2/
where the repositories list input file contains:
repositories.txt
http://mirrors.ibiblio.org/pub/mirrors/maven2/ http://repository.jboss.org/maven2/ http://download.java.net/maven/2 http://repository.codehaus.org
Source Code
- Java Maven Project - maven-dependency-finder.zip