diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java index dbbb7fb758a..81c5c198bbe 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2016 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. @@ -16,6 +16,12 @@ package org.springframework.boot.cli; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; import java.util.ServiceLoader; import org.springframework.boot.cli.command.CommandFactory; @@ -25,6 +31,7 @@ import org.springframework.boot.cli.command.core.HintCommand; import org.springframework.boot.cli.command.core.VersionCommand; import org.springframework.boot.cli.command.shell.ShellCommand; import org.springframework.boot.loader.tools.LogbackInitializer; +import org.springframework.util.SystemPropertyUtils; /** * Spring Command Line Interface. This is the main entry-point for the Spring command line @@ -60,10 +67,34 @@ public final class SpringCli { private static void addServiceLoaderCommands(CommandRunner runner) { ServiceLoader factories = ServiceLoader.load(CommandFactory.class, - runner.getClass().getClassLoader()); + createCommandClassLoader(runner)); for (CommandFactory factory : factories) { runner.addCommands(factory.getCommands()); } } + private static URLClassLoader createCommandClassLoader(CommandRunner runner) { + return new URLClassLoader(getExtensionURLs(), runner.getClass().getClassLoader()); + } + + private static URL[] getExtensionURLs() { + List urls = new ArrayList(); + String home = SystemPropertyUtils + .resolvePlaceholders("${spring.home:${SPRING_HOME:.}}"); + File extDirectory = new File(new File(home, "lib"), "ext"); + if (extDirectory.isDirectory()) { + for (File file : extDirectory.listFiles()) { + if (file.getName().endsWith(".jar")) { + try { + urls.add(file.toURI().toURL()); + } + catch (MalformedURLException ex) { + throw new IllegalStateException(ex); + } + } + } + } + return urls.toArray(new URL[urls.size()]); + } + } 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 55b65a200b1..00922141402 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2016 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. @@ -37,7 +37,7 @@ import org.springframework.util.Assert; public class InstallCommand extends OptionParsingCommand { public InstallCommand() { - super("install", "Install dependencies to the lib directory", + super("install", "Install dependencies to the lib/ext directory", new InstallOptionHandler()); } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/Installer.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/Installer.java index 290aad7822c..e6021a30593 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/Installer.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/Installer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2016 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. @@ -92,7 +92,7 @@ class Installer { } public void install(List artifactIdentifiers) throws Exception { - File libDirectory = getDefaultLibDirectory(); + File libDirectory = getDefaultExtDirectory(); libDirectory.mkdirs(); Log.info("Installing into: " + libDirectory); List artifactFiles = this.dependencyResolver.resolve(artifactIdentifiers); @@ -125,13 +125,13 @@ class Installer { } public void uninstall(List artifactIdentifiers) throws Exception { - File libDirectory = getDefaultLibDirectory(); - Log.info("Uninstalling from: " + libDirectory); + File extDirectory = getDefaultExtDirectory(); + Log.info("Uninstalling from: " + extDirectory); List artifactFiles = this.dependencyResolver.resolve(artifactIdentifiers); for (File artifactFile : artifactFiles) { int installCount = getInstallCount(artifactFile); if (installCount <= 1) { - new File(libDirectory, artifactFile.getName()).delete(); + new File(extDirectory, artifactFile.getName()).delete(); } setInstallCount(artifactFile, installCount - 1); } @@ -139,23 +139,30 @@ class Installer { } public void uninstallAll() throws Exception { - File libDirectory = getDefaultLibDirectory(); - Log.info("Uninstalling from: " + libDirectory); + File extDirectory = getDefaultExtDirectory(); + Log.info("Uninstalling from: " + extDirectory); for (String name : this.installCounts.stringPropertyNames()) { - new File(libDirectory, name).delete(); + new File(extDirectory, name).delete(); } this.installCounts.clear(); saveInstallCounts(); } - private File getDefaultLibDirectory() { + private File getDefaultExtDirectory() { String home = SystemPropertyUtils .resolvePlaceholders("${spring.home:${SPRING_HOME:.}}"); - return new File(home, "lib"); + File extDirectory = new File(new File(home, "lib"), "ext"); + if (!extDirectory.isDirectory()) { + if (!extDirectory.mkdirs()) { + throw new IllegalStateException( + "Failed to create ext directory " + extDirectory); + } + } + return extDirectory; } private File getInstalled() { - return new File(getDefaultLibDirectory(), ".installed"); + return new File(getDefaultExtDirectory(), ".installed"); } } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/UninstallCommand.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/UninstallCommand.java index 92d2338bf02..f7ccd54f9b8 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/UninstallCommand.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/install/UninstallCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2016 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. @@ -28,7 +28,7 @@ import org.springframework.boot.cli.command.status.ExitStatus; import org.springframework.boot.cli.util.Log; /** - * {@link Command} to uninstall dependencies from the CLI's lib directory. + * {@link Command} to uninstall dependencies from the CLI's lib/ext directory. * * @author Dave Syer * @author Andy Wilkinson @@ -37,7 +37,7 @@ import org.springframework.boot.cli.util.Log; public class UninstallCommand extends OptionParsingCommand { public UninstallCommand() { - super("uninstall", "Uninstall dependencies from the lib directory", + super("uninstall", "Uninstall dependencies from the lib/ext directory", new UninstallOptionHandler()); } diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/install/InstallerTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/install/InstallerTests.java index 3b41f127973..2003c106589 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/install/InstallerTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/install/InstallerTests.java @@ -66,7 +66,7 @@ public class InstallerTests { File foo = createTemporaryFile("foo.jar"); given(this.resolver.resolve(Arrays.asList("foo"))).willReturn(Arrays.asList(foo)); this.installer.install(Arrays.asList("foo")); - assertThat(getNamesOfFilesInLib()).containsOnly("foo.jar", ".installed"); + assertThat(getNamesOfFilesInLibExt()).containsOnly("foo.jar", ".installed"); } @Test @@ -75,7 +75,7 @@ public class InstallerTests { given(this.resolver.resolve(Arrays.asList("foo"))).willReturn(Arrays.asList(foo)); this.installer.install(Arrays.asList("foo")); this.installer.uninstall(Arrays.asList("foo")); - assertThat(getNamesOfFilesInLib()).contains(".installed"); + assertThat(getNamesOfFilesInLibExt()).contains(".installed"); } @Test @@ -88,16 +88,16 @@ public class InstallerTests { given(this.resolver.resolve(Arrays.asList("charlie"))) .willReturn(Arrays.asList(charlie, alpha)); this.installer.install(Arrays.asList("bravo")); - assertThat(getNamesOfFilesInLib()).containsOnly("alpha.jar", "bravo.jar", + assertThat(getNamesOfFilesInLibExt()).containsOnly("alpha.jar", "bravo.jar", ".installed"); this.installer.install(Arrays.asList("charlie")); - assertThat(getNamesOfFilesInLib()).containsOnly("alpha.jar", "bravo.jar", + assertThat(getNamesOfFilesInLibExt()).containsOnly("alpha.jar", "bravo.jar", "charlie.jar", ".installed"); this.installer.uninstall(Arrays.asList("bravo")); - assertThat(getNamesOfFilesInLib()).containsOnly("alpha.jar", "charlie.jar", + assertThat(getNamesOfFilesInLibExt()).containsOnly("alpha.jar", "charlie.jar", ".installed"); this.installer.uninstall(Arrays.asList("charlie")); - assertThat(getNamesOfFilesInLib()).containsOnly(".installed"); + assertThat(getNamesOfFilesInLibExt()).containsOnly(".installed"); } @Test @@ -111,15 +111,15 @@ public class InstallerTests { .willReturn(Arrays.asList(charlie, alpha)); this.installer.install(Arrays.asList("bravo")); this.installer.install(Arrays.asList("charlie")); - assertThat(getNamesOfFilesInLib()).containsOnly("alpha.jar", "bravo.jar", + assertThat(getNamesOfFilesInLibExt()).containsOnly("alpha.jar", "bravo.jar", "charlie.jar", ".installed"); this.installer.uninstallAll(); - assertThat(getNamesOfFilesInLib()).containsOnly(".installed"); + assertThat(getNamesOfFilesInLibExt()).containsOnly(".installed"); } - private Set getNamesOfFilesInLib() { + private Set getNamesOfFilesInLibExt() { Set names = new HashSet(); - for (File file : new File("target/lib").listFiles()) { + for (File file : new File("target/lib/ext").listFiles()) { names.add(file.getName()); } return names;