]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1814 add the method ExtensionProvider.provide() in order to support obfuscated...
authorsimonbrandhof <simon.brandhof@gmail.com>
Tue, 28 Sep 2010 15:53:04 +0000 (15:53 +0000)
committersimonbrandhof <simon.brandhof@gmail.com>
Tue, 28 Sep 2010 15:53:04 +0000 (15:53 +0000)
sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java
sonar-core/src/main/java/org/sonar/core/plugin/AbstractPluginRepository.java
sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java
sonar-plugin-api/src/main/java/org/sonar/api/ExtensionProvider.java
sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java
sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java

index 5544762ad7a450a8577a5d8effd3dee9a91007b6..51a2e095380bc91d2467f6190de117a0cded4c2f 100644 (file)
@@ -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) {
index b62bf9830451e43f75cafbed9ab69470f56bbe90..9a15c9e06bab406d29d7b0acbb59abf1e500edf6 100644 (file)
  */
 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);
-    }
-  }
 }
index 319150c5c9570c37b241861d57cc7d21f98c6e24..de2343a07f5f7b7ca56b67a87ec479799605a7e3 100644 (file)
@@ -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));
     }
   }
 
index 40dcbba5d974029a1cba9bacdfccc9c310e0cba8..02ad1ff5c05fc23aade3d9c3dcf81f686e4eb0b0 100644 (file)
  */
 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 &lt; 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();
 }
index 8529642a9f16c2129599db7066c3c3df190f6955..49e42cd4c8cff863e8f91495e50ebaab85e3e0b3 100644 (file)
@@ -31,6 +31,9 @@ public interface PluginRepository extends BatchComponent, ServerComponent {
 
   Plugin getPlugin(String key);
 
+  /**
+   * @deprecated since 2.3
+   */
   @Deprecated
   Plugin getPluginForExtension(Object extension);
 
index cfb1191fc11ad6eb1d9be270be2bc02be767e41e..318a5ac57694a45c4126ed67cde9b92ae1231eef 100644 (file)
@@ -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