From f5ab0cf2dc463783ad8345aaabebf4e85830f426 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 5 Sep 2014 15:15:14 +0100 Subject: [PATCH] Update CLI install to prefer local snapshots to those in a remote repo Previously, InstallCommand used a custom Grape root and then walked the tree of files downloaded by Aether to determine which files it should install or uninstall. In some scenarios two files for the same module would be present: one named foo-1.0.0.BUILD-SNAPSHOT.jar and one named foo-1.0.0.BUILD-20140905.091809-2.jar. The former is from the local repository and the later is from a remote repository. In this case, the visitor would do the wrong thing and the latter would be installed into lib. This commit updates InstallCommand to determine the jars that it should process by consulting GroovyCompiler's classpath, rather than by walking Aether's cache. This approach selects the correct jar as Aether has already figured this out as part of resolving the dependency. It also brings InstallCommand into line with JarCommand. The previous implementation used Java 7-specific File APIs. As part of the above-described change this usage has been removed. The install command can now be used on Java 6. Fixes gh-1515 --- .../cli/command/install/InstallCommand.java | 137 +++++++++--------- 1 file changed, 67 insertions(+), 70 deletions(-) diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/InstallCommand.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/InstallCommand.java index aaf1d41331..6edab81823 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/InstallCommand.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/InstallCommand.java @@ -19,18 +19,15 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; -import java.nio.file.FileVisitResult; -import java.nio.file.FileVisitor; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import joptsimple.OptionSet; import joptsimple.OptionSpec; -import org.codehaus.plexus.util.FileUtils; import org.springframework.boot.cli.command.Command; import org.springframework.boot.cli.command.OptionParsingCommand; import org.springframework.boot.cli.command.options.CompilerOptionHandler; @@ -41,36 +38,38 @@ import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; import org.springframework.boot.cli.util.Log; +import org.springframework.util.FileCopyUtils; import org.springframework.util.SystemPropertyUtils; /** * {@link Command} to install additional dependencies into the CLI. * * @author Dave Syer + * @author Andy Wilkinson */ public class InstallCommand extends OptionParsingCommand { public static Command install() { return new InstallCommand("install", "Install dependencies to lib directory", - new InstallFileVisitorFactory()); + new InstallFileProcessorFactory()); } public static Command uninstall() { return new InstallCommand("uninstall", "Uninstall dependencies from a lib directory", - new UninstallFileVisitorFactory()); + new UninstallFileProcessorFactory()); } - private InstallCommand(String name, String description, FileVisitorFactory visitor) { + private InstallCommand(String name, String description, FileProcessorFactory visitor) { super(name, description, new InstallOptionHandler(visitor)); } private static final class InstallOptionHandler extends CompilerOptionHandler { - private FileVisitorFactory factory; + private FileProcessorFactory factory; private OptionSpec allOption; - public InstallOptionHandler(FileVisitorFactory factory) { + public InstallOptionHandler(FileProcessorFactory factory) { this.factory = factory; } @@ -111,43 +110,60 @@ public class InstallCommand extends OptionParsingCommand { } }; - Path tmpdir = Files.createTempDirectory("SpringInstallCommand") - .toAbsolutePath(); - String grapeRoot = System.getProperty("grape.root"); - System.setProperty("grape.root", tmpdir.toString()); - GroovyCompiler groovyCompiler = new GroovyCompiler(configuration); try { if (!args.isEmpty()) { + List initialUrls = getClassPathUrls(groovyCompiler); groovyCompiler.compile(createSources(args)); - installJars(tmpdir); + List urlsToProcessor = getClassPathUrls(groovyCompiler); + urlsToProcessor.removeAll(initialUrls); + + processJars(urlsToProcessor); } - FileUtils.deleteDirectory(tmpdir.toFile()); } catch (Exception ex) { String message = ex.getMessage(); Log.error(message != null ? message : ex.getClass().toString()); } - finally { - if (grapeRoot != null) { - System.setProperty("grape.root", grapeRoot); - } - else { - System.clearProperty("grape.root"); - } - } return ExitStatus.OK; } - private void installJars(Path tmpdir) throws IOException { + private List getClassPathUrls(GroovyCompiler compiler) { + return new ArrayList(Arrays.asList(compiler.getLoader().getURLs())); + } + + private void processJars(List urlsToProcess) throws IOException { File lib = getDefaultLibDirectory(); - Files.walkFileTree(tmpdir, this.factory.visitor(lib)); + + FileProcessor processor = this.factory.processor(lib); + + for (URL url : urlsToProcess) { + File file = toFile(url); + if (file.getName().endsWith(".jar")) { + processor.processFile(file); + } + } + } + + private File toFile(URL url) { + try { + return new File(url.toURI()); + } + catch (URISyntaxException ex) { + return new File(url.getPath()); + } } private void uninstallAllJars() throws IOException { File lib = getDefaultLibDirectory(); - Files.walkFileTree(lib.toPath(), new DeleteNotTheCliVisitor()); + File[] filesInLib = lib.listFiles(); + if (filesInLib != null) { + FileProcessor processor = new DeleteNotTheCliProcessor(); + for (File file : filesInLib) { + processor.processFile(file); + } + } } private String createSources(List args) throws IOException { @@ -177,70 +193,51 @@ public class InstallCommand extends OptionParsingCommand { } - private interface FileVisitorFactory { - FileVisitor visitor(File lib); + private interface FileProcessorFactory { + FileProcessor processor(File lib); + } + + private interface FileProcessor { + void processFile(File file) throws IOException; } - private static class DeleteNotTheCliVisitor extends SimpleFileVisitor { + private static class DeleteNotTheCliProcessor implements FileProcessor { @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - String name = file.getFileName().toString(); - if (name.endsWith(".jar") && !name.startsWith("spring-boot-cli")) { - file.toFile().delete(); + public void processFile(File file) throws IOException { + if (!file.getName().startsWith("spring-boot-cli")) { + file.delete(); } - return FileVisitResult.CONTINUE; } + } - private static class InstallFileVisitorFactory implements FileVisitorFactory { + private static class InstallFileProcessorFactory implements FileProcessorFactory { @Override - public SimpleFileVisitor visitor(final File lib) { + public FileProcessor processor(final File lib) { Log.info("Installing into: " + lib); lib.mkdirs(); - return new SimpleFileVisitor() { + return new FileProcessor() { @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - if (file.getFileName().toString().endsWith(".jar")) { - Files.copy(file, new FileOutputStream(new File(lib, file - .getFileName().toString()))); - return FileVisitResult.SKIP_SIBLINGS; - } - return FileVisitResult.CONTINUE; + public void processFile(File file) throws IOException { + FileCopyUtils.copy(file, new File(lib, file.getName())); } }; } } - private static class UninstallFileVisitorFactory implements FileVisitorFactory { + private static class UninstallFileProcessorFactory implements FileProcessorFactory { @Override - public SimpleFileVisitor visitor(final File lib) { + public FileProcessor processor(final File lib) { Log.info("Uninstalling from: " + lib); - if (!lib.exists()) { - return new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - return FileVisitResult.TERMINATE; - } - }; - } - return new SimpleFileVisitor() { + return new FileProcessor() { @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - if (file.getFileName().toString().endsWith(".jar")) { - new File(lib, file.getFileName().toString()).delete(); - return FileVisitResult.SKIP_SIBLINGS; - } - return FileVisitResult.CONTINUE; + public void processFile(File file) throws IOException { + new File(lib, file.getName()).delete(); } }; - } }