Browse Source

SONAR-1814 remove the API to find a plugin from an extension :

 * do register coverage extensions in picocontainer only when the plugin is selected (see parameter sonar.core.codeCoveragePlugin)
 * do not display plugin names when detecting a duplication of metrics
 * remove unused methods from the deprecated component RulesManager
tags/2.6
simonbrandhof 13 years ago
parent
commit
d2f25e344d

+ 48
- 14
sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java View File

@@ -20,12 +20,16 @@
package org.sonar.batch;

import com.google.common.collect.HashMultimap;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.ArrayUtils;
import org.picocontainer.MutablePicoContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchExtension;
import org.sonar.api.Plugin;
import org.sonar.api.platform.PluginRepository;
import org.sonar.api.batch.AbstractCoverageExtension;
import org.sonar.api.utils.SonarException;
import org.sonar.core.plugin.AbstractPluginRepository;
import org.sonar.core.plugin.JpaPlugin;
import org.sonar.core.plugin.JpaPluginDao;
import org.sonar.core.plugin.JpaPluginFile;
@@ -36,15 +40,26 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class BatchPluginRepository extends PluginRepository {
public class BatchPluginRepository extends AbstractPluginRepository {

private static final Logger LOG = LoggerFactory.getLogger(BatchPluginRepository.class);

private Map<String, ClassLoader> classloaders;
private String baseUrl;
private JpaPluginDao dao;
private Configuration configuration;

public BatchPluginRepository(JpaPluginDao dao, ServerMetadata server) {
this.dao= dao;
public BatchPluginRepository(JpaPluginDao dao, ServerMetadata server, Configuration configuration) {
this.dao = dao;
this.baseUrl = server.getUrl() + "/deploy/plugins/";
this.configuration = configuration;
}

/**
* only for unit tests
*/
BatchPluginRepository(Configuration configuration) {
this.configuration = configuration;
}

public void start() {
@@ -56,7 +71,7 @@ public class BatchPluginRepository extends PluginRepository {
urlsByKey.put(key, url);

} catch (MalformedURLException e) {
throw new RuntimeException("Can not build the classloader of the plugin " + pluginFile.getPluginKey(), e);
throw new SonarException("Can not build the classloader of the plugin " + pluginFile.getPluginKey(), e);
}
}

@@ -64,11 +79,10 @@ public class BatchPluginRepository extends PluginRepository {
for (String key : urlsByKey.keySet()) {
Set<URL> urls = urlsByKey.get(key);

Logger logger = LoggerFactory.getLogger(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Classloader of plugin " + key + ":");
if (LOG.isDebugEnabled()) {
LOG.debug("Classloader of plugin " + key + ":");
for (URL url : urls) {
logger.debug(" -> " + url);
LOG.debug(" -> " + url);
}
}
classloaders.put(key, new RemoteClassLoader(urls, Thread.currentThread().getContextClassLoader()).getClassLoader());
@@ -80,16 +94,36 @@ public class BatchPluginRepository extends PluginRepository {
}

public void registerPlugins(MutablePicoContainer pico) {
try {
for (JpaPlugin pluginMetadata : dao.getPlugins()) {
for (JpaPlugin pluginMetadata : dao.getPlugins()) {
try {
String classloaderKey = getClassloaderKey(pluginMetadata.getKey());
Class claz = classloaders.get(classloaderKey).loadClass(pluginMetadata.getPluginClass());
Plugin plugin = (Plugin) claz.newInstance();
registerPlugin(pico, plugin, BatchExtension.class);
registerPlugin(pico, plugin, pluginMetadata.getKey());

} catch (Exception e) {
throw new SonarException("Fail to load extensions from plugin " + pluginMetadata.getKey(), e);
}
}
}

} catch (Exception e) {
throw new RuntimeException(e);
boolean shouldRegisterCoverageExtension(String pluginKey) {
String[] selectedPluginKeys = configuration.getStringArray(AbstractCoverageExtension.PARAM_PLUGIN);
if (ArrayUtils.isEmpty(selectedPluginKeys)) {
selectedPluginKeys = new String[]{AbstractCoverageExtension.DEFAULT_PLUGIN};
}
return ArrayUtils.contains(selectedPluginKeys, pluginKey);
}

@Override
protected boolean shouldRegisterExtension(String pluginKey, Object extension) {
boolean ok = isType(extension, BatchExtension.class);
if (ok && isType(extension, AbstractCoverageExtension.class)) {
ok = shouldRegisterCoverageExtension(pluginKey);
if (!ok) {
LOG.debug("The following extension is ignored: " + extension + ". See the parameter " + AbstractCoverageExtension.PARAM_PLUGIN);
}
}
return ok;
}
}

+ 76
- 0
sonar-batch/src/test/java/org/sonar/batch/BatchPluginRepositoryTest.java View File

@@ -0,0 +1,76 @@
/*
* Sonar, open source software quality management 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
*/
package org.sonar.batch;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Test;
import org.sonar.api.BatchExtension;
import org.sonar.api.ServerExtension;
import org.sonar.api.batch.AbstractCoverageExtension;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class BatchPluginRepositoryTest {

@Test
public void shouldRegisterBatchExtension() {
BatchPluginRepository repository = new BatchPluginRepository(new PropertiesConfiguration());

// check classes
assertThat(repository.shouldRegisterExtension("foo", FakeBatchExtension.class), is(true));
assertThat(repository.shouldRegisterExtension("foo", FakeServerExtension.class), is(false));
assertThat(repository.shouldRegisterExtension("foo", String.class), is(false));

// check objects
assertThat(repository.shouldRegisterExtension("foo", new FakeBatchExtension()), is(true));
assertThat(repository.shouldRegisterExtension("foo", new FakeServerExtension()), is(false));
assertThat(repository.shouldRegisterExtension("foo", "bar"), is(false));
}

@Test
public void shouldRegisterOnlyCoberturaExtensionByDefault() {
Configuration conf = new PropertiesConfiguration();
BatchPluginRepository repository = new BatchPluginRepository(conf);
assertThat(repository.shouldRegisterCoverageExtension("cobertura"), is(true));
assertThat(repository.shouldRegisterCoverageExtension("clover"), is(false));
}

@Test
public void shouldRegisterCustomCoverageExtension() {
Configuration conf = new PropertiesConfiguration();
conf.setProperty(AbstractCoverageExtension.PARAM_PLUGIN, "clover,phpunit");
BatchPluginRepository repository = new BatchPluginRepository(conf);
assertThat(repository.shouldRegisterCoverageExtension("cobertura"), is(false));
assertThat(repository.shouldRegisterCoverageExtension("clover"), is(true));
assertThat(repository.shouldRegisterCoverageExtension("phpunit"), is(true));
assertThat(repository.shouldRegisterCoverageExtension("other"), is(false));
}


public static class FakeBatchExtension implements BatchExtension {

}

public static class FakeServerExtension implements ServerExtension {

}
}

+ 120
- 0
sonar-core/src/main/java/org/sonar/core/plugin/AbstractPluginRepository.java View File

@@ -0,0 +1,120 @@
/*
* Sonar, open source software quality management 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
*/
package org.sonar.core.plugin;

import com.google.common.collect.Maps;
import org.picocontainer.Characteristics;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.injectors.ProviderAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.*;
import org.sonar.api.platform.PluginRepository;

import java.util.Collection;
import java.util.Map;

/**
* @since 2.2
*/
public abstract class AbstractPluginRepository implements PluginRepository {

private static final Logger LOG = LoggerFactory.getLogger(AbstractPluginRepository.class);

private Map<String, Plugin> pluginByKey = Maps.newHashMap();
private Map<Object, Plugin> pluginByExtension = Maps.newIdentityHashMap();

protected void registerPlugin(MutablePicoContainer container, Plugin plugin, String pluginKey) {
LOG.debug("Register the plugin {}", pluginKey);
pluginByKey.put(pluginKey, plugin);
for (Object extension : plugin.getExtensions()) {
registerExtension(container, plugin, pluginKey, extension);
}
}

private void registerExtension(MutablePicoContainer container, Plugin plugin, String pluginKey, Object extension) {
if (shouldRegisterExtension(pluginKey, extension)) {
LOG.debug("Register the extension: {}", extension);
container.as(Characteristics.CACHE).addComponent(getExtensionKey(extension), extension);
pluginByExtension.put(extension, plugin);
}
if (isExtensionProvider(extension)) {
LOG.debug("Register the extension provider: {}", extension);
container.as(Characteristics.CACHE).addAdapter(new ExtensionProviderAdapter(extension));
}
}

protected abstract boolean shouldRegisterExtension(String pluginKey, Object extension);

public Collection<Plugin> getPlugins() {
return pluginByKey.values();
}

public Plugin getPlugin(String key) {
return pluginByKey.get(key);
}

/**
* Returns the list of properties of a plugin
*/
public Property[] getProperties(Plugin plugin) {
if (plugin != null) {
Class<? extends Plugin> classInstance = plugin.getClass();
if (classInstance.isAnnotationPresent(Properties.class)) {
return classInstance.getAnnotation(Properties.class).value();
}
}
return new Property[0];
}

public Property[] getProperties(String pluginKey) {
return getProperties(pluginByKey.get(pluginKey));
}

public Plugin getPluginForExtension(Object extension) {
Plugin plugin = pluginByExtension.get(extension);
if (plugin == null && !(extension instanceof Class)) {
plugin = pluginByExtension.get(extension.getClass());
}
return plugin;
}

protected static boolean isType(Object extension, Class<? extends Extension> extensionClass) {
Class clazz = (extension instanceof Class ? (Class) extension : extension.getClass());
return extensionClass.isAssignableFrom(clazz);
}

protected static boolean isExtensionProvider(Object extension) {
return extension instanceof ExtensionProvider;
}

protected static Object getExtensionKey(Object component) {
if (component instanceof Class) {
return component;
}
return component.getClass().getCanonicalName() + "-" + component.toString();
}

public static class ExtensionProviderAdapter extends ProviderAdapter {
public ExtensionProviderAdapter(Object provider) {
super(provider);
}
}
}

+ 152
- 0
sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java View File

@@ -0,0 +1,152 @@
/*
* Sonar, open source software quality management 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
*/
package org.sonar.core.plugin;

import org.junit.Test;
import org.picocontainer.MutablePicoContainer;
import org.sonar.api.BatchExtension;
import org.sonar.api.ExtensionProvider;
import org.sonar.api.Plugin;
import org.sonar.api.ServerExtension;
import org.sonar.api.utils.IocContainer;

import java.util.Arrays;

import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class AbstractPluginRepositoryTest {

@Test
public void testIsType() {
assertThat(AbstractPluginRepository.isType(FakeServerExtension.class, ServerExtension.class), is(true));
assertThat(AbstractPluginRepository.isType(new FakeServerExtension(), ServerExtension.class), is(true));

assertThat(AbstractPluginRepository.isType(FakeBatchExtension.class, ServerExtension.class), is(false));
assertThat(AbstractPluginRepository.isType(new FakeBatchExtension(), ServerExtension.class), is(false));
assertThat(AbstractPluginRepository.isType(String.class, ServerExtension.class), is(false));
assertThat(AbstractPluginRepository.isType("foo", ServerExtension.class), is(false));
}

@Test
public void extensionKeyshouldBeClassNameIfClass() {
assertEquals(AbstractPluginRepository.getExtensionKey(FakeServerExtension.class), FakeServerExtension.class);
}

@Test
public void extensionKeyshouldBeUniqueIfObject() {
assertThat((String) AbstractPluginRepository.getExtensionKey(new FakeServerExtension()), endsWith("FakeServerExtension-instance"));
}

@Test
public void extensionProviderIsAnObjectButNotAClass() {
assertThat(AbstractPluginRepository.isExtensionProvider(BProvider.class), is(false));
assertThat(AbstractPluginRepository.isExtensionProvider(new BProvider()), is(true));
}

@Test
public void shouldRegisterExtensionProviders() {
MutablePicoContainer pico = IocContainer.buildPicoContainer();
AbstractPluginRepository repository = new AbstractPluginRepository() {
@Override
protected boolean shouldRegisterExtension(String pluginKey, Object extension) {
return isType(extension, ServerExtension.class);
}
};

Plugin plugin = mock(Plugin.class);
BProvider bProvider = new BProvider();
when(plugin.getExtensions()).thenReturn(Arrays.asList(A.class, bProvider, C.class, D.class));
repository.registerPlugin(pico, plugin, "foo");
pico.start();

assertThat(pico.getComponent(A.class), is(A.class));
assertThat(pico.getComponent(C.class), is(C.class));
assertThat(pico.getComponent(D.class), is(D.class));
assertThat(pico.getComponent(C.class).getBees().length, is(2));
assertThat(pico.getComponent(D.class).getBees().length, is(2));
assertThat(bProvider.calls, is(1)); // do not create B instances two times (C and D dependencies)

// Picocontainer question: why components created by providers are not registered in the container ?
//assertThat(pico.getComponents(B.class).size(), is(2)); // created by the ExtensionProvider
}

public static class FakeServerExtension implements ServerExtension {
@Override
public String toString() {
return "instance";
}
}

public static class FakeBatchExtension implements BatchExtension {

}

public static class A implements ServerExtension {
}

public static class B implements ServerExtension {
private A a;

public B(A a) {
this.a = a;
}
}

public static class C implements ServerExtension {
private B[] bees;

public C(B[] bees) {
this.bees = bees;
}

public B[] getBees() {
return bees;
}
}

public static class D implements ServerExtension {
private B[] bees;

public D(B[] bees) {
this.bees = bees;
}

public B[] getBees() {
return bees;
}
}

public static class BProvider extends ExtensionProvider {

private int calls = 0;
public B[] provide(A a) {
calls++;
return new B[]{new B(a), new B(a)};
}
}


}

+ 9
- 132
sonar-deprecated/src/main/java/org/sonar/api/rules/DefaultRulesManager.java View File

@@ -19,143 +19,31 @@
*/
package org.sonar.api.rules;

import org.apache.commons.collections.CollectionUtils;
import org.sonar.api.Plugin;
import org.sonar.api.Plugins;
import com.google.common.collect.Maps;
import org.sonar.jpa.dao.RulesDao;
import org.sonar.api.resources.Language;

import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* A class to manage and access rules defined in Sonar.
*
* @deprecated UGLY CLASS - WILL BE COMPLETELY REFACTORED IN SONAR 2.3
* @deprecated UGLY CLASS
*/
@Deprecated
public class DefaultRulesManager extends RulesManager {

private final Set<Language> languages;
private final RulesRepository<?>[] repositories;
private final Map<Language, List<RulesRepository<?>>> rulesByLanguage;
private final Map<Language, List<Plugin>> pluginsByLanguage;
private final Map<String, Map<String, Rule>> rulesByPluginAndKey = new HashMap<String, Map<String, Rule>>();
private final Map<String, Map<String, Rule>> rulesByPluginAndKey = Maps.newHashMap();
private final RulesDao rulesDao;
private final Plugins plugins;

/**
* Creates a RuleManager
* @param plugins the plugins dictionnary
* @param repositories the repositories of rules
* @param dao the dao object
*/
public DefaultRulesManager(Plugins plugins, RulesRepository[] repositories, RulesDao dao) {
this.plugins = plugins;
public DefaultRulesManager(RulesDao dao) {
this.rulesDao = dao;

languages = new HashSet<Language>();
rulesByLanguage = new HashMap<Language, List<RulesRepository<?>>>();
pluginsByLanguage = new HashMap<Language, List<Plugin>>();
this.repositories = repositories;

for (RulesRepository<?> repository : repositories) {
languages.add(repository.getLanguage());

List<RulesRepository<?>> list = rulesByLanguage.get(repository.getLanguage());
if (list == null) {
list = new ArrayList<RulesRepository<?>>();
rulesByLanguage.put(repository.getLanguage(), list);
}
list.add(repository);

List<Plugin> languagePlugins = pluginsByLanguage.get(repository.getLanguage());
if (languagePlugins == null) {
languagePlugins = new ArrayList<Plugin>();
pluginsByLanguage.put(repository.getLanguage(), languagePlugins);
}
languagePlugins.add(plugins.getPluginByExtension(repository));
}
}

/**
* Constructor for tests only
*
* @param dao the dao
*/
protected DefaultRulesManager(RulesDao dao, Plugins plugins) {
this.rulesDao = dao;
this.plugins = plugins;
languages = new HashSet<Language>();
rulesByLanguage = new HashMap<Language, List<RulesRepository<?>>>();
pluginsByLanguage = new HashMap<Language, List<Plugin>>();
repositories = null;

}

/**
* Returns the list of languages for which there is a rule repository
*
* @return a Set of languages
*/
public Set<Language> getLanguages() {
return languages;
}

/**
* Gets the list of Rules Repositories available for a language
*
* @param language the language
* @return the list of rules repositories
*/
public List<RulesRepository<?>> getRulesRepositories(Language language) {
List<RulesRepository<?>> rulesRepositories = rulesByLanguage.get(language);
if (CollectionUtils.isNotEmpty(rulesRepositories)) {
return rulesRepositories;
}
return Collections.emptyList();
}

/**
* Gets the complete list of Rules Repositories in the Sonar instance
*
* @return the list of rules repositories
*/
public List<RulesRepository<?>> getRulesRepositories() {
return Arrays.asList(repositories);
}

/**
* Gets the list of rules plugins for a given language
* @param language the language
* @return the list of plugins
*/
public List<Plugin> getPlugins(Language language) {
List<Plugin> result = pluginsByLanguage.get(language);
if (!CollectionUtils.isEmpty(result)) {
return result;
}
return Collections.emptyList();
}


/**
* Get the list of rules plugin that implement a mechanism of import for a given language
*
* @param language the language
* @return the list of plugins
*/
public List<Plugin> getImportablePlugins(Language language) {
List<Plugin> targets = new ArrayList<Plugin>();
for (RulesRepository<?> repository : getRulesRepositories(language)) {
if (repository instanceof ConfigurationImportable) {
targets.add(plugins.getPluginByExtension(repository));
}
}
return targets;
}

/**
* Gets a list of rules indexed by their key for a given plugin
*
* @param pluginKey the plugin key
* @return a Map with the rule key and the rule
*/
@@ -174,22 +62,11 @@ public class DefaultRulesManager extends RulesManager {
return rulesByKey;
}

/**
* Gets a collection of rules belonging to a plugin
*
* @param pluginKey the plugin key
* @return the collection of rules
*/
public Collection<Rule> getPluginRules(String pluginKey) {
Map<String, Rule> rulesByKey = getPluginRulesIndexedByKey(pluginKey);
return rulesByKey.values();
}

/**
* Gets a rule belonging to a defined plugin based on its key
*
* @param pluginKey the plugin key
* @param ruleKey the rule key
* @param ruleKey the rule key
* @return the rule
*/
public Rule getPluginRule(String pluginKey, String ruleKey) {

+ 0
- 52
sonar-deprecated/src/main/java/org/sonar/api/rules/RulesManager.java View File

@@ -33,58 +33,6 @@ import java.util.Set;
@Deprecated
public abstract class RulesManager {

/**
* Returns the list of languages for which there is a rule repository
*
* @return a Set of languages
*/
public abstract Set<Language> getLanguages();

/**
* Gets the list of Rules Repositories available for a language
*
* @param language the language
* @return the list of rules repositories
*/
public abstract List<RulesRepository<?>> getRulesRepositories(Language language);

/**
* Gets the complete list of Rules Repositories in the Sonar instance
*
* @return the list of rules repositories
*/
public abstract List<RulesRepository<?>> getRulesRepositories();

/**
* Gets the list of rules plugins for a given language
* @param language the language
* @return the list of plugins
*/
public abstract List<Plugin> getPlugins(Language language);

/**
* Get the list of rules plugin that implement a mechanism of import for a given language
*
* @param language the language
* @return the list of plugins
*/
public abstract List<Plugin> getImportablePlugins(Language language);

/**
* Gets a list of rules indexed by their key for a given plugin
* @param pluginKey the plugin key
* @return a Map with the rule key and the rule
*/
public abstract Map<String, Rule> getPluginRulesIndexedByKey(String pluginKey);

/**
* Gets a collection of rules belonging to a plugin
*
* @param pluginKey the plugin key
* @return the collection of rules
*/
public abstract Collection<Rule> getPluginRules(String pluginKey);

/**
* Gets a rule belonging to a defined plugin based on its key
*

+ 0
- 72
sonar-deprecated/src/test/java/org/sonar/api/rules/DefaultRulesManagerTest.java View File

@@ -1,72 +0,0 @@
/*
* Sonar, open source software quality management 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
*/
package org.sonar.api.rules;
import org.junit.Test;
import org.sonar.api.Plugin;
import org.sonar.api.Plugins;
import org.sonar.jpa.dao.RulesDao;
import org.sonar.api.resources.Language;
import org.sonar.jpa.dao.DaoFacade;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class DefaultRulesManagerTest {
@Test
public void shouldReturnsZeroImportablePluginsWhenLanguageHasNoRulesPlugin() {
DefaultRulesManager rulesManager = createRulesManagerForAlanguageWithNoPlugins();
Language language = mock(Language.class);
List<Plugin> result = rulesManager.getImportablePlugins(language);
assertThat(result.size(), is(0));
}
@Test
public void shouldReturnZeroPluginWhenLanguageHasNoRulesPlugin() {
DefaultRulesManager rulesManager = createRulesManagerForAlanguageWithNoPlugins();
Language language = mock(Language.class);
List<Plugin> result = rulesManager.getPlugins(language);
assertThat(result.size(), is(0));
}
@Test
public void shouldReturnZeroRulesRepositoryWhenLanguageHasNoRulesRepository() {
DefaultRulesManager rulesManager = createRulesManagerForAlanguageWithNoPlugins();
Language language = mock(Language.class);
List<RulesRepository<?>> result = rulesManager.getRulesRepositories(language);
assertThat(result.size(), is(0));
}
private DefaultRulesManager createRulesManagerForAlanguageWithNoPlugins() {
DaoFacade dao = mock(DaoFacade.class);
RulesDao rulesDao = mock(RulesDao.class);
when(rulesDao.getCategories()).thenReturn(Collections.<RulesCategory>emptyList());
DefaultRulesManager rulesManager = new DefaultRulesManager(rulesDao, new Plugins(null));
return rulesManager;
}
}

+ 2
- 4
sonar-plugin-api/src/main/java/org/sonar/api/ExtensionProvider.java View File

@@ -19,13 +19,11 @@
*/
package org.sonar.api;

import java.util.Collection;
import org.picocontainer.injectors.Provider;

/**
* @since 2.3
*/
public interface ExtensionProvider {

Collection provide();
public abstract class ExtensionProvider implements Extension, Provider {

}

+ 0
- 15
sonar-plugin-api/src/main/java/org/sonar/api/Plugins.java View File

@@ -54,21 +54,6 @@ public class Plugins {
return pluginProvider.getPlugin(key);
}


/**
* Returns a plugin based on its extension
*/
public Plugin getPluginByExtension(Class<? extends Extension> clazz) {
return pluginProvider.getPluginForExtension(clazz);
}

/**
* Returns a plugin key based on its extension
*/
public String getPluginKeyByExtension(Class<? extends Extension> clazz) {
return pluginProvider.getPluginKeyForExtension(clazz);
}

/**
* Returns a plugin based on its extension
*/

+ 6
- 14
sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCoverageExtension.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.api.batch;

import org.apache.commons.lang.ArrayUtils;
import org.sonar.api.BatchExtension;
import org.sonar.api.Plugins;
import org.sonar.api.resources.Project;
@@ -42,29 +41,22 @@ public abstract class AbstractCoverageExtension implements BatchExtension {
*/
public static final String DEFAULT_PLUGIN = "cobertura";

private final Plugins plugins;

/**
* Default constructor
*
* @param plugins the list of plugins available
* @deprecated since 2.3. Use the default constructor
*/
public AbstractCoverageExtension(Plugins plugins) {
this.plugins = plugins;
}

public AbstractCoverageExtension() {
}

/**
* Whether to implement the extension on the project
*/
public boolean shouldExecuteOnProject(Project project) {
return project.getAnalysisType().isDynamic(true) && isSelectedPlugin(project);
}

protected boolean isSelectedPlugin(Project project) {
String[] selectedPluginKeys = project.getConfiguration().getStringArray(PARAM_PLUGIN);
if (selectedPluginKeys.length == 0) {
selectedPluginKeys = new String[]{DEFAULT_PLUGIN};
}
String pluginKey = plugins.getPluginKeyByExtension(getClass());
return ArrayUtils.contains(selectedPluginKeys, pluginKey);
return project.getAnalysisType().isDynamic(true);
}
}

+ 10
- 85
sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java View File

@@ -19,95 +19,20 @@
*/
package org.sonar.api.platform;

import org.picocontainer.Characteristics;
import org.picocontainer.MutablePicoContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.*;
import org.sonar.api.BatchComponent;
import org.sonar.api.Plugin;
import org.sonar.api.Property;
import org.sonar.api.ServerComponent;

import java.util.Collection;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;

/**
* @since 2.2
*/
public abstract class PluginRepository implements BatchComponent, ServerComponent {

private static final Logger LOG = LoggerFactory.getLogger(PluginRepository.class);

private Map<String, Plugin> pluginByKey = new HashMap<String, Plugin>();
private Map<Object, Plugin> pluginByExtension = new IdentityHashMap<Object, Plugin>();

public void registerPlugin(MutablePicoContainer container, Plugin plugin, Class<? extends Extension> extensionClass) {
LOG.debug("Registering the plugin {}", plugin.getKey());
pluginByKey.put(plugin.getKey(), plugin);
for (Object extension : plugin.getExtensions()) {
if (isExtension(extension, extensionClass)) {
LOG.debug("Registering the extension: {}", extension);
container.as(Characteristics.CACHE).addComponent(getExtensionKey(extension), extension);
pluginByExtension.put(extension, plugin);
}
}
}

public Collection<Plugin> getPlugins() {
return pluginByKey.values();
}

public Plugin getPlugin(String key) {
return pluginByKey.get(key);
}

/**
* Returns the list of properties of a plugin
*/
public Property[] getProperties(Plugin plugin) {
if (plugin != null) {
Class<? extends Plugin> classInstance = plugin.getClass();
if (classInstance.isAnnotationPresent(Properties.class)) {
return classInstance.getAnnotation(Properties.class).value();
}
}
return new Property[0];
}

public Property[] getProperties(String pluginKey) {
return getProperties(pluginByKey.get(pluginKey));
}

public Plugin getPluginForExtension(Object extension) {
Plugin plugin = pluginByExtension.get(extension);
if (plugin == null && !(extension instanceof Class)) {
plugin = pluginByExtension.get(extension.getClass());
}
return plugin;
}

public String getPluginKeyForExtension(Object extension) {
Plugin plugin = getPluginForExtension(extension);
if (plugin != null) {
return plugin.getKey();
}
return null;
}

private boolean isExtension(Object extension, Class<? extends Extension> extensionClass) {
Class clazz = (extension instanceof Class ? (Class) extension : extension.getClass());
return extensionClass.isAssignableFrom(clazz);
}
public interface PluginRepository extends BatchComponent, ServerComponent {
Collection<Plugin> getPlugins();

public void registerExtension(MutablePicoContainer container, Plugin plugin, Object extension) {
container.as(Characteristics.CACHE).addComponent(getExtensionKey(extension), extension);
pluginByExtension.put(extension, plugin);
}
Plugin getPlugin(String key);

protected Object getExtensionKey(Object component) {
if (component instanceof Class) {
return component;
}
return component.getClass().getCanonicalName() + "-" + component.toString();
}
@Deprecated
Plugin getPluginForExtension(Object extension);

Property[] getProperties(Plugin plugin);
}

+ 5
- 32
sonar-plugin-api/src/test/java/org/sonar/api/batch/AbstractCoverageExtensionTest.java View File

@@ -19,53 +19,26 @@
*/
package org.sonar.api.batch;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Test;
import org.sonar.api.resources.Project;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.sonar.api.Plugins;
import org.sonar.api.resources.Project;

public class AbstractCoverageExtensionTest {

@Test
public void defaultPluginIsCobertura() {
Plugins plugins = mock(Plugins.class);
when(plugins.getPluginKeyByExtension(FakeCoverageSensor.class)).thenReturn("cobertura");

Project project = mock(Project.class);
when(project.getConfiguration()).thenReturn(new PropertiesConfiguration());

assertThat(new FakeCoverageSensor(plugins).isSelectedPlugin(project), is(true));
}

@Test
public void doNotExecuteIfNotSelectedPlugin() {
Plugins plugins = mock(Plugins.class);
when(plugins.getPluginKeyByExtension(FakeCoverageSensor.class)).thenReturn("fake");

Project project = mock(Project.class);
PropertiesConfiguration config = new PropertiesConfiguration();
when(project.getConfiguration()).thenReturn(config);
config.setProperty(AbstractCoverageExtension.PARAM_PLUGIN, "cobertura");

assertThat(new FakeCoverageSensor(plugins).isSelectedPlugin(project), is(false));
}

@Test
public void doNotExecuteIfStaticAnalysis() {
Project project = mock(Project.class);
when(project.getAnalysisType()).thenReturn(Project.AnalysisType.STATIC);
FakeCoverageSensor sensor = new FakeCoverageSensor(null);
FakeCoverageSensor sensor = new FakeCoverageSensor();

assertThat(sensor.shouldExecuteOnProject(project), is(false));
}

protected static class FakeCoverageSensor extends AbstractCoverageExtension {
public FakeCoverageSensor(Plugins plugins) {
super(plugins);
}

}
}

+ 1
- 1
sonar-server/src/main/java/org/sonar/server/platform/Platform.java View File

@@ -133,7 +133,7 @@ public final class Platform {
coreContainer.as(Characteristics.CACHE).addComponent(UpdateFinderFactory.class);
coreContainer.as(Characteristics.CACHE).addComponent(PluginDownloader.class);
coreContainer.as(Characteristics.NO_CACHE).addComponent(FilterExecutor.class);
coreContainer.addAdapter(new DatabaseSessionProvider());
coreContainer.as(Characteristics.NO_CACHE).addAdapter(new DatabaseSessionProvider());
coreContainer.start();

DatabaseConfiguration dbConfiguration = new DatabaseConfiguration(coreContainer.getComponent(DatabaseSessionFactory.class));

+ 15
- 4
sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java View File

@@ -23,15 +23,15 @@ import org.picocontainer.Characteristics;
import org.picocontainer.MutablePicoContainer;
import org.sonar.api.Plugin;
import org.sonar.api.ServerExtension;
import org.sonar.api.platform.PluginRepository;
import org.sonar.api.utils.SonarException;
import org.sonar.core.plugin.AbstractPluginRepository;
import org.sonar.core.plugin.JpaPlugin;
import org.sonar.core.plugin.JpaPluginDao;

/**
* @since 2.2
*/
public class ServerPluginRepository extends PluginRepository {
public class ServerPluginRepository extends AbstractPluginRepository {

private JpaPluginDao dao;
private PluginClassLoaders classloaders;
@@ -41,18 +41,29 @@ public class ServerPluginRepository extends PluginRepository {
this.classloaders = classloaders;
}

/**
* Only for unit tests
*/
ServerPluginRepository() {
}

public void registerPlugins(MutablePicoContainer pico) {
for (JpaPlugin jpaPlugin : dao.getPlugins()) {
try {
Class pluginClass = classloaders.getClassLoader(jpaPlugin.getKey()).loadClass(jpaPlugin.getPluginClass());
pico.as(Characteristics.CACHE).addComponent(pluginClass);
Plugin plugin = (Plugin) pico.getComponent(pluginClass);
registerPlugin(pico, plugin, ServerExtension.class);
registerPlugin(pico, plugin, jpaPlugin.getKey());


} catch (ClassNotFoundException e) {
throw new SonarException("Please check the plugin manifest. The main plugin class does not exist: " + jpaPlugin.getPluginClass(), e);
}
}
}

@Override
protected boolean shouldRegisterExtension(String pluginKey, Object extension) {
return isType(extension, ServerExtension.class);
}
}

+ 7
- 14
sonar-server/src/main/java/org/sonar/server/startup/RegisterMetrics.java View File

@@ -36,35 +36,30 @@ import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Query;
public class RegisterMetrics {
private PluginRepository pluginRepository;
private MeasuresDao measuresDao;
private Metrics[] metricsRepositories = null;
private DatabaseSession session;
public RegisterMetrics(DatabaseSession session, MeasuresDao measuresDao, PluginRepository pluginRepository, Metrics[] metricsRepositories) {
public RegisterMetrics(DatabaseSession session, MeasuresDao measuresDao, Metrics[] metricsRepositories) {
this.session = session;
this.measuresDao = measuresDao;
this.pluginRepository = pluginRepository;
this.metricsRepositories = metricsRepositories;
}
public RegisterMetrics(MeasuresDao measuresDao) {
this.measuresDao = measuresDao;
}
public void start() {
TimeProfiler profiler = new TimeProfiler().start("Load metrics");
measuresDao.disableAutomaticMetrics();
ArrayList<Metric> metricsToRegister = Lists.newArrayList();
List<Metric> metricsToRegister = Lists.newArrayList();
metricsToRegister.addAll(CoreMetrics.getMetrics());
HashMap<String, Metrics> metricsByRepository = Maps.newHashMap();
Map<String, Metrics> metricsByRepository = Maps.newHashMap();
if (metricsRepositories != null) {
for (Metrics metrics : metricsRepositories) {
checkMetrics(metricsByRepository, metrics);
@@ -76,17 +71,15 @@ public class RegisterMetrics {
profiler.stop();
}
private void checkMetrics(HashMap<String, Metrics> metricsByRepository, Metrics metrics) {
private void checkMetrics(Map<String, Metrics> metricsByRepository, Metrics metrics) {
for (Metric metric : metrics.getMetrics()) {
String metricKey = metric.getKey();
if (CoreMetrics.metrics.contains(metric)) {
throw new ServerStartException("Found plugin, which contains metric '" + metricKey + "' from core: " + pluginRepository.getPluginKeyForExtension(metrics));
throw new ServerStartException("The following metric is already defined in sonar: " + metricKey);
}
Metrics anotherRepository = metricsByRepository.get(metricKey);
if (anotherRepository != null) {
String firstPlugin = pluginRepository.getPluginKeyForExtension(anotherRepository);
String secondPlugin = pluginRepository.getPluginKeyForExtension(metrics);
throw new ServerStartException("Found two plugins with the same metric '" + metricKey + "': " + firstPlugin + " and " + secondPlugin);
throw new ServerStartException("The metric '" + metricKey + "' is already defined in the extension: " + anotherRepository);
}
metricsByRepository.put(metricKey, metrics);
}

+ 52
- 0
sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java View File

@@ -0,0 +1,52 @@
/*
* Sonar, open source software quality management 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
*/
package org.sonar.server.plugins;

import org.junit.Test;
import org.sonar.api.BatchExtension;
import org.sonar.api.ServerExtension;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class ServerPluginRepositoryTest {
@Test
public void shouldRegisterServerExtensions() {
ServerPluginRepository repository = new ServerPluginRepository();

// check classes
assertThat(repository.shouldRegisterExtension("foo", FakeBatchExtension.class), is(false));
assertThat(repository.shouldRegisterExtension("foo", FakeServerExtension.class), is(true));
assertThat(repository.shouldRegisterExtension("foo", String.class), is(false));

// check objects
assertThat(repository.shouldRegisterExtension("foo", new FakeBatchExtension()), is(false));
assertThat(repository.shouldRegisterExtension("foo", new FakeServerExtension()), is(true));
assertThat(repository.shouldRegisterExtension("foo", "foo"), is(false));
}

public static class FakeBatchExtension implements BatchExtension {

}

public static class FakeServerExtension implements ServerExtension {

}
}

+ 4
- 4
sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java View File

@@ -41,7 +41,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase {
Metric metric1 = new Metric("new1", "short1", "desc1", Metric.ValueType.FLOAT, 1, true, "domain1", false);
Metric metric2 = new Metric("new2", "short2", "desc2", Metric.ValueType.FLOAT, 1, true, "domain2", false);
RegisterMetrics synchronizer = new RegisterMetrics(getSession(), new MeasuresDao(getSession()), null, null);
RegisterMetrics synchronizer = new RegisterMetrics(getSession(), new MeasuresDao(getSession()), null);
synchronizer.register(Arrays.asList(metric1, metric2));
checkTables("shouldSaveIfNew", "metrics");
}
@@ -52,7 +52,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase {
final List<Metric> metrics = new ArrayList<Metric>();
metrics.add(new Metric("key", "new short name", "new description", Metric.ValueType.FLOAT, -1, true, "new domain", false));
RegisterMetrics synchronizer = new RegisterMetrics(getSession(), new MeasuresDao(getSession()), null, null);
RegisterMetrics synchronizer = new RegisterMetrics(getSession(), new MeasuresDao(getSession()), null);
synchronizer.register(metrics);
checkTables("shouldUpdateIfAlreadyExists", "metrics");
@@ -62,7 +62,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase {
public void enableOnlyLoadedMetrics() throws SQLException {
setupData("enableOnlyLoadedMetrics");
RegisterMetrics loader = new RegisterMetrics(getSession(), new MeasuresDao(getSession()), null, null);
RegisterMetrics loader = new RegisterMetrics(getSession(), new MeasuresDao(getSession()), null);
loader.start();
assertFalse(getDao().getMeasuresDao().getMetric("deprecated").getEnabled());
@@ -73,7 +73,7 @@ public class RegisterMetricsTest extends AbstractDbUnitTestCase {
public void cleanAlerts() throws SQLException {
setupData("cleanAlerts");
RegisterMetrics loader = new RegisterMetrics(getSession(), new MeasuresDao(getSession()), null, null);
RegisterMetrics loader = new RegisterMetrics(getSession(), new MeasuresDao(getSession()), null);
loader.cleanAlerts();
checkTables("cleanAlerts", "metrics", "alerts");

Loading…
Cancel
Save