From 6bb628adb4c338c373cae952b87c17a919e5a60c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sun, 4 Aug 2013 23:35:08 -0700 Subject: [PATCH] Indicate downloading dependencies progress in CLI Update CLI to show a "Downloading Dependencies..." message if the initial dependency resolution takes more than 3 seconds. Whilst downloading dots are appended to the message. Issue: #54589094 --- .../org/springframework/boot/cli/Log.java | 4 + .../cli/compiler/GrapeEngineCustomizer.java | 174 ++++++++++++++++++ .../boot/cli/compiler/GroovyCompiler.java | 15 +- 3 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GrapeEngineCustomizer.java diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/Log.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/Log.java index 6305aa951c..182a019fec 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/Log.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/Log.java @@ -27,6 +27,10 @@ public abstract class Log { System.out.println(message); } + public static void infoPrint(String message) { + System.out.print(message); + } + public static void error(String message) { System.err.println(message); } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GrapeEngineCustomizer.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GrapeEngineCustomizer.java new file mode 100644 index 0000000000..b642f09a6a --- /dev/null +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GrapeEngineCustomizer.java @@ -0,0 +1,174 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.cli.compiler; + +import groovy.grape.GrapeEngine; +import groovy.grape.GrapeIvy; + +import java.util.concurrent.TimeUnit; + +import org.apache.ivy.Ivy; +import org.apache.ivy.core.cache.ArtifactOrigin; +import org.apache.ivy.core.event.IvyEvent; +import org.apache.ivy.core.event.IvyListener; +import org.apache.ivy.core.event.resolve.EndResolveEvent; +import org.apache.ivy.core.module.descriptor.Artifact; +import org.apache.ivy.core.module.id.ArtifactId; +import org.apache.ivy.core.module.id.ModuleId; +import org.apache.ivy.core.settings.IvySettings; +import org.apache.ivy.plugins.resolver.ChainResolver; +import org.apache.ivy.plugins.resolver.DependencyResolver; +import org.apache.ivy.plugins.resolver.IBiblioResolver; +import org.apache.ivy.util.AbstractMessageLogger; +import org.apache.ivy.util.MessageLogger; +import org.springframework.boot.cli.Log; + +/** + * Customizes the groovy grape engine to download from Spring repos and provide simple log + * progress feedback. + * + * @author Phillip Webb + */ +class GrapeEngineCustomizer { + + private GrapeIvy engine; + + public GrapeEngineCustomizer(GrapeEngine engine) { + this.engine = (GrapeIvy) engine; + } + + @SuppressWarnings("unchecked") + public void customize() { + Ivy ivy = this.engine.getIvyInstance(); + IvySettings settings = this.engine.getSettings(); + + final DownloadingLog downloadingLog = new DownloadingLog(); + + ivy.getLoggerEngine().pushLogger(downloadingLog); + ChainResolver resolver = (ChainResolver) settings.getResolver("downloadGrapes"); + + // Add an early resolver for spring snapshots that doesn't try to locate + // anything non-spring + ChainResolver earlySpringResolver = new ChainResolver() { + @Override + public ArtifactOrigin locate(Artifact artifact) { + try { + ArtifactId artifactId = artifact.getId().getArtifactId(); + ModuleId moduleId = artifactId.getModuleId(); + if (moduleId.getOrganisation().startsWith("org.springframework")) { + return super.locate(artifact); + } + } + catch (Exception ex) { + return null; + } + return null; + } + }; + earlySpringResolver.setSettings(settings); + earlySpringResolver.setReturnFirst(true); + addSpringResolvers(earlySpringResolver); + resolver.getResolvers().add(0, earlySpringResolver); + + // Add spring resolvers again, but this time without any filtering + addSpringResolvers(resolver); + ivy.getEventManager().addIvyListener(new IvyListener() { + @Override + public void progress(IvyEvent event) { + if (event instanceof EndResolveEvent) { + downloadingLog.finished(); + } + } + }); + } + + private void addSpringResolvers(ChainResolver chain) { + chain.add(newResolver("spring-snapshot", "http://repo.springsource.org/snapshot")); + chain.add(newResolver("spring-milestone", + "http://repo.springsource.org/milestone")); + } + + private DependencyResolver newResolver(String name, String root) { + IBiblioResolver resolver = new IBiblioResolver(); + resolver.setName(name); + resolver.setRoot(root); + resolver.setM2compatible(true); + resolver.setSettings(this.engine.getSettings()); + return resolver; + } + + /** + * {@link MessageLogger} to provide simple progress information. + */ + private static class DownloadingLog extends AbstractMessageLogger { + + private static final long INITIAL_DELAY = TimeUnit.SECONDS.toMillis(3); + + private static final long PROGRESS_DELAY = TimeUnit.SECONDS.toMillis(1); + + private long startTime = System.currentTimeMillis(); + + private long lastProgressTime = System.currentTimeMillis(); + + private boolean started; + + private boolean finished; + + @Override + public void log(String msg, int level) { + logDownloadingMessage(); + } + + @Override + public void rawlog(String msg, int level) { + } + + @Override + protected void doProgress() { + logDownloadingMessage(); + } + + @Override + protected void doEndProgress(String msg) { + } + + private void logDownloadingMessage() { + if (!this.finished && System.currentTimeMillis() - this.startTime > INITIAL_DELAY) { + if (!this.started) { + this.started = true; + Log.infoPrint("Downloading dependencies.."); + this.lastProgressTime = System.currentTimeMillis(); + } + else if (System.currentTimeMillis() - this.lastProgressTime > PROGRESS_DELAY) { + Log.infoPrint("."); + this.lastProgressTime = System.currentTimeMillis(); + } + } + } + + public void finished() { + if (!this.finished) { + this.finished = true; + if (this.started) { + Log.info(""); + } + } + } + + } + +} diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java index fb9b3abf37..4ed956bbec 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java @@ -24,9 +24,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.ServiceLoader; import org.codehaus.groovy.ast.ClassNode; @@ -75,8 +73,7 @@ public class GroovyCompiler { if (configuration.getClasspath().length() > 0) { this.loader.addClasspath(configuration.getClasspath()); } - // FIXME: allow the extra resolvers to be switched on (off by default) - addExtraResolvers(); + new GrapeEngineCustomizer(Grape.getInstance()).customize(); compilerConfiguration .addCompilationCustomizers(new CompilerAutoConfigureCustomizer()); } @@ -203,14 +200,4 @@ public class GroovyCompiler { } - private void addExtraResolvers() { - Map resolver = new HashMap(); - resolver.put("name", "spring-milestone"); - resolver.put("root", "http://repo.springsource.org/milestone"); - Grape.addResolver(resolver); - resolver.put("name", "spring-snapshot"); - resolver.put("root", "http://repo.springsource.org/snapshot"); - Grape.addResolver(resolver); - } - }