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
pull/1519/head
Andy Wilkinson 10 years ago committed by Andy Wilkinson
parent 84249da941
commit f5ab0cf2dc

@ -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<Void> 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<URL> initialUrls = getClassPathUrls(groovyCompiler);
groovyCompiler.compile(createSources(args));
installJars(tmpdir);
List<URL> 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<URL> getClassPathUrls(GroovyCompiler compiler) {
return new ArrayList<URL>(Arrays.asList(compiler.getLoader().getURLs()));
}
private void processJars(List<URL> 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<String> args) throws IOException {
@ -177,70 +193,51 @@ public class InstallCommand extends OptionParsingCommand {
}
private interface FileVisitorFactory {
FileVisitor<Path> 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<Path> {
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<Path> visitor(final File lib) {
public FileProcessor processor(final File lib) {
Log.info("Installing into: " + lib);
lib.mkdirs();
return new SimpleFileVisitor<Path>() {
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<Path> visitor(final File lib) {
public FileProcessor processor(final File lib) {
Log.info("Uninstalling from: " + lib);
if (!lib.exists()) {
return new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
return FileVisitResult.TERMINATE;
}
};
}
return new SimpleFileVisitor<Path>() {
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();
}
};
}
}

Loading…
Cancel
Save