首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >重新解析依赖关系并总结所有结果。

重新解析依赖关系并总结所有结果。
EN

Code Review用户
提问于 2017-06-09 23:35:02
回答 1查看 61关注 0票数 5

我的任务是解决项目的所有依赖关系。因为它不可能很简单(从来都不是,对吧?),所以需求中包含了一些不同的依赖管理工具。第一个版本是:

  • Maven
  • OSGI

因为需要分析这些依赖项(及其源)以获得许可证,所以我必须下载这些项目的实际文件(更好的是源代码)。

这意味着我必须支持以下功能:

  1. 从基本任意的urls中下载广域网中的jars,详见maven / osgi协议。
  2. 跟踪“工件名称”、版本和我们解析的实际URL。
  3. 最好的例子(也就是。在未来)程序应该已经在工件上放置了一个初步的许可。
  4. 将结果写进csv并关闭程序。

为了支持这一点,我设计了以下主要例程,它负责协调与这一混乱有关的所有进程。

代码语言:javascript
复制
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);
        }
    }
}

为了完整起见:

MavenResolutionTaskOsgiResolutionTaskResultProcessingTask都扩展了抽象类ResolutionTask,定义如下:

代码语言:javascript
复制
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算法),因为执行图可能相当大。

我知道有一些需要修改的评论,以及一些主要的可读性问题,但如果算法不像我所希望的那样工作,那么这些问题就不值得处理,如果存在更简单的解决方案,我也不应该解决这些问题。

EN

回答 1

Code Review用户

发布于 2017-08-04 15:47:26

这是如此令人难以置信的不可靠,因为与CompletableFutures的集成只是简单地说,这里没有给出。对此的“简单”解决方案是将ResolutionTask更改为以下内容:

代码语言:javascript
复制
public abstract class ResolutionTask 
  extends CompletableFuture<List<DependencyResult>> 
  implements Runnable, Comparable<ResolutionTask> {

这极大地简化了数据流,并明确了实现类的行为。很明显,我要说的是:

代码语言:javascript
复制
public class ExampleResolutionTask extends ResolutionTask {
    @Override
    public void run() {
        try {
            // perform surgery here
            complete(results);
        } catch (Exception e) {
            completeExceptionally(e);
            return;
        }
    }
}

这将立即简化正在检查的调用代码:

代码语言:javascript
复制
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的条件是否完全正确,但这是朝着正确方向迈出的一步。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/165410

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档