diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2010-09-28 15:53:04 +0000 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2010-09-28 15:53:04 +0000 |
commit | f25c971275702a3a53195b568fe3c91469076c83 (patch) | |
tree | 52764695c638d5905d6116d2bf4d5d4416290ba6 | |
parent | 5cdbf337fef2559593e53dc7b2a2c68d8e1f992f (diff) | |
download | sonarqube-f25c971275702a3a53195b568fe3c91469076c83.tar.gz sonarqube-f25c971275702a3a53195b568fe3c91469076c83.zip |
SONAR-1814 add the method ExtensionProvider.provide() in order to support obfuscated code
6 files changed, 73 insertions, 50 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java b/sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java index 5544762ad7a..51a2e095380 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java +++ b/sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java @@ -105,6 +105,7 @@ public class BatchPluginRepository extends AbstractPluginRepository { throw new SonarException("Fail to load extensions from plugin " + pluginMetadata.getKey(), e); } } + invokeExtensionProviders(pico); } boolean shouldRegisterCoverageExtension(String pluginKey) { diff --git a/sonar-core/src/main/java/org/sonar/core/plugin/AbstractPluginRepository.java b/sonar-core/src/main/java/org/sonar/core/plugin/AbstractPluginRepository.java index b62bf983045..9a15c9e06ba 100644 --- a/sonar-core/src/main/java/org/sonar/core/plugin/AbstractPluginRepository.java +++ b/sonar-core/src/main/java/org/sonar/core/plugin/AbstractPluginRepository.java @@ -19,16 +19,18 @@ */ package org.sonar.core.plugin; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; 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.List; import java.util.Map; /** @@ -38,7 +40,7 @@ public abstract class AbstractPluginRepository implements PluginRepository { private static final Logger LOG = LoggerFactory.getLogger(AbstractPluginRepository.class); - private Map<String, Plugin> pluginByKey = Maps.newHashMap(); + private BiMap<String, Plugin> pluginByKey = HashBiMap.create(); private Map<Object, Plugin> pluginByExtension = Maps.newIdentityHashMap(); protected void registerPlugin(MutablePicoContainer container, Plugin plugin, String pluginKey) { @@ -49,15 +51,30 @@ public abstract class AbstractPluginRepository implements PluginRepository { } } + /** + * Must be executed by implementations when all plugins are registered. + */ + protected void invokeExtensionProviders(MutablePicoContainer container) { + List<ExtensionProvider> providers = container.getComponents(ExtensionProvider.class); + for (ExtensionProvider provider : providers) { + Plugin plugin = getPluginForExtension(provider); + Object obj = provider.provide(); + if (obj instanceof Iterable) { + for (Object elt : (Iterable) obj) { + registerExtension(container, plugin, getPluginKey(plugin), elt); + } + } else { + registerExtension(container, plugin, getPluginKey(plugin), obj); + } + } + } + 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)); + } } @@ -71,6 +88,10 @@ public abstract class AbstractPluginRepository implements PluginRepository { return pluginByKey.get(key); } + public String getPluginKey(Plugin plugin) { + return pluginByKey.inverse().get(plugin); + } + /** * Returns the list of properties of a plugin */ @@ -102,15 +123,7 @@ public abstract class AbstractPluginRepository implements PluginRepository { } protected static boolean isExtensionProvider(Object extension) { - boolean is = false; - if (isType(extension, ExtensionProvider.class)) { - if (extension instanceof ExtensionProvider) { - is = true; - } else { - LOG.error("The following ExtensionProvider must be registered in Plugin.getExtensions() as an instance but not a class: " + extension); - } - } - return is; + return isType(extension, ExtensionProvider.class); } protected static Object getExtensionKey(Object component) { @@ -119,10 +132,4 @@ public abstract class AbstractPluginRepository implements PluginRepository { } return component.getClass().getCanonicalName() + "-" + component.toString(); } - - public static class ExtensionProviderAdapter extends ProviderAdapter { - public ExtensionProviderAdapter(Object provider) { - super(provider); - } - } } diff --git a/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java b/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java index 319150c5c95..de2343a07f5 100644 --- a/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java +++ b/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java @@ -28,6 +28,7 @@ import org.sonar.api.ServerExtension; import org.sonar.api.utils.IocContainer; import java.util.Arrays; +import java.util.Collection; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.notNullValue; @@ -61,9 +62,9 @@ public class AbstractPluginRepositoryTest { } @Test - public void extensionProviderIsAnObjectButNotAClass() { - assertThat(AbstractPluginRepository.isExtensionProvider(BProvider.class), is(false)); - assertThat(AbstractPluginRepository.isExtensionProvider(new BProvider()), is(true)); + public void shouldBeExtensionProvider() { + assertThat(AbstractPluginRepository.isExtensionProvider(BProvider.class), is(true)); + assertThat(AbstractPluginRepository.isExtensionProvider(new BProvider(new A())), is(true)); } @Test @@ -77,20 +78,18 @@ public class AbstractPluginRepositoryTest { }; Plugin plugin = mock(Plugin.class); - BProvider bProvider = new BProvider(); - when(plugin.getExtensions()).thenReturn(Arrays.asList(A.class, bProvider, C.class, D.class)); + when(plugin.getExtensions()).thenReturn(Arrays.asList(A.class, BProvider.class, B.class, C.class, D.class)); repository.registerPlugin(pico, plugin, "foo"); + repository.invokeExtensionProviders(pico); 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 + assertThat(pico.getComponent(C.class).getBees().length, is(3));// 1 in plugin.getExtensions() + 2 created by BProvider + assertThat(pico.getComponent(D.class).getBees().length, is(3)); + assertThat(pico.getComponent(BProvider.class).calls, is(1)); // do not create B instances two times (C and D dependencies) + assertThat(pico.getComponents(B.class).size(), is(3)); } public static class FakeServerExtension implements ServerExtension { @@ -115,6 +114,7 @@ public class AbstractPluginRepositoryTest { } } + public static class C implements ServerExtension { private B[] bees; @@ -139,12 +139,18 @@ public class AbstractPluginRepositoryTest { } } - public static class BProvider extends ExtensionProvider { + public static class BProvider extends ExtensionProvider implements ServerExtension { private int calls = 0; - public B[] provide(A a) { + private A a; + + public BProvider(A a) { + this.a = a; + } + + public Collection<B> provide() { calls++; - return new B[]{new B(a), new B(a)}; + return Arrays.asList(new B(a), new B(a)); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/ExtensionProvider.java b/sonar-plugin-api/src/main/java/org/sonar/api/ExtensionProvider.java index 40dcbba5d97..02ad1ff5c05 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/ExtensionProvider.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/ExtensionProvider.java @@ -19,38 +19,43 @@ */ package org.sonar.api; -import org.picocontainer.injectors.Provider; - /** * Factory of extensions. It allows to dynamically create extensions depending upon runtime context. A use-case is * to create one rule repository by language. * - * <p>Constraints are : + * <p>Notes : * <ul> - * <li>the factory is declared in Plugin.getExtensions() as an instance but not as a class</li> - * <li>the factory must have a public method named "provide()"</li> - * <li>the method provide() must return an object or an array of objects. Collections and classes are excluded.</li> - * <li>the methode provide() can accept parameters. These parameters are IoC dependencies. + * <li>the provider is declared in Plugin.getExtensions()</li> + * <li>the provider must also implement ServerExtension and/or BatchExtension</li> + * <li>the provider can accept dependencies (parameters) in its constructors.</li> + * <li>the method provide() is automatically executed once by sonar</li> + * <li>the method provide() must return an object or an Iterable of objects. <strong>Arrays are excluded</strong>.</li> * </ul> * </p> * * <p>Example: * <pre> - * public class RuleRepositoryProvider extends ExtensionProvider { - * public RuleRepository[] provide(Language[] languages) { - * RuleRepository[] result = new RuleRepository[languages.length]; - * for(int index=0; index < languages.length ; index++) { - * Language language = languages[index]; - * result[index] = new RuleRepository(...); + * public class RuleRepositoryProvider extends ExtensionProvider implements ServerExtension { + * private Language[] languages; + * + * public RuleRepositoryProvider(Language[] languages) { + * this.languages = languages; + * } + * + * public List<RuleRepository> provide() { + * List<RuleRepository> result = new ArrayList<RuleRepository>(); + * for(Language language: languages) { + * result.add(new RuleRepository(..., language, ...)); * } * return result; * } * } * </pre> * </p> - * + * * @since 2.3 */ -public abstract class ExtensionProvider implements Extension, Provider { +public abstract class ExtensionProvider implements Extension { + public abstract Object provide(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java index 8529642a9f1..49e42cd4c8c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java @@ -31,6 +31,9 @@ public interface PluginRepository extends BatchComponent, ServerComponent { Plugin getPlugin(String key); + /** + * @deprecated since 2.3 + */ @Deprecated Plugin getPluginForExtension(Object extension); diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java b/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java index cfb1191fc11..318a5ac5769 100644 --- a/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java +++ b/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java @@ -60,6 +60,7 @@ public class ServerPluginRepository extends AbstractPluginRepository { throw new SonarException("Please check the plugin manifest. The main plugin class does not exist: " + jpaPlugin.getPluginClass(), e); } } + invokeExtensionProviders(pico); } @Override |