Preserve dependencies when uninstalling from the CLI
Previously, the CLI did not keep track of a dependency's users. This meant that installing two extensions with a common dependency and then unistalling one extension would break the other extension as the common dependency would be deleted: 1. Install a that depends on c 2. Install b that depends on c 3. Uninstall b 4. a is now broken as c has been deleted This commit updates the CLI to maintain a count for each artifact that's installed into /lib. An artifact is now only deleted when the count reaches zero. As part of this change the code has been extensively refactored to bring the structure into line with other CLI commands and to improve testability. Closes gh-1410
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.command.install;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
|
||||
import org.springframework.boot.cli.compiler.GroovyCompilerScope;
|
||||
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory;
|
||||
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
|
||||
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link GroovyGrabDependencyResolver}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class GroovyGrabDependencyResolverTests {
|
||||
|
||||
private DependencyResolver resolver;
|
||||
|
||||
@Before
|
||||
public void setupResolver() {
|
||||
GroovyCompilerConfiguration configuration = new GroovyCompilerConfiguration() {
|
||||
|
||||
@Override
|
||||
public boolean isGuessImports() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuessDependencies() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoconfigure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroovyCompilerScope getScope() {
|
||||
return GroovyCompilerScope.DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RepositoryConfiguration> getRepositoryConfiguration() {
|
||||
return RepositoryConfigurationFactory
|
||||
.createDefaultRepositoryConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getClasspath() {
|
||||
return new String[] { "." };
|
||||
}
|
||||
};
|
||||
this.resolver = new GroovyGrabDependencyResolver(configuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArtifactWithNoDependencies() throws Exception {
|
||||
List<File> resolved = this.resolver.resolve(Arrays
|
||||
.asList("commons-logging:commons-logging:1.1.3"));
|
||||
assertThat(resolved, hasSize(1));
|
||||
assertThat(getNames(resolved), hasItems("commons-logging-1.1.3.jar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArtifactWithDependencies() throws Exception {
|
||||
List<File> resolved = this.resolver.resolve(Arrays
|
||||
.asList("org.springframework:spring-core:4.1.1.RELEASE"));
|
||||
assertThat(resolved, hasSize(2));
|
||||
assertThat(getNames(resolved),
|
||||
hasItems("commons-logging-1.1.3.jar", "spring-core-4.1.1.RELEASE.jar"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void resolveShorthandArtifactWithDependencies() throws Exception {
|
||||
List<File> resolved = this.resolver.resolve(Arrays.asList("spring-core"));
|
||||
assertThat(resolved, hasSize(2));
|
||||
assertThat(getNames(resolved),
|
||||
hasItems(startsWith("commons-logging-"), startsWith("spring-core-")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMultipleArtifacts() throws Exception {
|
||||
List<File> resolved = this.resolver.resolve(Arrays.asList("junit:junit:4.11",
|
||||
"commons-logging:commons-logging:1.1.3"));
|
||||
assertThat(resolved, hasSize(3));
|
||||
assertThat(
|
||||
getNames(resolved),
|
||||
hasItems("junit-4.11.jar", "commons-logging-1.1.3.jar",
|
||||
"hamcrest-core-1.3.jar"));
|
||||
}
|
||||
|
||||
public Set<String> getNames(Collection<File> files) {
|
||||
Set<String> names = new HashSet<String>(files.size());
|
||||
for (File file : files) {
|
||||
names.add(file.getName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.command.install;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link Installer}
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class InstallerTests {
|
||||
|
||||
public DependencyResolver resolver = mock(DependencyResolver.class);
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
private Installer installer;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
System.setProperty("spring.home", "target");
|
||||
FileSystemUtils.deleteRecursively(new File("target/lib"));
|
||||
|
||||
this.installer = new Installer(this.resolver);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
System.clearProperty("spring.home");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installNewDependency() throws Exception {
|
||||
File foo = createTemporaryFile("foo.jar");
|
||||
|
||||
when(this.resolver.resolve(Arrays.asList("foo"))).thenReturn(Arrays.asList(foo));
|
||||
this.installer.install(Arrays.asList("foo"));
|
||||
|
||||
assertThat(getNamesOfFilesInLib(), containsInAnyOrder("foo.jar", ".installed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installAndUninstall() throws Exception {
|
||||
File foo = createTemporaryFile("foo.jar");
|
||||
|
||||
when(this.resolver.resolve(Arrays.asList("foo"))).thenReturn(Arrays.asList(foo));
|
||||
|
||||
this.installer.install(Arrays.asList("foo"));
|
||||
this.installer.uninstall(Arrays.asList("foo"));
|
||||
|
||||
assertThat(getNamesOfFilesInLib(), contains(".installed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installAndUninstallWithCommonDependencies() throws Exception {
|
||||
File alpha = createTemporaryFile("alpha.jar");
|
||||
File bravo = createTemporaryFile("bravo.jar");
|
||||
File charlie = createTemporaryFile("charlie.jar");
|
||||
|
||||
when(this.resolver.resolve(Arrays.asList("bravo"))).thenReturn(
|
||||
Arrays.asList(bravo, alpha));
|
||||
|
||||
when(this.resolver.resolve(Arrays.asList("charlie"))).thenReturn(
|
||||
Arrays.asList(charlie, alpha));
|
||||
|
||||
this.installer.install(Arrays.asList("bravo"));
|
||||
|
||||
assertThat(getNamesOfFilesInLib(),
|
||||
containsInAnyOrder("alpha.jar", "bravo.jar", ".installed"));
|
||||
|
||||
this.installer.install(Arrays.asList("charlie"));
|
||||
|
||||
assertThat(getNamesOfFilesInLib(),
|
||||
containsInAnyOrder("alpha.jar", "bravo.jar", "charlie.jar", ".installed"));
|
||||
|
||||
this.installer.uninstall(Arrays.asList("bravo"));
|
||||
|
||||
assertThat(getNamesOfFilesInLib(),
|
||||
containsInAnyOrder("alpha.jar", "charlie.jar", ".installed"));
|
||||
|
||||
this.installer.uninstall(Arrays.asList("charlie"));
|
||||
|
||||
assertThat(getNamesOfFilesInLib(), containsInAnyOrder(".installed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uninstallAll() throws Exception {
|
||||
File alpha = createTemporaryFile("alpha.jar");
|
||||
File bravo = createTemporaryFile("bravo.jar");
|
||||
File charlie = createTemporaryFile("charlie.jar");
|
||||
|
||||
when(this.resolver.resolve(Arrays.asList("bravo"))).thenReturn(
|
||||
Arrays.asList(bravo, alpha));
|
||||
|
||||
when(this.resolver.resolve(Arrays.asList("charlie"))).thenReturn(
|
||||
Arrays.asList(charlie, alpha));
|
||||
|
||||
this.installer.install(Arrays.asList("bravo"));
|
||||
this.installer.install(Arrays.asList("charlie"));
|
||||
|
||||
assertThat(getNamesOfFilesInLib(),
|
||||
containsInAnyOrder("alpha.jar", "bravo.jar", "charlie.jar", ".installed"));
|
||||
|
||||
this.installer.uninstallAll();
|
||||
|
||||
assertThat(getNamesOfFilesInLib(), containsInAnyOrder(".installed"));
|
||||
}
|
||||
|
||||
private Set<String> getNamesOfFilesInLib() {
|
||||
Set<String> names = new HashSet<String>();
|
||||
for (File file : new File("target/lib").listFiles()) {
|
||||
names.add(file.getName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private File createTemporaryFile(String name) throws IOException {
|
||||
File temporaryFile = this.tempFolder.newFile(name);
|
||||
temporaryFile.deleteOnExit();
|
||||
return temporaryFile;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user