* Upgrade to sonar-update-center-common 0.3-SNAPSHOT * Add column 'child_first_classloader' to table 'plugins' * Add possibility to use child-first classloadertags/2.6
@@ -386,7 +386,7 @@ mmmm | |||
<dependency> | |||
<groupId>org.codehaus.sonar</groupId> | |||
<artifactId>sonar-update-center-common</artifactId> | |||
<version>0.2</version> | |||
<version>0.3-SNAPSHOT</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.codehaus.sonar</groupId> |
@@ -38,11 +38,11 @@ import org.sonar.core.plugin.JpaPlugin; | |||
import org.sonar.core.plugin.JpaPluginDao; | |||
import org.sonar.core.plugin.JpaPluginFile; | |||
import com.google.common.collect.HashMultimap; | |||
import com.google.common.collect.Lists; | |||
import java.net.MalformedURLException; | |||
import java.net.URL; | |||
import java.util.Set; | |||
import java.util.List; | |||
public class BatchPluginRepository extends AbstractPluginRepository { | |||
@@ -66,30 +66,25 @@ public class BatchPluginRepository extends AbstractPluginRepository { | |||
} | |||
public void start() { | |||
HashMultimap<String, URL> urlsByKey = HashMultimap.create(); | |||
for (JpaPluginFile pluginFile : dao.getPluginFiles()) { | |||
try { | |||
String key = pluginFile.getPluginKey(); | |||
URL url = new URL(baseUrl + pluginFile.getPath()); | |||
urlsByKey.put(key, url); | |||
} catch (MalformedURLException e) { | |||
throw new SonarException("Can not build the classloader of the plugin " + pluginFile.getPluginKey(), e); | |||
} | |||
} | |||
classLoaders = new ClassLoadersCollection(Thread.currentThread().getContextClassLoader()); | |||
for (String key : urlsByKey.keySet()) { | |||
Set<URL> urls = urlsByKey.get(key); | |||
for (JpaPlugin pluginMetadata : dao.getPlugins()) { | |||
String key = pluginMetadata.getKey(); | |||
List<URL> urls = Lists.newArrayList(); | |||
for (JpaPluginFile pluginFile : pluginMetadata.getFiles()) { | |||
try { | |||
URL url = new URL(baseUrl + pluginFile.getPath()); | |||
urls.add(url); | |||
} catch (MalformedURLException e) { | |||
throw new SonarException("Can not build the classloader of the plugin " + pluginFile.getPluginKey(), e); | |||
} | |||
} | |||
if (LOG.isDebugEnabled()) { | |||
LOG.debug("Classloader of plugin " + key + ":"); | |||
for (URL url : urls) { | |||
LOG.debug(" -> " + url); | |||
} | |||
} | |||
classLoaders.createClassLoader(key, urls); | |||
classLoaders.createClassLoader(key, urls, pluginMetadata.isUseChildFirstClassLoader() == Boolean.TRUE); | |||
} | |||
classLoaders.done(); | |||
} |
@@ -39,18 +39,6 @@ public class ClassLoadersCollection { | |||
this.baseClassLoader = baseClassLoader; | |||
} | |||
/** | |||
* Generates URLClassLoader with parent-first delegation model. | |||
* | |||
* @param key plugin key | |||
* @param urls libraries | |||
* @return created ClassLoader, but actually this method shouldn't return anything, | |||
* because dependencies must be established - see {@link #done()}. | |||
*/ | |||
public ClassLoader createClassLoader(String key, Collection<URL> urls) { | |||
return createClassLoader(key, urls, false); | |||
} | |||
/** | |||
* Generates URLClassLoader with specified delegation model. | |||
* |
@@ -25,15 +25,21 @@ import org.apache.commons.lang.builder.ToStringStyle; | |||
import org.hibernate.annotations.Cascade; | |||
import org.sonar.api.database.BaseIdentifiable; | |||
import javax.persistence.*; | |||
import java.net.URI; | |||
import java.util.ArrayList; | |||
import java.util.Date; | |||
import java.util.List; | |||
import javax.persistence.CascadeType; | |||
import javax.persistence.Column; | |||
import javax.persistence.Entity; | |||
import javax.persistence.FetchType; | |||
import javax.persistence.OneToMany; | |||
import javax.persistence.Table; | |||
/** | |||
* Installed plugins | |||
* | |||
* | |||
* @since 2.2 | |||
*/ | |||
@Entity | |||
@@ -72,6 +78,9 @@ public class JpaPlugin extends BaseIdentifiable { | |||
@Column(name = "core", updatable = true, nullable = true) | |||
private Boolean core; | |||
@Column(name = "child_first_classloader", updatable = true, nullable = true) | |||
private Boolean childFirstClassLoader; | |||
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, | |||
org.hibernate.annotations.CascadeType.DELETE, | |||
@@ -200,6 +209,15 @@ public class JpaPlugin extends BaseIdentifiable { | |||
return this; | |||
} | |||
public Boolean isUseChildFirstClassLoader() { | |||
return childFirstClassLoader; | |||
} | |||
public JpaPlugin setUseChildFirstClassLoader(boolean use) { | |||
this.childFirstClassLoader = use; | |||
return this; | |||
} | |||
public void createFile(String filename) { | |||
JpaPluginFile file = new JpaPluginFile(this, filename); | |||
this.files.add(file); | |||
@@ -212,7 +230,7 @@ public class JpaPlugin extends BaseIdentifiable { | |||
public void removeFiles() { | |||
files.clear(); | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
@@ -241,7 +259,6 @@ public class JpaPlugin extends BaseIdentifiable { | |||
.toString(); | |||
} | |||
public static JpaPlugin create(String pluginKey) { | |||
return new JpaPlugin(pluginKey); | |||
} |
@@ -30,7 +30,7 @@ import java.sql.Statement; | |||
public class SchemaMigration { | |||
public final static int VERSION_UNKNOWN = -1; | |||
public static final int LAST_VERSION = 141; | |||
public static final int LAST_VERSION = 142; | |||
public final static String TABLE_NAME = "schema_migrations"; | |||
@@ -56,6 +56,7 @@ public class JpaPluginDaoTest extends AbstractDbUnitTestCase { | |||
setupData("shared"); | |||
JpaPlugin pmd = JpaPlugin.create("pmd"); | |||
pmd.setCore(false); | |||
pmd.setUseChildFirstClassLoader(false); | |||
pmd.setName("PMD"); | |||
pmd.setVersion("2.2"); | |||
pmd.setPluginClass("org.sonar.pmd.Main"); | |||
@@ -73,6 +74,7 @@ public class JpaPluginDaoTest extends AbstractDbUnitTestCase { | |||
setupData("shared"); | |||
JpaPlugin pmd = JpaPlugin.create("pmd"); | |||
pmd.setCore(false); | |||
pmd.setUseChildFirstClassLoader(false); | |||
pmd.setName("PMD"); | |||
pmd.setPluginClass("org.sonar.pmd.Main"); | |||
@@ -1,6 +1,6 @@ | |||
<dataset> | |||
<plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]" | |||
description="[null]" installation_date="[null]" plugin_class="[null]" core="true" version="2.2" /> | |||
description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" version="2.2" /> | |||
<plugin_files id="3" plugin_id="1" filename="newfile.jar"/> | |||
</dataset> |
@@ -1,9 +1,9 @@ | |||
<dataset> | |||
<plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]" | |||
description="[null]" installation_date="[null]" plugin_class="[null]" core="true" version="2.2"/> | |||
description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" version="2.2"/> | |||
<plugins id="2" name="PMD" plugin_key="pmd" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]" | |||
description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" version="[null]" /> | |||
description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" child_first_classloader="false" version="[null]" /> | |||
<plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/> | |||
<plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/> |
@@ -1,9 +1,9 @@ | |||
<dataset> | |||
<plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]" | |||
description="[null]" installation_date="[null]" plugin_class="[null]" core="true" version="2.2"/> | |||
description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" version="2.2"/> | |||
<plugins id="2" name="PMD" plugin_key="pmd" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]" | |||
description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" version="2.2" /> | |||
description="[null]" installation_date="[null]" plugin_class="org.sonar.pmd.Main" core="false" child_first_classloader="false" version="2.2" /> | |||
<plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/> | |||
<plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/> |
@@ -1,6 +1,6 @@ | |||
<dataset> | |||
<plugins id="1" name="Checkstyle" plugin_key="checkstyle" organization="[null]" organization_url="[null]" license="[null]" homepage="[null]" | |||
description="[null]" installation_date="[null]" plugin_class="[null]" core="true" version="2.2" /> | |||
description="[null]" installation_date="[null]" plugin_class="[null]" core="true" child_first_classloader="false" version="2.2" /> | |||
<plugin_files id="1" plugin_id="1" filename="checkstyle.jar"/> | |||
<plugin_files id="2" plugin_id="1" filename="checkstyle-extension.jar"/> |
@@ -36,16 +36,16 @@ public class PluginClassLoaders implements ServerComponent { | |||
private ClassLoadersCollection classLoaders = new ClassLoadersCollection(getClass().getClassLoader()); | |||
public ClassLoader create(PluginMetadata plugin) { | |||
return create(plugin.getKey(), plugin.getDeployedFiles()); | |||
return create(plugin.getKey(), plugin.getDeployedFiles(), plugin.isUseChildFirstClassLoader()); | |||
} | |||
ClassLoader create(String pluginKey, Collection<File> classloaderFiles) { | |||
ClassLoader create(String pluginKey, Collection<File> classloaderFiles, boolean useChildFirstClassLoader) { | |||
try { | |||
List<URL> urls = new ArrayList<URL>(); | |||
for (File file : classloaderFiles) { | |||
urls.add(toUrl(file)); | |||
} | |||
return classLoaders.createClassLoader(pluginKey, urls); | |||
return classLoaders.createClassLoader(pluginKey, urls, useChildFirstClassLoader); | |||
} catch (MalformedURLException e) { | |||
throw new RuntimeException("Fail to load the classloader of the plugin: " + pluginKey, e); | |||
} |
@@ -46,6 +46,7 @@ public class PluginMetadata { | |||
private String license; | |||
private String homepage; | |||
private boolean core; | |||
private boolean useChildFirstClassLoader; | |||
private String[] dependencyPaths = new String[0]; | |||
public List<File> deployedFiles = new ArrayList<File>(); | |||
@@ -158,6 +159,13 @@ public class PluginMetadata { | |||
return StringUtils.isNotBlank(mainClass); | |||
} | |||
public void setUseChildFirstClassLoader(boolean use) { | |||
this.useChildFirstClassLoader = use; | |||
} | |||
public boolean isUseChildFirstClassLoader() { | |||
return useChildFirstClassLoader; | |||
} | |||
public void setDependencyPaths(String[] paths) { | |||
this.dependencyPaths = paths; | |||
@@ -181,7 +189,9 @@ public class PluginMetadata { | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) return true; | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
@@ -218,6 +228,7 @@ public class PluginMetadata { | |||
metadata.setHomepage(manifest.getHomepage()); | |||
metadata.setDependencyPaths(manifest.getDependencies()); | |||
metadata.setCore(corePlugin); | |||
metadata.setUseChildFirstClassLoader(manifest.isUseChildFirstClassLoader()); | |||
return metadata; | |||
} | |||
@@ -231,6 +242,7 @@ public class PluginMetadata { | |||
jpaPlugin.setVersion(getVersion()); | |||
jpaPlugin.setHomepage(getHomepage()); | |||
jpaPlugin.setCore(isCore()); | |||
jpaPlugin.setUseChildFirstClassLoader(isUseChildFirstClassLoader()); | |||
jpaPlugin.removeFiles(); | |||
for (File file : getDeployedFiles()) { | |||
jpaPlugin.createFile(file.getName()); |
@@ -0,0 +1,27 @@ | |||
# | |||
# Sonar, entreprise quality control tool. | |||
# Copyright (C) 2009 SonarSource SA | |||
# 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 | |||
# | |||
class AddPluginsChildFirstClassloaderColumn < ActiveRecord::Migration | |||
def self.up | |||
add_column 'plugins', 'child_first_classloader', :boolean, :null => true | |||
Plugin.reset_column_information | |||
end | |||
end |
@@ -19,16 +19,17 @@ | |||
*/ | |||
package org.sonar.server.plugins; | |||
import com.google.common.collect.Lists; | |||
import static junit.framework.Assert.assertNotNull; | |||
import static junit.framework.Assert.assertNull; | |||
import org.junit.Test; | |||
import org.sonar.test.TestUtils; | |||
import com.google.common.collect.Lists; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import static junit.framework.Assert.assertNotNull; | |||
import static junit.framework.Assert.assertNull; | |||
public class PluginClassLoadersTest { | |||
@Test | |||
@@ -38,7 +39,7 @@ public class PluginClassLoadersTest { | |||
File jar = getFile("foo-plugin.jar"); | |||
PluginMetadata metadata = PluginMetadata.createFromJar(jar, false); | |||
metadata.addDeployedFile(jar); | |||
assertNull(getClass().getClassLoader().getResource("foo.txt")); | |||
PluginClassLoaders classloaders = new PluginClassLoaders(); | |||
@@ -53,7 +54,7 @@ public class PluginClassLoadersTest { | |||
File jar = getFile("sonar-build-breaker-plugin-0.1.jar"); | |||
PluginClassLoaders classloaders = new PluginClassLoaders(); | |||
classloaders.create("build-breaker", Lists.<File>newArrayList(jar)); | |||
classloaders.create("build-breaker", Lists.<File> newArrayList(jar), false); | |||
assertNotNull(classloaders.getClass("build-breaker", "org.sonar.plugins.buildbreaker.BuildBreakerPlugin")); | |||
assertNull(classloaders.getClass("build-breaker", "org.sonar.plugins.buildbreaker.Unknown")); |
@@ -19,6 +19,10 @@ | |||
*/ | |||
package org.sonar.server.plugins; | |||
import static org.hamcrest.Matchers.is; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertThat; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
@@ -40,10 +44,6 @@ import java.text.ParseException; | |||
import java.text.SimpleDateFormat; | |||
import java.util.List; | |||
import static org.hamcrest.Matchers.is; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertThat; | |||
public class PluginDeployerTest extends AbstractDbUnitTestCase { | |||
private Server server; | |||
@@ -80,6 +80,7 @@ public class PluginDeployerTest extends AbstractDbUnitTestCase { | |||
assertThat(plugin.getName(), is("Foo")); | |||
assertThat(plugin.getFiles().size(), is(1)); | |||
assertThat(plugin.isCore(), is(false)); | |||
assertThat(plugin.isUseChildFirstClassLoader(), is(false)); | |||
JpaPluginFile pluginFile = plugin.getFiles().get(0); | |||
assertThat(pluginFile.getFilename(), is("foo-plugin.jar")); | |||
assertThat(pluginFile.getPath(), is("foo/foo-plugin.jar")); | |||
@@ -106,6 +107,7 @@ public class PluginDeployerTest extends AbstractDbUnitTestCase { | |||
assertThat(plugin.getKey(), is("build-breaker")); | |||
assertThat(plugin.getFiles().size(), is(1)); | |||
assertThat(plugin.isCore(), is(false)); | |||
assertThat(plugin.isUseChildFirstClassLoader(), is(false)); | |||
JpaPluginFile pluginFile = plugin.getFiles().get(0); | |||
assertThat(pluginFile.getFilename(), is("sonar-build-breaker-plugin-0.1.jar")); | |||
assertThat(pluginFile.getPath(), is("build-breaker/sonar-build-breaker-plugin-0.1.jar")); |
@@ -37,6 +37,7 @@ public class PluginMetadataTest { | |||
assertThat(metadata.getMainClass(), is("foo.Main")); | |||
assertThat(metadata.getVersion(), is("2.2-SNAPSHOT")); | |||
assertThat(metadata.getOrganization(), is("SonarSource")); | |||
assertThat(metadata.isUseChildFirstClassLoader(), is(false)); | |||
assertThat(metadata.getDependencyPaths().length, is(0)); | |||
assertThat(metadata.isCore(), is(false)); | |||
} |