diff options
Diffstat (limited to 'sonar-core')
-rw-r--r-- | sonar-core/src/main/java/org/sonar/core/plugin/AbstractPluginRepository.java | 120 | ||||
-rw-r--r-- | sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java | 152 |
2 files changed, 272 insertions, 0 deletions
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 new file mode 100644 index 00000000000..24a40d9fd68 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/plugin/AbstractPluginRepository.java @@ -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); + } + } +} 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 new file mode 100644 index 00000000000..319150c5c95 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java @@ -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)}; + } + } + + +} |