From: simonbrandhof Date: Tue, 28 Sep 2010 15:53:04 +0000 (+0000) Subject: SONAR-1814 add the method ExtensionProvider.provide() in order to support obfuscated... X-Git-Tag: 2.6~925 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=f25c971275702a3a53195b568fe3c91469076c83;p=sonarqube.git SONAR-1814 add the method ExtensionProvider.provide() in order to support obfuscated code --- 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 pluginByKey = Maps.newHashMap(); + private BiMap pluginByKey = HashBiMap.create(); private Map 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 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 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. * - *

Constraints are : + *

Notes : *

    - *
  • the factory is declared in Plugin.getExtensions() as an instance but not as a class
  • - *
  • the factory must have a public method named "provide()"
  • - *
  • the method provide() must return an object or an array of objects. Collections and classes are excluded.
  • - *
  • the methode provide() can accept parameters. These parameters are IoC dependencies. + *
  • the provider is declared in Plugin.getExtensions()
  • + *
  • the provider must also implement ServerExtension and/or BatchExtension
  • + *
  • the provider can accept dependencies (parameters) in its constructors.
  • + *
  • the method provide() is automatically executed once by sonar
  • + *
  • the method provide() must return an object or an Iterable of objects. Arrays are excluded.
  • *
*

* *

Example: *

- * 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 provide() {
+ *     List result = new ArrayList();
+ *     for(Language language: languages) {
+ *       result.add(new RuleRepository(..., language, ...));
  *     }
  *     return result;
  *   }
  * }
  * 
*

- * + * * @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