我的任务是解决项目的所有依赖关系。因为它不可能很简单(从来都不是,对吧?),所以需求中包含了一些不同的依赖管理工具。第一个版本是:
因为需要分析这些依赖项(及其源)以获得许可证,所以我必须下载这些项目的实际文件(更好的是源代码)。
这意味着我必须支持以下功能:
为了支持这一点,我设计了以下主要例程,它负责协调与这一混乱有关的所有进程。
public class Main {
private static final String MAVEN_SHORT = "m";
private static final String MAVEN_LONG = "maven";
private static final String OSGI_SHORT = "o";
private static final String OSGI_LONG = "osgi";
private static final String THREADS_SHORT = "t";
private static final String THREADS_LONG = "threads";
private static final Executor consumer = Executors.newSingleThreadExecutor();
private static final int DEFAULT_THREAD_COUNT = 8;
private static Executor workers;
private static final PriorityBlockingQueue<ResolutionTask> taskQueue = new PriorityBlockingQueue<>();
public static void main(String[] args) {
CommandLineParser cli = new DefaultParser();
Options opts = new Options();
// TODO Option.Builder?
opts.addOption(Option.builder(THREADS_SHORT).longOpt(THREADS_LONG).hasArg(true).desc("The number of threads to use for resolution").build());
opts.addOption(MAVEN_SHORT, MAVEN_LONG, true, "A maven-dependency declaration xml to analyze");
opts.addOption(OSGI_SHORT, OSGI_LONG, true, "An osgi-target descriptor xml to analyze");
List<DependencyResult> depResults = new ArrayList<>(args.length / 2);
try {
CommandLine commandLine = cli.parse(opts, args);
int threads = 0;
for (Iterator<Option> options = commandLine.iterator(); options.hasNext(); ) {
Option currentOption = options.next();
switch (currentOption.getOpt()) {
case THREADS_SHORT:
threads = Integer.parseInt(currentOption.getValue());
break;
case MAVEN_SHORT:
taskQueue.put(new MavenResolutionTask(taskQueue, currentOption.getValue()));
break;
case OSGI_SHORT:
taskQueue.put(new OsgiResolutionTask(taskQueue, currentOption.getValue()));
break;
}
}
if (threads == 0) {
threads = DEFAULT_THREAD_COUNT;
}
workers = Executors.newFixedThreadPool(threads, runnable -> {
Thread result = new Thread(runnable, "WorkerQueue Thread");
result.setDaemon(true);
return result;
});
ResolutionTask finalizer = new ResultProcessingTask(depResults);
taskQueue.put(finalizer);
consumer.execute(() -> {
List<CompletableFuture<DependencyResult>> currentCompletables = new ArrayList<>();
for (Iterator<ResolutionTask> taskIterator = taskQueue.iterator();
taskIterator.hasNext(); ) {
ResolutionTask task = taskIterator.next();
if (task == finalizer) {
CompletableFuture<Void> cleanup;
// TODO is synchronized necessary??
synchronized (currentCompletables) {
cleanup = CompletableFuture.allOf(currentCompletables.toArray(new CompletableFuture<?>[0]));
currentCompletables.clear();
}
cleanup.join();
if (taskQueue.isEmpty()) {
try {
finalizer.call();
} catch (Exception e) {
e.printStackTrace(System.err);
}
} else {
taskQueue.put(finalizer);
}
} else {
CompletableFuture<DependencyResult> completable = new CompletableFuture<>();
workers.execute(() -> {
try {
DependencyResult res = task.call();
completable.complete(res);
} catch (Exception e) {
completable.completeExceptionally(e);
}
});
// TODO is synchronized necessary??
synchronized (currentCompletables) {
currentCompletables.add(completable);
}
}
}
});
} catch (ParseException e) {
// Something really went wrong.
e.printStackTrace(System.out);
}
}
}为了完整起见:
MavenResolutionTask、OsgiResolutionTask和ResultProcessingTask都扩展了抽象类ResolutionTask,定义如下:
public abstract class ResolutionTask implements Callable<DependencyResult>, Comparable<ResolutionTask> {
protected static final int HIGH_PRIORITY = 10;
protected static final int MEDIUM_PRIORITY = 5;
protected static final int LAST_EXECUTE = Integer.MIN_VALUE;
protected final int priority;
protected ResolutionTask(int priority) {
this.priority = priority;
}
@Override
public int compareTo(ResolutionTask resolutionTask) {
return priority - resolutionTask.priority;
}
}我正在寻找一个回顾,特别是关于我如何解决同步和终止问题。虽然有逻辑执行图,但我不喜欢标准的终端检测机制(如Dijkstra算法),因为执行图可能相当大。
我知道有一些需要修改的评论,以及一些主要的可读性问题,但如果算法不像我所希望的那样工作,那么这些问题就不值得处理,如果存在更简单的解决方案,我也不应该解决这些问题。
发布于 2017-08-04 15:47:26
这是如此令人难以置信的不可靠,因为与CompletableFutures的集成只是简单地说,这里没有给出。对此的“简单”解决方案是将ResolutionTask更改为以下内容:
public abstract class ResolutionTask
extends CompletableFuture<List<DependencyResult>>
implements Runnable, Comparable<ResolutionTask> {这极大地简化了数据流,并明确了实现类的行为。很明显,我要说的是:
public class ExampleResolutionTask extends ResolutionTask {
@Override
public void run() {
try {
// perform surgery here
complete(results);
} catch (Exception e) {
completeExceptionally(e);
return;
}
}
}这将立即简化正在检查的调用代码:
workers = Executors.newFixedThreadPool(threads, runnable -> {
Thread result = new Thread(runnable, "WorkerQueue Thread");
result.setDaemon(true);
return result;
});
ResolutionTask finalizer = new ResultProcessingTask(depResults);
finalizer.whenComplete((a, b) -> {
workers.shutdown();
consumer.shutdown();
});
taskQueue.put(finalizer);
consumer.execute(() -> {
List<CompletableFuture<List<DependencyResult>>> currentCompletables = new ArrayList<>();
for (Iterator<ResolutionTask> taskIterator = taskQueue.iterator();
taskIterator.hasNext(); ) {
ResolutionTask task = taskIterator.next();
// remove the task from consideration in later stages
taskIterator.remove();
if (task == finalizer) {
CompletableFuture<Void> cleanup;
// TODO is synchronized necessary??
synchronized (currentCompletables) {
cleanup = CompletableFuture.allOf(currentCompletables.toArray(new CompletableFuture<?>[0]));
currentCompletables.clear();
}
cleanup.join();
if (taskQueue.isEmpty()) {
try {
finalizer.run();
} catch (Exception e) {
e.printStackTrace(System.err);
}
} else {
taskQueue.put(finalizer);
}
} else {
task.thenAccept(collection -> {
LOGGER.info("Completing resolution process with {} results", collection.size());
depResults.addAll(collection);
});
synchronized (currentCompletables) {
currentCompletables.add(task);
}
workers.execute(task);
}
}
});同步仍然存在不确定性,我还不太确定运行finalizer的条件是否完全正确,但这是朝着正确方向迈出的一步。
https://codereview.stackexchange.com/questions/165410
复制相似问题