diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2013-11-26 15:28:00 +0100 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2013-11-26 15:28:46 +0100 |
commit | 25fbc1708b8cc7787db574ff4f124b706930802d (patch) | |
tree | aa0fb1a3e0d7350a9f04049dff9de891913a2c2e /sonar-batch | |
parent | 145913a4689d0e95f41ddecff499582607cf4b36 (diff) | |
download | sonarqube-25fbc1708b8cc7787db574ff4f124b706930802d.tar.gz sonarqube-25fbc1708b8cc7787db574ff4f124b706930802d.zip |
SONAR-4830 Extract plugin dependencies in the cache
Diffstat (limited to 'sonar-batch')
6 files changed, 179 insertions, 37 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java new file mode 100644 index 00000000000..64086ef1cbb --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java @@ -0,0 +1,76 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import org.sonar.api.BatchComponent; +import org.sonar.api.utils.ZipUtils; +import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.core.plugins.PluginInstaller; +import org.sonar.home.cache.FileCache; + +import java.io.File; +import java.io.IOException; +import java.util.zip.ZipEntry; + +public class BatchPluginInstaller extends PluginInstaller implements BatchComponent { + + private FileCache cache; + + public BatchPluginInstaller(FileCache cache) { + this.cache = cache; + } + + public DefaultPluginMetadata installToCache(File pluginFile, boolean isCore) { + DefaultPluginMetadata metadata = extractMetadata(pluginFile, isCore); + install(metadata, null, pluginFile); + return metadata; + } + + @Override + protected File extractPluginDependencies(File pluginFile, File pluginBasedir) throws IOException { + return cache.unzip(pluginFile); + } + + private void copyDependencies(DefaultPluginMetadata metadata, File pluginFile, File pluginBasedir) throws IOException { + if (!metadata.getPathsToInternalDeps().isEmpty()) { + // needs to unzip the jar + File baseDir; + if (pluginBasedir == null) { + baseDir = cache.unzip(pluginFile); + } else { + ZipUtils.unzip(pluginFile, pluginBasedir, new LibFilter()); + baseDir = pluginBasedir; + } + for (String depPath : metadata.getPathsToInternalDeps()) { + File dependency = new File(baseDir, depPath); + if (!dependency.isFile() || !dependency.exists()) { + throw new IllegalArgumentException("Dependency " + depPath + " can not be found in " + pluginFile.getName()); + } + metadata.addDeployedFile(dependency); + } + } + } + + private static final class LibFilter implements ZipUtils.ZipEntryFilter { + public boolean accept(ZipEntry entry) { + return entry.getName().startsWith("META-INF/lib"); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java index bcc4c0afd9c..b0ebab822af 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java @@ -30,9 +30,7 @@ import org.sonar.api.Plugin; import org.sonar.api.config.Settings; import org.sonar.api.platform.PluginMetadata; import org.sonar.api.platform.PluginRepository; -import org.sonar.api.utils.TempFolder; import org.sonar.core.plugins.PluginClassloaders; -import org.sonar.core.plugins.PluginInstaller; import org.sonar.core.plugins.RemotePlugin; import java.io.File; @@ -54,14 +52,15 @@ public class BatchPluginRepository implements PluginRepository { private Map<String, PluginMetadata> metadataByKey; private Settings settings; private PluginClassloaders classLoaders; - private TempFolder tempDirectories; private final AnalysisMode analysisMode; + private final BatchPluginInstaller pluginInstaller; - public BatchPluginRepository(PluginDownloader pluginDownloader, TempFolder tempDirectories, Settings settings, AnalysisMode analysisMode) { + public BatchPluginRepository(PluginDownloader pluginDownloader, Settings settings, AnalysisMode analysisMode, + BatchPluginInstaller pluginInstaller) { this.pluginDownloader = pluginDownloader; - this.tempDirectories = tempDirectories; this.settings = settings; this.analysisMode = analysisMode; + this.pluginInstaller = pluginInstaller; } public void start() { @@ -71,14 +70,11 @@ public class BatchPluginRepository implements PluginRepository { void doStart(List<RemotePlugin> remotePlugins) { PluginFilter filter = new PluginFilter(settings, analysisMode); - PluginInstaller extractor = new PluginInstaller(); metadataByKey = Maps.newHashMap(); for (RemotePlugin remote : remotePlugins) { if (filter.accepts(remote.getKey())) { File pluginFile = pluginDownloader.downloadPlugin(remote); - File targetDir = tempDirectories.newDir("plugins/" + remote.getKey()); - LOG.debug("Installing plugin {} into {}", remote.getKey(), targetDir.getAbsolutePath()); - PluginMetadata metadata = extractor.install(pluginFile, remote.isCore(), targetDir); + PluginMetadata metadata = pluginInstaller.installToCache(pluginFile, remote.isCore()); if (StringUtils.isBlank(metadata.getBasePlugin()) || filter.accepts(metadata.getBasePlugin())) { metadataByKey.put(metadata.getKey(), metadata); } else { diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java index 13bb325e89d..502c092e8dc 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java @@ -82,6 +82,7 @@ public class BootstrapContainer extends ComponentContainer { AnalysisMode.class, PluginDownloader.class, BatchPluginRepository.class, + BatchPluginInstaller.class, BatchSettings.class, ServerClient.class, ExtensionInstaller.class, diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java new file mode 100644 index 00000000000..eb35381c476 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrap; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.home.cache.FileCacheBuilder; + +import java.io.File; +import java.io.IOException; + +import static org.fest.assertions.Assertions.assertThat; + +public class BatchPluginInstallerTest { + + private BatchPluginInstaller extractor; + + @ClassRule + public static TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private File userHome; + + @Before + public void setUp() throws IOException { + userHome = temporaryFolder.newFolder(); + extractor = new BatchPluginInstaller(new FileCacheBuilder().setUserHome(userHome).build()); + } + + @Test + public void should_copy_and_extract_dependencies() throws IOException { + File fileFromCache = getFileFromCache("sonar-checkstyle-plugin-2.8.jar"); + DefaultPluginMetadata metadata = extractor.installToCache(fileFromCache, true); + + assertThat(metadata.getKey()).isEqualTo("checkstyle"); + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar")).exists(); + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/META-INF/lib/checkstyle-5.1.jar")).exists(); + } + + @Test + public void should_extract_only_dependencies() throws IOException { + File fileFromCache = getFileFromCache("sonar-checkstyle-plugin-2.8.jar"); + extractor.installToCache(fileFromCache, true); + + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar")).exists(); + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/META-INF/MANIFEST.MF")).doesNotExist(); + assertThat(new File(fileFromCache.getParent(), "sonar-checkstyle-plugin-2.8.jar_unzip/org/sonar/plugins/checkstyle/CheckstyleVersion.class")).doesNotExist(); + } + + File getFileFromCache(String filename) throws IOException { + File src = FileUtils.toFile(BatchPluginInstallerTest.class.getResource("/org/sonar/batch/bootstrap/BatchPluginInstallerTest/" + filename)); + File destFile = new File(new File(userHome, "" + filename.hashCode()), filename); + FileUtils.copyFile(src, destFile); + return destFile; + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java index 6ab8e5182fb..bb947f8b8c5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java @@ -27,8 +27,9 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; -import org.sonar.api.utils.TempFolder; import org.sonar.core.plugins.RemotePlugin; +import org.sonar.home.cache.FileCache; +import org.sonar.home.cache.FileCacheBuilder; import org.sonar.test.TestUtils; import java.io.File; @@ -46,10 +47,14 @@ public class BatchPluginRepositoryTest { private BatchPluginRepository repository; private AnalysisMode mode; + private FileCache cache; + private File userHome; @Before - public void before() { + public void before() throws IOException { mode = mock(AnalysisMode.class); + userHome = temp.newFolder(); + cache = new FileCacheBuilder().setUserHome(userHome).build(); } @After @@ -61,15 +66,12 @@ public class BatchPluginRepositoryTest { @Test public void shouldLoadPlugin() throws IOException { - TempFolder tempDirs = mock(TempFolder.class); - File toDir = temp.newFolder(); - when(tempDirs.newDir("plugins/checkstyle")).thenReturn(toDir); RemotePlugin checkstyle = new RemotePlugin("checkstyle", true); PluginDownloader downloader = mock(PluginDownloader.class); - when(downloader.downloadPlugin(checkstyle)).thenReturn(copyFile("sonar-checkstyle-plugin-2.8.jar")); + when(downloader.downloadPlugin(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar")); - repository = new BatchPluginRepository(downloader, tempDirs, new Settings(), mode); + repository = new BatchPluginRepository(downloader, new Settings(), mode, new BatchPluginInstaller(cache)); repository.doStart(Arrays.asList(checkstyle)); @@ -81,19 +83,14 @@ public class BatchPluginRepositoryTest { @Test public void shouldLoadPluginExtension() throws IOException { - TempFolder tempDirs = mock(TempFolder.class); - File toDir1 = temp.newFolder(); - File toDir2 = temp.newFolder(); - when(tempDirs.newDir("plugins/checkstyle")).thenReturn(toDir1); - when(tempDirs.newDir("plugins/checkstyleextensions")).thenReturn(toDir2); RemotePlugin checkstyle = new RemotePlugin("checkstyle", true); RemotePlugin checkstyleExt = new RemotePlugin("checkstyleextensions", false); PluginDownloader downloader = mock(PluginDownloader.class); - when(downloader.downloadPlugin(checkstyle)).thenReturn(copyFile("sonar-checkstyle-plugin-2.8.jar")); - when(downloader.downloadPlugin(checkstyleExt)).thenReturn(copyFile("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar")); + when(downloader.downloadPlugin(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar")); + when(downloader.downloadPlugin(checkstyleExt)).thenReturn(fileFromCache("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar")); - repository = new BatchPluginRepository(downloader, tempDirs, new Settings(), mode); + repository = new BatchPluginRepository(downloader, new Settings(), mode, new BatchPluginInstaller(cache)); repository.doStart(Arrays.asList(checkstyle, checkstyleExt)); @@ -106,33 +103,28 @@ public class BatchPluginRepositoryTest { @Test public void shouldExcludePluginAndItsExtensions() throws IOException { - TempFolder tempDirs = mock(TempFolder.class); - File toDir1 = temp.newFolder(); - File toDir2 = temp.newFolder(); - when(tempDirs.newDir("plugins/checkstyle")).thenReturn(toDir1); - when(tempDirs.newDir("plugins/checkstyleextensions")).thenReturn(toDir2); RemotePlugin checkstyle = new RemotePlugin("checkstyle", true); RemotePlugin checkstyleExt = new RemotePlugin("checkstyleextensions", false); PluginDownloader downloader = mock(PluginDownloader.class); - when(downloader.downloadPlugin(checkstyle)).thenReturn(copyFile("sonar-checkstyle-plugin-2.8.jar")); - when(downloader.downloadPlugin(checkstyleExt)).thenReturn(copyFile("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar")); + when(downloader.downloadPlugin(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar")); + when(downloader.downloadPlugin(checkstyleExt)).thenReturn(fileFromCache("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar")); Settings settings = new Settings(); settings.setProperty(CoreProperties.BATCH_EXCLUDE_PLUGINS, "checkstyle"); - repository = new BatchPluginRepository(downloader, tempDirs, settings, mode); + repository = new BatchPluginRepository(downloader, settings, mode, new BatchPluginInstaller(cache)); repository.doStart(Arrays.asList(checkstyle, checkstyleExt)); assertThat(repository.getMetadata()).isEmpty(); } - private File copyFile(String filename) throws IOException { + private File fileFromCache(String filename) throws IOException { File file = TestUtils.getResource("/org/sonar/batch/bootstrap/BatchPluginRepositoryTest/" + filename); - File tempDir = new File("target/test-tmp/BatchPluginRepositoryTest"); - FileUtils.forceMkdir(tempDir); - FileUtils.copyFileToDirectory(file, tempDir); - return new File(tempDir, filename); + File destDir = new File(userHome, "cache/foomd5"); + FileUtils.forceMkdir(destDir); + FileUtils.copyFileToDirectory(file, destDir); + return new File(destDir, filename); } @Test diff --git a/sonar-batch/src/test/resources/org/sonar/batch/bootstrap/BatchPluginInstallerTest/sonar-checkstyle-plugin-2.8.jar b/sonar-batch/src/test/resources/org/sonar/batch/bootstrap/BatchPluginInstallerTest/sonar-checkstyle-plugin-2.8.jar Binary files differnew file mode 100644 index 00000000000..f937399bec5 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/bootstrap/BatchPluginInstallerTest/sonar-checkstyle-plugin-2.8.jar |