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