package org.dru.clay.resolver;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import org.dru.clay.logger.Logger;
import org.dru.clay.logger.LoggerFactory;
import org.dru.clay.respository.Configuration;
import org.dru.clay.respository.Repository;
import org.dru.clay.respository.ResolveResult;
import org.dru.clay.respository.artifact.Artifact;
import org.dru.clay.respository.artifact.Group;
import org.dru.clay.respository.artifact.Module;
import org.dru.clay.respository.artifact.UnresolvedArtifact;
import org.dru.clay.respository.dependency.Dependency;
import org.dru.clay.respository.transport.Transport;
public class DependencyResolverImpl implements DependencyResolver {
private static final int RESOLVE_THREAD_COUNT = 5;
private final Logger logger = LoggerFactory.getLogger(DependencyResolverImpl.class);
private final Repository repository;
private final Transport transport;
private final DirectoryLayout layout;
private final Object lock = new Object();
private int count;
final Set<Artifact> visitedArtifacts = new HashSet<Artifact>();
final Queue<ConfigurationContext> resolveQueue = new LinkedBlockingDeque<ConfigurationContext>();
final List<File> downloadedFiles = new ArrayList<File>();
ExecutorService executorService = null;
public DependencyResolverImpl(Repository repository, Transport transport) {
this.repository = repository;
this.transport = transport;
this.layout = new FlatDirectoryLayout();
}
@Override
public void begin() {
if (executorService != null) {
executorService.shutdownNow();
}
downloadedFiles.clear();
executorService = Executors.newFixedThreadPool(RESOLVE_THREAD_COUNT);
}
@Override
public Collection<File> end() {
try {
synchronized (lock) {
while (count > 0) {
lock.wait(0L);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
// TODO: All list might not have been downloaded.
} finally {
visitedArtifacts.clear();
resolveQueue.clear();
count = 0;
executorService.shutdownNow();
transport.cleanup();
}
return downloadedFiles;
}
@Override
public void resolve(final File directory, String configuration, Collection<Dependency> dependencies) {
filterDependencies(Collections.singleton(configuration), dependencies, resolveQueue);
while (resolveQueue.peek() != null) {
final ConfigurationContext context = resolveQueue.poll();
final Dependency dependency = context.getDependency();
final Group group = dependency.getGroup();
final UnresolvedArtifact unresolved = dependency.getArtifact();
final Artifact resolveArtifact = repository.lookup(transport, group, unresolved);
logger.info("Resolved %s -> %s", unresolved, resolveArtifact);
// TODO: Handle version conflicts
if (visitedArtifacts.contains(resolveArtifact)) {
// artifact already visited
continue;
}
visitedArtifacts.add(resolveArtifact);
final Module resolveModule = new Module(group, resolveArtifact);
final ResolveResult resolveResult = repository.resolve(transport, resolveModule);
for (Configuration config : resolveResult.getConfigurations()) {
if (!context.getConfigurations().contains(config.getName())) {
// the configuration isn't used in this resolve
continue;
}
synchronized (lock) {
count += config.getArtifacts().size();
}
for (final Artifact artifact : config.getArtifacts()) {
System.out.println("Downloading " + artifact); // TODO: fix
executorService.submit(new Runnable() {
@Override
public void run() {
final File destination = layout.file(directory, resolveModule, artifact);
try {
destination.getParentFile().mkdirs();
repository.get(transport, resolveModule, artifact, destination);
} finally {
synchronized (lock) {
downloadedFiles.add(destination);
count--;
lock.notify();
}
}
}
});
}
}
filterDependencies(context.getConfigurations(), resolveResult.getDependencies(), resolveQueue);
}
}
private void filterDependencies(Set<String> configs, Collection<Dependency> dependencies, final Queue<ConfigurationContext> resolveQueue) {
for (Dependency dependency : dependencies) {
for (String configuration : configs) {
final Set<String> configurations = dependency.getMapping().transform(configuration);
if (configurations.isEmpty()) {
// the dependency is not part of the current configuration
break;
}
resolveQueue.add(new ConfigurationContext(dependency, configurations));
}
}
}
}