import org.sonar.api.utils.HttpDownloader;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.ServerMetadata;
+import org.sonar.core.plugins.RemotePlugin;
import java.io.File;
import java.net.URI;
}
}
- public File downloadPlugin(RemotePluginLocation remote) {
- File targetFile = new File(workingDirectories.getDir("plugins/" + remote.getPluginKey()), remote.getFilename());
- String url = baseUrl + "/deploy/plugins/" + remote.getRemotePath();
+ public List<File> downloadPlugin(RemotePlugin remote) {
try {
- FileUtils.forceMkdir(targetFile.getParentFile());
- LOG.info("Download plugin to " + targetFile);
- httpDownloader.download(new URI(url), targetFile);
- return targetFile;
+ File targetDir = workingDirectories.getDir("plugins/" + remote.getKey());
+ FileUtils.forceMkdir(targetDir);
+ LOG.info("Downloading plugin " + remote.getKey() + " into " + targetDir);
+
+ List<File> files = Lists.newArrayList();
+ for (String filename : remote.getFilenames()) {
+ String url = baseUrl + "/deploy/plugins/" + remote.getKey() + "/" + filename;
+ File toFile = new File(targetDir, filename);
+ httpDownloader.download(new URI(url), toFile);
+ files.add(toFile);
+ }
+
+
+ return files;
} catch (Exception e) {
- throw new SonarException("Fail to download extension: " + url, e);
+ throw new SonarException("Fail to download plugin: " + remote.getKey(), e);
}
}
- public List<RemotePluginLocation> downloadPluginIndex() {
+ public List<RemotePlugin> downloadPluginIndex() {
String url = baseUrl + "/deploy/plugins/index.txt";
try {
String indexContent = httpDownloader.downloadPlainText(new URI(url), "UTF-8");
String[] rows = StringUtils.split(indexContent, CharUtils.LF);
- List<RemotePluginLocation> remoteLocations = Lists.newArrayList();
+ List<RemotePlugin> remoteLocations = Lists.newArrayList();
for (String row : rows) {
- remoteLocations.add(RemotePluginLocation.createFromRow(row));
+ remoteLocations.add(RemotePlugin.unmarshal(row));
}
return remoteLocations;
}
}
- public static final class RemotePluginLocation {
- private String pluginKey;
- private String remotePath;
- private boolean core;
-
- private RemotePluginLocation(String pluginKey, String remotePath, boolean core) {
- this.pluginKey = pluginKey;
- this.remotePath = remotePath;
- this.core = core;
- }
-
- static RemotePluginLocation create(String key) {
- return new RemotePluginLocation(key, null, false);
- }
-
- static RemotePluginLocation createFromRow(String row) {
- String[] fields = StringUtils.split(row, ",");
- return new RemotePluginLocation(fields[0], fields[1], Boolean.parseBoolean(fields[2]));
- }
-
- public String getPluginKey() {
- return pluginKey;
- }
-
- public String getRemotePath() {
- return remotePath;
- }
-
- public String getFilename() {
- return StringUtils.substringAfterLast(remotePath, "/");
- }
-
- public boolean isCore() {
- return core;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- RemotePluginLocation that = (RemotePluginLocation) o;
- return pluginKey.equals(that.pluginKey);
- }
-
- @Override
- public int hashCode() {
- return pluginKey.hashCode();
- }
- }
}
import org.sonar.api.platform.PluginRepository;
import org.sonar.core.plugins.PluginClassloaders;
import org.sonar.core.plugins.PluginFileExtractor;
+import org.sonar.core.plugins.RemotePlugin;
import java.io.File;
import java.util.*;
doStart(artifactDownloader.downloadPluginIndex());
}
- void doStart(List<ArtifactDownloader.RemotePluginLocation> remoteLocations) {
+ void doStart(List<RemotePlugin> remotePlugins) {
PluginFileExtractor extractor = new PluginFileExtractor();
metadataByKey = Maps.newHashMap();
- for (ArtifactDownloader.RemotePluginLocation remoteLocation : remoteLocations) {
- if (isAccepted(remoteLocation.getPluginKey())) {
- File pluginFile = artifactDownloader.downloadPlugin(remoteLocation);
- PluginMetadata metadata = extractor.installInSameLocation(pluginFile, remoteLocation.isCore());
+ for (RemotePlugin remote : remotePlugins) {
+ if (isAccepted(remote.getKey())) {
+ List<File> pluginFiles = artifactDownloader.downloadPlugin(remote);
+ List<File> extensionFiles = pluginFiles.subList(1, pluginFiles.size());
+ PluginMetadata metadata = extractor.installInSameLocation(pluginFiles.get(0), remote.isCore(), extensionFiles);
if (StringUtils.isBlank(metadata.getBasePlugin()) || isAccepted(metadata.getBasePlugin())) {
// TODO log when excluding plugin
metadataByKey.put(metadata.getKey(), metadata);
*/
package org.sonar.batch.bootstrap;
+import com.google.common.collect.Lists;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.codehaus.plexus.util.FileUtils;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Test;
import org.sonar.api.CoreProperties;
+import org.sonar.core.plugins.RemotePlugin;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
+import java.util.List;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@Test
public void shouldLoadPlugin() throws IOException {
- ArtifactDownloader.RemotePluginLocation checkstyleLocation = ArtifactDownloader.RemotePluginLocation.create("checkstyle");
+ RemotePlugin checkstyle = new RemotePlugin("checkstyle", true);
ArtifactDownloader downloader = mock(ArtifactDownloader.class);
- when(downloader.downloadPlugin(eq(checkstyleLocation))).thenReturn(copyFile("sonar-checkstyle-plugin-2.8.jar"));
+ when(downloader.downloadPlugin(checkstyle)).thenReturn(copyFiles("sonar-checkstyle-plugin-2.8.jar"));
repository = new BatchPluginRepository(downloader, new PropertiesConfiguration());
- repository.doStart(Arrays.asList(checkstyleLocation));
+ repository.doStart(Arrays.asList(checkstyle));
assertThat(repository.getPlugins().size(), Matchers.is(1));
assertThat(repository.getPlugin("checkstyle"), not(nullValue()));
assertThat(repository.getMetadata().size(), Matchers.is(1));
assertThat(repository.getMetadata("checkstyle").getName(), Matchers.is("Checkstyle"));
+ assertThat(repository.getMetadata("checkstyle").getDeployedFiles().size(), Matchers.is(4)); // plugin + 3 dependencies
}
@Test
public void shouldLoadPluginExtension() throws IOException {
- ArtifactDownloader.RemotePluginLocation checkstyleLocation = ArtifactDownloader.RemotePluginLocation.create("checkstyle");
- ArtifactDownloader.RemotePluginLocation checkstyleExtLocation = ArtifactDownloader.RemotePluginLocation.create("checkstyleextensions");
+ RemotePlugin checkstyle = new RemotePlugin("checkstyle", true);
+ RemotePlugin checkstyleExt = new RemotePlugin("checkstyleextensions", false);
ArtifactDownloader downloader = mock(ArtifactDownloader.class);
- when(downloader.downloadPlugin(eq(checkstyleLocation))).thenReturn(copyFile("sonar-checkstyle-plugin-2.8.jar"));
- when(downloader.downloadPlugin(eq(checkstyleExtLocation))).thenReturn(copyFile("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
+ when(downloader.downloadPlugin(checkstyle)).thenReturn(copyFiles("sonar-checkstyle-plugin-2.8.jar"));
+ when(downloader.downloadPlugin(checkstyleExt)).thenReturn(copyFiles("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
repository = new BatchPluginRepository(downloader, new PropertiesConfiguration());
- repository.doStart(Arrays.asList(checkstyleLocation, checkstyleExtLocation));
+ repository.doStart(Arrays.asList(checkstyle, checkstyleExt));
assertThat(repository.getPlugins().size(), Matchers.is(2));
assertThat(repository.getPlugin("checkstyle"), not(nullValue()));
assertThat(repository.getMetadata("checkstyleextensions").getVersion(), Matchers.is("0.1-SNAPSHOT"));
}
+ @Test
+ public void shouldLoadPluginDeprecatedExtensions() throws IOException {
+ RemotePlugin checkstyle = new RemotePlugin("checkstyle", true)
+ .addFilename("checkstyle-ext.xml");
+
+ ArtifactDownloader downloader = mock(ArtifactDownloader.class);
+ when(downloader.downloadPlugin(checkstyle)).thenReturn(copyFiles("sonar-checkstyle-plugin-2.8.jar", "checkstyle-ext.xml"));
+
+ repository = new BatchPluginRepository(downloader, new PropertiesConfiguration());
+
+ repository.doStart(Arrays.asList(checkstyle));
+
+ assertThat(repository.getPlugins().size(), Matchers.is(1));
+ assertThat(repository.getPlugin("checkstyle"), not(nullValue()));
+ assertThat(repository.getMetadata().size(), Matchers.is(1));
+ assertThat(repository.getMetadata("checkstyle").getName(), Matchers.is("Checkstyle"));
+ assertThat(repository.getMetadata("checkstyle").getDeployedFiles().size(), Matchers.is(5)); // plugin + 3 dependencies + 1 deprecated extension
+ }
+
@Test
public void shouldExcludePluginAndItsExtensions() throws IOException {
- ArtifactDownloader.RemotePluginLocation checkstyleLocation = ArtifactDownloader.RemotePluginLocation.create("checkstyle");
- ArtifactDownloader.RemotePluginLocation checkstyleExtLocation = ArtifactDownloader.RemotePluginLocation.create("checkstyleextensions");
+ RemotePlugin checkstyle = new RemotePlugin("checkstyle", true);
+ RemotePlugin checkstyleExt = new RemotePlugin("checkstyleextensions", false);
ArtifactDownloader downloader = mock(ArtifactDownloader.class);
- when(downloader.downloadPlugin(eq(checkstyleLocation))).thenReturn(copyFile("sonar-checkstyle-plugin-2.8.jar"));
- when(downloader.downloadPlugin(eq(checkstyleExtLocation))).thenReturn(copyFile("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
+ when(downloader.downloadPlugin(checkstyle)).thenReturn(copyFiles("sonar-checkstyle-plugin-2.8.jar"));
+ when(downloader.downloadPlugin(checkstyleExt)).thenReturn(copyFiles("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty(CoreProperties.EXCLUDE_PLUGINS, "checkstyle");
repository = new BatchPluginRepository(downloader, conf);
- repository.doStart(Arrays.asList(checkstyleLocation, checkstyleExtLocation));
+ repository.doStart(Arrays.asList(checkstyle, checkstyleExt));
assertThat(repository.getPlugins().size(), Matchers.is(0));
assertThat(repository.getMetadata().size(), Matchers.is(0));
}
- private File copyFile(String filename) throws IOException {
- File file = FileUtils.toFile(getClass().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);
+ private List<File> copyFiles(String... filenames) throws IOException {
+ List files = Lists.newArrayList();
+ for (String filename : filenames) {
+ File file = FileUtils.toFile(getClass().getResource("/org/sonar/batch/bootstrap/BatchPluginRepositoryTest/" + filename));
+ File tempDir = new File("target/test-tmp/BatchPluginRepositoryTest");
+ FileUtils.forceMkdir(tempDir);
+ FileUtils.copyFileToDirectory(file, tempDir);
+ files.add(new File(tempDir, filename));
+ }
+ return files;
}
--- /dev/null
+<fake/>
\ No newline at end of file
package org.sonar.core.plugins;
import com.google.common.collect.Lists;
-import org.apache.commons.collections.ComparatorUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.sonar.api.platform.PluginMetadata;
import java.io.File;
-import java.util.ArrayList;
import java.util.List;
public final class DefaultPluginMetadata implements PluginMetadata, Comparable<PluginMetadata> {
return this;
}
+ public DefaultPluginMetadata setDeprecatedExtensions(List<File> files) {
+ this.deprecatedExtensions = (files==null ? Lists.<File>newArrayList() : files);
+ return this;
+ }
+
public String[] getPathsToInternalDeps() {
return pathsToInternalDeps;
}
package org.sonar.core.plugins;
import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.StringUtils;
import org.sonar.api.Plugin;
import org.sonar.api.utils.SonarException;
import org.sonar.api.utils.ZipUtils;
import org.sonar.updatecenter.common.PluginKeyUtils;
import org.sonar.updatecenter.common.PluginManifest;
-import javax.swing.plaf.metal.MetalTabbedPaneUI;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
+import java.util.List;
import java.util.zip.ZipEntry;
public class PluginFileExtractor {
- public DefaultPluginMetadata installInSameLocation(File pluginFile, boolean isCore) {
- return install(pluginFile, isCore, null);
+ public DefaultPluginMetadata installInSameLocation(File pluginFile, boolean isCore, List<File> deprecatedExtensions) {
+ return install(pluginFile, isCore, deprecatedExtensions, null);
}
- public DefaultPluginMetadata install(File pluginFile, boolean isCore, File toDir) {
+ public DefaultPluginMetadata install(File pluginFile, boolean isCore, List<File> deprecatedExtensions, File toDir) {
DefaultPluginMetadata metadata = extractMetadata(pluginFile, isCore);
+ metadata.setDeprecatedExtensions(deprecatedExtensions);
return install(metadata, toDir);
}
for (File extension : metadata.getDeprecatedExtensions()) {
File toFile = new File(pluginBasedir, extension.getName());
- FileUtils.copyFile(extension, toFile);
+ if (!toFile.equals(extension)) {
+ FileUtils.copyFile(extension, toFile);
+ }
metadata.addDeployedFile(toFile);
}
// copy file in a temp directory because Windows+Oracle JVM Classloader lock the JAR file
File tempFile = File.createTempFile(pluginFile.getName(), null);
FileUtils.copyFile(pluginFile, tempFile);
-
+
URLClassLoader pluginClassLoader = URLClassLoader.newInstance(new URL[]{tempFile.toURI().toURL()}, getClass().getClassLoader());
Plugin pluginInstance = (Plugin) pluginClassLoader.loadClass(mainClass).newInstance();
metadata.setKey(PluginKeyUtils.sanitize(pluginInstance.getKey()));
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.plugins;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.File;
+import java.util.List;
+
+public class RemotePlugin {
+ private String pluginKey;
+ private List<String> filenames = Lists.newArrayList();
+ private boolean core;
+
+ public RemotePlugin(String pluginKey, boolean core) {
+ this.pluginKey = pluginKey;
+ this.core = core;
+ }
+
+ public static RemotePlugin create(DefaultPluginMetadata metadata) {
+ RemotePlugin result = new RemotePlugin(metadata.getKey(), metadata.isCore());
+ result.addFilename(metadata.getFile().getName());
+ for (File file : metadata.getDeprecatedExtensions()) {
+ result.addFilename(file.getName());
+ }
+ return result;
+ }
+
+ public static RemotePlugin unmarshal(String row) {
+ String[] fields = StringUtils.split(row, ",");
+ RemotePlugin result = new RemotePlugin(fields[0], Boolean.parseBoolean(fields[1]));
+ if (fields.length > 2) {
+ for (int index = 2; index < fields.length; index++) {
+ result.addFilename(fields[index]);
+ }
+ }
+ return result;
+ }
+
+ public String marshal() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(pluginKey).append(",");
+ sb.append(String.valueOf(core));
+ for (String filename : filenames) {
+ sb.append(",").append(filename);
+ }
+ return sb.toString();
+ }
+
+ public String getKey() {
+ return pluginKey;
+ }
+
+
+ public boolean isCore() {
+ return core;
+ }
+
+ public RemotePlugin addFilename(String s) {
+ filenames.add(s);
+ return this;
+ }
+
+ public List<String> getFilenames() {
+ return filenames;
+ }
+
+ public String getPluginFilename() {
+ return (filenames.size()>0 ? filenames.get(0) : null);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ RemotePlugin that = (RemotePlugin) o;
+ return pluginKey.equals(that.pluginKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return pluginKey.hashCode();
+ }
+}
FileUtils.forceMkdir(toDir);
FileUtils.cleanDirectory(toDir);
- DefaultPluginMetadata metadata = extractor.install(getFile("sonar-checkstyle-plugin-2.8.jar"), true, toDir);
+ DefaultPluginMetadata metadata = extractor.install(getFile("sonar-checkstyle-plugin-2.8.jar"), true, null, toDir);
assertThat(metadata.getKey(), is("checkstyle"));
assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true));
FileUtils.forceMkdir(toDir);
FileUtils.cleanDirectory(toDir);
- extractor.install(getFile("sonar-checkstyle-plugin-2.8.jar"), true, toDir);
+ extractor.install(getFile("sonar-checkstyle-plugin-2.8.jar"), true, null, toDir);
assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true));
assertThat(new File(toDir, "META-INF/MANIFEST.MF").exists(), is(false));
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.core.plugins;
+
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+
+public class RemotePluginTest {
+ @Test
+ public void shouldEqual() {
+ RemotePlugin clirr1 = new RemotePlugin("clirr", false);
+ RemotePlugin clirr2 = new RemotePlugin("clirr", false);
+ RemotePlugin checkstyle = new RemotePlugin("checkstyle", true);
+ assertThat(clirr1.equals(clirr2), is(true));
+ assertThat(clirr1.equals(clirr1), is(true));
+ assertThat(clirr1.equals(checkstyle), is(false));
+ }
+
+ @Test
+ public void shouldMarshal() {
+ RemotePlugin clirr = new RemotePlugin("clirr", false).addFilename("clirr-1.1.jar");
+ String text = clirr.marshal();
+ assertThat(text, is("clirr,false,clirr-1.1.jar"));
+ }
+
+ @Test
+ public void shouldMarshalDeprecatedExtensions() {
+ RemotePlugin checkstyle = new RemotePlugin("checkstyle", true);
+ checkstyle.addFilename("checkstyle-2.8.jar");
+ checkstyle.addFilename("ext.xml");
+ checkstyle.addFilename("ext.jar");
+
+ String text = checkstyle.marshal();
+ assertThat(text, is("checkstyle,true,checkstyle-2.8.jar,ext.xml,ext.jar"));
+ }
+
+ @Test
+ public void shouldUnmarshal() {
+ RemotePlugin clirr = RemotePlugin.unmarshal("clirr,false,clirr-1.1.jar");
+ assertThat(clirr.getKey(), is("clirr"));
+ assertThat(clirr.isCore(), is(false));
+ assertThat(clirr.getFilenames().size(), is(1));
+ assertThat(clirr.getFilenames().get(0), is("clirr-1.1.jar"));
+
+ }
+
+ @Test
+ public void shouldUnmarshalDeprecatedExtensions() {
+ RemotePlugin checkstyle = RemotePlugin.unmarshal("checkstyle,true,checkstyle-2.8.jar,ext.xml,ext.jar");
+ assertThat(checkstyle.getKey(), is("checkstyle"));
+ assertThat(checkstyle.isCore(), is(true));
+ assertThat(checkstyle.getFilenames().size(), is(3));
+ assertThat(checkstyle.getFilenames(), hasItems("checkstyle-2.8.jar", "ext.xml", "ext.jar"));
+ }
+}
import org.sonar.api.utils.TimeProfiler;
import org.sonar.core.plugins.DefaultPluginMetadata;
import org.sonar.core.plugins.PluginFileExtractor;
+import org.sonar.core.plugins.RemotePlugin;
import org.sonar.server.platform.DefaultServerFileSystem;
import org.sonar.server.platform.ServerStartException;
FileWriter writer = new FileWriter(indexFile, false);
try {
for (PluginMetadata metadata : pluginByKeys.values()) {
- writer.append(metadata.getKey()).append(",");
- writer.append(metadata.getKey()).append("/").append(metadata.getFile().getName()).append(",");
- writer.append(String.valueOf(metadata.isCore())).append(CharUtils.LF);
+ writer.append(RemotePlugin.create((DefaultPluginMetadata)metadata).marshal());
+ writer.append(CharUtils.LF);
}
writer.flush();