diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2011-10-05 00:44:37 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@gmail.com> | 2011-10-07 13:36:25 +0200 |
commit | ef5bf7fdece84e5a52c8d478c8a733ecdf4d57f1 (patch) | |
tree | c64a8a041183379e92fbc81becc77af07c9b38e3 /sonar-plugin-api | |
parent | 3174a29201e922db758b51f4b693ca131e7037ec (diff) | |
download | sonarqube-ef5bf7fdece84e5a52c8d478c8a733ecdf4d57f1.tar.gz sonarqube-ef5bf7fdece84e5a52c8d478c8a733ecdf4d57f1.zip |
SONAR-2861 New Configuration API
The component org.apache.commons.Configuration is still available but plugins should use org.sonar.api.config.Settings.
It also implies the following issues :
SONAR-2870 do not rebuild the WAR file when editing sonar.properties
SONAR-2869 allow to use the annotations @Properties/@Property on extensions
Diffstat (limited to 'sonar-plugin-api')
14 files changed, 956 insertions, 115 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java index dddea1ccc0b..e7b62cf8dde 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java @@ -23,14 +23,12 @@ import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import org.apache.commons.lang.ClassUtils; -import org.picocontainer.MutablePicoContainer; -import org.picocontainer.PicoContainer; import org.sonar.api.BatchExtension; import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; import org.sonar.api.utils.AnnotationUtils; -import org.sonar.api.utils.IocContainer; import org.sonar.api.utils.dag.DirectAcyclicGraph; import java.lang.annotation.Annotation; @@ -45,14 +43,10 @@ import java.util.List; */ public class BatchExtensionDictionnary { - private MutablePicoContainer picoContainer; + private ComponentContainer componentContainer; - public BatchExtensionDictionnary(IocContainer iocContainer) { - this.picoContainer = iocContainer.getPicoContainer(); - } - - public BatchExtensionDictionnary(MutablePicoContainer picoContainer) { - this.picoContainer = picoContainer; + public BatchExtensionDictionnary(ComponentContainer componentContainer) { + this.componentContainer = componentContainer; } public <T> Collection<T> select(Class<T> type) { @@ -88,14 +82,14 @@ public class BatchExtensionDictionnary { private List<BatchExtension> getExtensions() { List<BatchExtension> extensions = Lists.newArrayList(); - completeBatchExtensions(picoContainer, extensions); + completeBatchExtensions(componentContainer, extensions); return extensions; } - private void completeBatchExtensions(PicoContainer picoContainer, List<BatchExtension> extensions) { - if (picoContainer!=null) { - extensions.addAll(picoContainer.getComponents(BatchExtension.class)); - completeBatchExtensions(picoContainer.getParent(), extensions); + private static void completeBatchExtensions(ComponentContainer container, List<BatchExtension> extensions) { + if (container != null) { + extensions.addAll(container.getComponentsByType(BatchExtension.class)); + completeBatchExtensions(container.getParent(), extensions); } } @@ -212,7 +206,7 @@ public class BatchExtensionDictionnary { if (result != null) { //TODO add arrays/collections of objects/classes if (result instanceof Class) { - results.addAll(picoContainer.getComponents((Class) result)); + results.addAll(componentContainer.getComponentsByType((Class) result)); } else if (result instanceof Collection) { results.addAll((Collection) result); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java new file mode 100644 index 00000000000..8190e157a49 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java @@ -0,0 +1,114 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * 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.config; + +import com.google.common.collect.Maps; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.BatchComponent; +import org.sonar.api.Properties; +import org.sonar.api.Property; +import org.sonar.api.ServerComponent; +import org.sonar.api.utils.AnnotationUtils; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; + +/** + * Metadata of all the properties declared by plugins + * + * @since 2.12 + */ +public final class PropertyDefinitions implements BatchComponent, ServerComponent { + + private Map<String, Property> properties = Maps.newHashMap(); + private Map<String, String> categories = Maps.newHashMap(); + + public PropertyDefinitions(Object... components) { + if (components != null) { + addComponents(Arrays.asList(components)); + } + } + + public PropertyDefinitions addComponents(Collection components) { + return addComponents(components, ""); + } + + public PropertyDefinitions addComponents(Collection components, String defaultCategory) { + for (Object component : components) { + addComponent(component, defaultCategory); + } + return this; + } + + public PropertyDefinitions addComponent(Object object) { + return addComponent(object, ""); + } + + public PropertyDefinitions addComponent(Object component, String defaultCategory) { + Properties annotations = AnnotationUtils.getClassAnnotation(component, Properties.class); + if (annotations != null) { + for (Property property : annotations.value()) { + addProperty(property, defaultCategory); + } + } + Property annotation = AnnotationUtils.getClassAnnotation(component, Property.class); + if (annotation != null) { + addProperty(annotation, defaultCategory); + } + return this; + } + + PropertyDefinitions addProperty(Property property) { + return addProperty(property, ""); + } + + PropertyDefinitions addProperty(Property property, String defaultCategory) { + if (!properties.containsKey(property.key())) { + properties.put(property.key(), property); + categories.put(property.key(), StringUtils.defaultIfBlank(property.category(), defaultCategory)); + } + return this; + } + + public Property getProperty(String key) { + return properties.get(key); + } + + public Collection<Property> getProperties() { + return properties.values(); + } + + public String getDefaultValue(String key) { + Property prop = getProperty(key); + if (prop != null) { + return StringUtils.defaultIfEmpty(prop.defaultValue(), null); + } + return null; + } + + public String getCategory(String key) { + return categories.get(key); + } + + public String getCategory(Property prop) { + return getCategory(prop.key()); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java new file mode 100644 index 00000000000..fc626bb5f9f --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java @@ -0,0 +1,251 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * 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.config; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.BatchComponent; +import org.sonar.api.ServerComponent; +import org.sonar.api.utils.DateUtils; + +import java.util.*; + +/** + * Project Settings on batch side, Global Settings on server side. + * <p/> + * Replace the deprecated component org.apache.commons.configuration.Configuration + * + * @since 2.12 + */ +public class Settings implements BatchComponent, ServerComponent { + + protected Map<String, String> properties = Maps.newHashMap(); + protected PropertyDefinitions definitions; + + public Settings() { + this(new PropertyDefinitions()); + } + + public Settings(PropertyDefinitions definitions) { + this.definitions = definitions; + } + + public final String getDefaultValue(String key) { + return definitions.getDefaultValue(key); + } + + public final boolean hasKey(String key) { + return properties.containsKey(key); + } + + public final boolean hasDefaultValue(String key) { + return StringUtils.isNotEmpty(getDefaultValue(key)); + } + + public final String getString(String key) { + String value = properties.get(key); + if (value==null) { + value = getDefaultValue(key); + } + return value; + } + + public final boolean getBoolean(String key) { + String value = getString(key); + return StringUtils.isNotEmpty(value) && Boolean.parseBoolean(value); + } + + public final int getInt(String key) { + String value = getString(key); + if (StringUtils.isNotEmpty(value)) { + return Integer.parseInt(value); + } + return 0; + } + + public final long getLong(String key) { + String value = getString(key); + if (StringUtils.isNotEmpty(value)) { + return Long.parseLong(value); + } + return 0L; + } + + public final Date getDate(String key) { + String value = getString(key); + if (StringUtils.isNotEmpty(value)) { + return DateUtils.parseDate(value); + } + return null; + } + + public final Date getDateTime(String key) { + String value = getString(key); + if (StringUtils.isNotEmpty(value)) { + return DateUtils.parseDateTime(value); + } + return null; + } + + public final String[] getStringArray(String key) { + return getStringArrayBySeparator(key, ","); + } + + public final String[] getStringArrayBySeparator(String key, String separator) { + String value = getString(key); + if (value != null) { + return StringUtils.splitByWholeSeparator(value, separator); + } + return ArrayUtils.EMPTY_STRING_ARRAY; + } + + public List<String> getKeysStartingWith(String prefix) { + List<String> result = Lists.newArrayList(); + for (String key : properties.keySet()) { + if (StringUtils.startsWith(key, prefix)) { + result.add(key); + } + } + return result; + } + + + + public final Settings appendProperty(String key, String value) { + String newValue = properties.get(key); + if (StringUtils.isEmpty(newValue)) { + newValue = value; + } else { + newValue += "," + value; + } + properties.put(key, newValue); + return this; + } + + public final Settings setProperty(String key, String value) { + if (!clearIfNullValue(key, value)) { + properties.put(key, value); + } + return this; + } + + public final Settings setProperty(String key, Boolean value) { + if (!clearIfNullValue(key, value)) { + properties.put(key, String.valueOf(value)); + } + return this; + } + + public final Settings setProperty(String key, Integer value) { + if (!clearIfNullValue(key, value)) { + properties.put(key, String.valueOf(value)); + } + return this; + } + + public final Settings setProperty(String key, Long value) { + if (!clearIfNullValue(key, value)) { + properties.put(key, String.valueOf(value)); + } + return this; + } + + public final Settings setProperty(String key, Double value) { + if (!clearIfNullValue(key, value)) { + properties.put(key, String.valueOf(value)); + } + return this; + } + + public final Settings setProperty(String key, Date date) { + return setProperty(key, date, false); + } + + public final Settings addProperties(Map<String, String> props) { + properties.putAll(props); + return this; + } + + public final Settings addProperties(Properties props) { + for (Map.Entry<Object, Object> entry : props.entrySet()) { + properties.put(entry.getKey().toString(), entry.getValue().toString()); + } + return this; + } + + public final Settings addSystemProperties() { + return addProperties(System.getProperties()); + } + + public final Settings addEnvironmentVariables() { + return addProperties(System.getenv()); + } + + public final Settings setProperties(Map<String, String> props) { + properties = Maps.newHashMap(props); + return this; + } + + public final Settings setProperty(String key, Date date, boolean includeTime) { + if (!clearIfNullValue(key, date)) { + properties.put(key, includeTime ? DateUtils.formatDateTime(date) : DateUtils.formatDate(date)); + } + return this; + } + + public final Settings removeProperty(String key) { + properties.remove(key); + return this; + } + + public final Settings clear() { + properties.clear(); + return this; + } + + /** + * @return unmodifiable properties + */ + public final Map<String, String> getProperties() { + return Collections.unmodifiableMap(properties); + } + + public final PropertyDefinitions getDefinitions() { + return definitions; + } + + private boolean clearIfNullValue(String key, Object value) { + if (value == null) { + properties.remove(key); + return true; + } + return false; + } + + /** + * Create empty settings. Definition of available properties is loaded from the given annotated class. + * This method is usually used by unit tests. + */ + public static Settings createForComponent(Object component) { + return new Settings(new PropertyDefinitions(component)); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java new file mode 100644 index 00000000000..53734229b65 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java @@ -0,0 +1,164 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * 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.platform; + +import org.picocontainer.Characteristics; +import org.picocontainer.ComponentAdapter; +import org.picocontainer.DefaultPicoContainer; +import org.picocontainer.MutablePicoContainer; +import org.picocontainer.behaviors.OptInCaching; +import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; +import org.picocontainer.monitors.NullComponentMonitor; +import org.sonar.api.BatchComponent; +import org.sonar.api.ServerComponent; +import org.sonar.api.config.PropertyDefinitions; + +/** + * @since 2.12 + */ +public class ComponentContainer implements BatchComponent, ServerComponent { + + ComponentContainer parent, child; // no need for multiple children + MutablePicoContainer pico; + PropertyDefinitions propertyDefinitions; + + /** + * Create root container + */ + public ComponentContainer() { + this.parent = null; + this.child = null; + this.pico = createPicoContainer(); + propertyDefinitions = new PropertyDefinitions(); + addSingleton(propertyDefinitions); + addSingleton(this); + } + + /** + * Create child container + */ + private ComponentContainer(ComponentContainer parent) { + this.parent = parent; + this.pico = parent.pico.makeChildContainer(); + this.parent.child = this; + this.propertyDefinitions = parent.propertyDefinitions; + addSingleton(this); + } + + /** + * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting + * a component twice is not authorized. + */ + public final ComponentContainer startComponents() { + pico.start(); + return this; + } + + /** + * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting + * a component twice is not authorized. + */ + public final ComponentContainer stopComponents() { + pico.stop(); + return this; + } + + public final ComponentContainer addSingleton(Object component) { + return addComponent(component, true); + } + + /** + * @param singleton return always the same instance if true, else a new instance + * is returned each time the component is requested + */ + public final ComponentContainer addComponent(Object component, boolean singleton) { + pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(getComponentKey(component), component); + propertyDefinitions.addComponent(component); + return this; + } + + public final ComponentContainer addExtension(PluginMetadata plugin, Object extension) { + pico.as(Characteristics.CACHE).addComponent(getComponentKey(extension), extension); + declareExtension(plugin, extension); + return this; + } + + public final void declareExtension(PluginMetadata plugin, Object extension) { + propertyDefinitions.addComponent(extension, plugin.getName()); + } + + public final ComponentContainer addPicoAdapter(ComponentAdapter adapter) { + pico.addAdapter(adapter); + return this; + } + + public final <T> T getComponentByType(Class<T> tClass) { + return pico.getComponent(tClass); + } + + public final Object getComponentByKey(Object key) { + return pico.getComponent(key); + } + + public final <T> java.util.List<T> getComponentsByType(java.lang.Class<T> tClass) { + return pico.getComponents(tClass); + } + + public final ComponentContainer removeChild() { + if (child != null) { + pico.removeChildContainer(child.pico); + child = null; + } + return this; + } + + public final ComponentContainer createChild() { + return new ComponentContainer(this); + } + + static MutablePicoContainer createPicoContainer() { + ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose"); + return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null); + } + + static Object getComponentKey(Object component) { + if (component instanceof Class) { + return component; + } + return new StringBuilder().append(component.getClass().getCanonicalName()).append("-").append(component.toString()).toString(); + } + + public ComponentContainer getParent() { + return parent; + } + + public ComponentContainer getChild() { + return child; + } + + /** + * Warning - do not use. This method exists only for the backward-compatibility due to the suppression + * of {@link org.sonar.api.utils.IocContainer} + * @return + */ + public MutablePicoContainer getPicoContainer() { + return pico; + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Languages.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Languages.java index 03062e94de3..ab9d26fef38 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Languages.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Languages.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.google.common.collect.Maps; import org.apache.commons.lang.ArrayUtils; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; @@ -36,7 +37,7 @@ import org.sonar.api.ServerComponent; */ public class Languages implements BatchComponent, ServerComponent { - private final Map<String, Language> map = new HashMap<String, Language>(); + private final Map<String, Language> map = Maps.newHashMap(); /** * Creates a list of languages diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java index dda4c228c52..99432b0313f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java @@ -420,7 +420,9 @@ public class Project extends Resource { /** * @return the project configuration + * @deprecated since 2.12. The component org.sonar.api.config.Settings must be used. */ + @Deprecated public Configuration getConfiguration() { return configuration; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java index 097fa7a2ba9..1893f02e5de 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java @@ -21,13 +21,12 @@ package org.sonar.api.utils; import com.google.common.base.Joiner; import com.google.common.collect.Lists; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; +import org.sonar.api.config.Settings; import org.sonar.api.platform.Server; import java.io.File; @@ -39,7 +38,7 @@ import java.util.List; /** * This component downloads HTTP files - * + * * @since 2.2 */ public class HttpDownloader implements BatchComponent, ServerComponent { @@ -48,30 +47,23 @@ public class HttpDownloader implements BatchComponent, ServerComponent { private String userAgent; - public HttpDownloader(Server server, Configuration configuration) { - this(configuration, server.getVersion()); - } - - public HttpDownloader(Configuration configuration) { - this(configuration, null); + public HttpDownloader(Server server, Settings settings) { + this(settings, server.getVersion()); } - /** - * Should be package protected for unit tests only, but public is still required for jacoco 0.2. - */ - public HttpDownloader() { - this(new PropertiesConfiguration(), null); + public HttpDownloader(Settings settings) { + this(settings, null); } - private HttpDownloader(Configuration configuration, String userAgent) { - initProxy(configuration); + private HttpDownloader(Settings settings, String userAgent) { + initProxy(settings); initUserAgent(userAgent); } - private void initProxy(Configuration configuration) { - propagateProxySystemProperties(configuration); - if (requiresProxyAuthentication(configuration)) { - registerProxyCredentials(configuration); + private void initProxy(Settings settings) { + propagateProxySystemProperties(settings); + if (requiresProxyAuthentication(settings)) { + registerProxyCredentials(settings); } } @@ -99,27 +91,27 @@ public class HttpDownloader implements BatchComponent, ServerComponent { return Joiner.on(", ").join(descriptions); } - private void registerProxyCredentials(Configuration configuration) { - Authenticator.setDefault(new ProxyAuthenticator(configuration.getString("http.proxyUser"), configuration + private void registerProxyCredentials(Settings settings) { + Authenticator.setDefault(new ProxyAuthenticator(settings.getString("http.proxyUser"), settings .getString("http.proxyPassword"))); } - private boolean requiresProxyAuthentication(Configuration configuration) { - return configuration.getString("http.proxyUser") != null; + private boolean requiresProxyAuthentication(Settings settings) { + return settings.getString("http.proxyUser") != null; } - private void propagateProxySystemProperties(Configuration configuration) { - propagateSystemProperty(configuration, "http.proxyHost"); - propagateSystemProperty(configuration, "http.proxyPort"); - propagateSystemProperty(configuration, "http.nonProxyHosts"); - propagateSystemProperty(configuration, "http.auth.ntlm.domain"); - propagateSystemProperty(configuration, "socksProxyHost"); - propagateSystemProperty(configuration, "socksProxyPort"); + private void propagateProxySystemProperties(Settings settings) { + propagateSystemProperty(settings, "http.proxyHost"); + propagateSystemProperty(settings, "http.proxyPort"); + propagateSystemProperty(settings, "http.nonProxyHosts"); + propagateSystemProperty(settings, "http.auth.ntlm.domain"); + propagateSystemProperty(settings, "socksProxyHost"); + propagateSystemProperty(settings, "socksProxyPort"); } - private void propagateSystemProperty(Configuration configuration, String key) { - if (configuration.getString(key) != null) { - System.setProperty(key, configuration.getString(key)); + private void propagateSystemProperty(Settings settings, String key) { + if (settings.getString(key) != null) { + System.setProperty(key, settings.getString(key)); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/IocContainer.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/IocContainer.java index 7791d04fd49..9d348f248c9 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/IocContainer.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/IocContainer.java @@ -25,12 +25,14 @@ import org.picocontainer.MutablePicoContainer; import org.picocontainer.behaviors.OptInCaching; import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; import org.picocontainer.monitors.NullComponentMonitor; +import org.sonar.api.platform.ComponentContainer; /** * Proxy to inject the container as a component$ * * @since 1.10 + * @deprecated since 2.12. To be replaced by {@link org.sonar.api.platform.ComponentContainer} */ public class IocContainer { private final MutablePicoContainer pico; @@ -39,6 +41,10 @@ public class IocContainer { this.pico = pico; } + public IocContainer(ComponentContainer container) { + this.pico = container.getPicoContainer(); + } + public MutablePicoContainer getPicoContainer() { return pico; } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java index 37e6e644c49..bda9de6d1bc 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java @@ -21,16 +21,12 @@ package org.sonar.api.batch; import com.google.common.collect.Lists; import org.junit.Test; -import org.picocontainer.Characteristics; -import org.picocontainer.DefaultPicoContainer; -import org.picocontainer.PicoContainer; -import org.picocontainer.containers.TransientPicoContainer; import org.sonar.api.BatchExtension; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; +import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; -import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -44,10 +40,9 @@ import static org.mockito.Mockito.mock; public class BatchExtensionDictionnaryTest { private BatchExtensionDictionnary newSelector(BatchExtension... extensions) { - TransientPicoContainer iocContainer = new TransientPicoContainer(); - int key = 0; + ComponentContainer iocContainer = new ComponentContainer(); for (BatchExtension extension : extensions) { - iocContainer.addComponent(key++, extension); + iocContainer.addSingleton(extension); } return new BatchExtensionDictionnary(iocContainer); } @@ -67,14 +62,14 @@ public class BatchExtensionDictionnaryTest { @Test public void shouldSearchInParentContainers() { - TransientPicoContainer grandParent = new TransientPicoContainer(); - grandParent.as(Characteristics.CACHE).addComponent("ncloc", CoreMetrics.NCLOC); + ComponentContainer grandParent = new ComponentContainer(); + grandParent.addSingleton(CoreMetrics.NCLOC); - DefaultPicoContainer parent = (DefaultPicoContainer)grandParent.makeChildContainer(); - parent.as(Characteristics.CACHE).addComponent("coverage", CoreMetrics.COVERAGE); + ComponentContainer parent = grandParent.createChild(); + parent.addSingleton(CoreMetrics.COVERAGE); - DefaultPicoContainer child = (DefaultPicoContainer)parent.makeChildContainer(); - child.as(Characteristics.CACHE).addComponent("complexity", CoreMetrics.COMPLEXITY); + ComponentContainer child = parent.createChild(); + child.addSingleton(CoreMetrics.COMPLEXITY); BatchExtensionDictionnary dictionnary = new BatchExtensionDictionnary(child); assertThat(dictionnary.select(Metric.class).size(), is(3)); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java new file mode 100644 index 00000000000..857dbb82aed --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java @@ -0,0 +1,93 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * 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.config; + +import org.junit.Test; +import org.sonar.api.Properties; +import org.sonar.api.Property; + +import java.util.Arrays; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +public class PropertyDefinitionsTest { + + @Test + public void shouldInspectPluginObjects() { + PropertyDefinitions def = new PropertyDefinitions(new PluginWithProperty(), new PluginWithProperties()); + + assertProperties(def); + } + + @Test + public void shouldInspectPluginClasses() { + PropertyDefinitions def = new PropertyDefinitions(PluginWithProperty.class, PluginWithProperties.class); + + assertProperties(def); + } + + private void assertProperties(PropertyDefinitions def) { + assertThat(def.getProperty("foo").name(), is("Foo")); + assertThat(def.getProperty("one").name(), is("One")); + assertThat(def.getProperty("two").name(), is("Two")); + assertThat(def.getProperty("unknown"), nullValue()); + + assertThat(def.getDefaultValue("foo"), nullValue()); + assertThat(def.getDefaultValue("two"), is("2")); + + assertThat(def.getProperties().size(), is(3)); + } + + @Property(key = "foo", name = "Foo") + static final class PluginWithProperty { + } + + @Properties({ + @Property(key = "one", name = "One"), + @Property(key = "two", name = "Two", defaultValue = "2") + }) + static final class PluginWithProperties { + } + + + @Test + public void testCategories() { + PropertyDefinitions def = new PropertyDefinitions(Categories.class); + assertThat(def.getCategory("inCateg"), is("categ")); + assertThat(def.getCategory("noCateg"), is("")); + } + + @Test + public void testDefaultCategory() { + PropertyDefinitions def = new PropertyDefinitions(); + def.addComponent(Categories.class, "default"); + assertThat(def.getCategory("inCateg"), is("categ")); + assertThat(def.getCategory("noCateg"), is("default")); + } + + @Properties({ + @Property(key = "inCateg", name="In Categ", category = "categ"), + @Property(key = "noCateg", name="No categ") + }) + static final class Categories { + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java new file mode 100644 index 00000000000..c34f1278ca4 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java @@ -0,0 +1,124 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * 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.config; + +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.Property; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SettingsTest { + + private PropertyDefinitions definitions; + + @Before + public void initDefinitions() { + definitions = new PropertyDefinitions(); + definitions.addProperty(newProperty("hello", "world")); + definitions.addProperty(newProperty("date", "2010-05-18")); + definitions.addProperty(newProperty("boolean", "true")); + definitions.addProperty(newProperty("falseboolean", "false")); + definitions.addProperty(newProperty("integer", "12345")); + definitions.addProperty(newProperty("array", "one,two,three")); + } + + private Property newProperty(String key, String defaultValue) { + Property prop = mock(Property.class); + when(prop.key()).thenReturn(key); + when(prop.defaultValue()).thenReturn(defaultValue); + return prop; + } + + @Test + public void defaultValuesShouldBeLoadedFromDefinitions() { + Settings settings = new Settings(definitions); + assertThat(settings.getDefaultValue("hello"), is("world")); + } + + @Test + public void testGetDefaultValue() { + Settings settings = new Settings(definitions); + assertThat(settings.getDefaultValue("unknown"), nullValue()); + } + + @Test + public void testGetString() { + Settings settings = new Settings(definitions); + settings.setProperty("hello", "Russia"); + assertThat(settings.getString("hello"), is("Russia")); + } + + @Test + public void testGetDate() { + Settings settings = new Settings(definitions); + assertThat(settings.getDate("date").getDate(), is(18)); + assertThat(settings.getDate("date").getMonth(), is(4)); + } + + @Test + public void testGetDateNotFound() { + Settings settings = new Settings(definitions); + assertThat(settings.getDate("unknown"), CoreMatchers.<Object>nullValue()); + } + + @Test + public void testGetArray() { + Settings settings = new Settings(definitions); + String[] array = settings.getStringArray("array"); + assertThat(array.length, is(3)); + assertThat(array[0], is("one")); + assertThat(array[1], is("two")); + assertThat(array[2], is("three")); + } + + @Test + public void testDefaultValueOfGetString() { + Settings settings = new Settings(definitions); + assertThat(settings.getString("hello"), is("world")); + } + + @Test + public void testGetBoolean() { + Settings settings = new Settings(definitions); + assertThat(settings.getBoolean("boolean"), is(true)); + assertThat(settings.getBoolean("falseboolean"), is(false)); + assertThat(settings.getBoolean("unknown"), is(false)); + assertThat(settings.getBoolean("hello"), is(false)); + } + + @Test + public void shouldCreateByIntrospectingComponent() { + Settings settings = Settings.createForComponent(MyComponent.class); + + // property definition has been loaded, ie for default value + assertThat(settings.getDefaultValue("foo"), is("bar")); + } + + @Property(key="foo", name="Foo", defaultValue = "bar") + public static class MyComponent { + + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java new file mode 100644 index 00000000000..f5a173598c3 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java @@ -0,0 +1,147 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * 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.platform; + +import org.junit.Test; +import org.sonar.api.Property; +import org.sonar.api.config.PropertyDefinitions; + +import static junit.framework.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +public class ComponentContainerTest { + + @Test + public void shouldRegisterItself() { + ComponentContainer container = new ComponentContainer(); + assertTrue(container.getComponentByType(ComponentContainer.class) == container); + } + + @Test + public void testStartAndStop() { + ComponentContainer container = new ComponentContainer(); + container.addSingleton(StartableComponent.class); + container.startComponents(); + + assertThat(container.getComponentByType(StartableComponent.class).started, is(true)); + assertThat(container.getComponentByType(StartableComponent.class).stopped, is(false)); + + container.stopComponents(); + assertThat(container.getComponentByType(StartableComponent.class).stopped, is(true)); + } + + @Test + public void testChild() { + ComponentContainer parent = new ComponentContainer(); + parent.startComponents(); + + ComponentContainer child = parent.createChild(); + child.addSingleton(StartableComponent.class); + child.startComponents(); + + assertTrue(child.getParent() == parent); + assertTrue(parent.getChild() == child); + assertTrue(child.getComponentByType(ComponentContainer.class) == child); + assertTrue(parent.getComponentByType(ComponentContainer.class) == parent); + assertThat(child.getComponentByType(StartableComponent.class), notNullValue()); + assertThat(parent.getComponentByType(StartableComponent.class), nullValue()); + + parent.stopComponents(); + } + + @Test + public void testRemoveChild() { + ComponentContainer parent = new ComponentContainer(); + parent.startComponents(); + + ComponentContainer child = parent.createChild(); + assertTrue(parent.getChild() == child); + + parent.removeChild(); + assertThat(parent.getChild(), nullValue()); + } + + @Test + public void shouldForwardStartAndStopToDescendants() { + ComponentContainer grandParent = new ComponentContainer(); + ComponentContainer parent = grandParent.createChild(); + ComponentContainer child = parent.createChild(); + child.addSingleton(StartableComponent.class); + + grandParent.startComponents(); + + StartableComponent component = child.getComponentByType(StartableComponent.class); + assertTrue(component.started); + + parent.stopComponents(); + assertTrue(component.stopped); + } + + @Test + public void shouldDeclareComponentProperties() { + ComponentContainer container = new ComponentContainer(); + container.addSingleton(ComponentWithProperty.class); + + PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class); + assertThat(propertyDefinitions.getProperty("foo"), notNullValue()); + assertThat(propertyDefinitions.getProperty("foo").defaultValue(), is("bar")); + } + + @Test + public void shouldDeclareExtensionWithoutAddingIt() { + ComponentContainer container = new ComponentContainer(); + PluginMetadata plugin = mock(PluginMetadata.class); + container.declareExtension(plugin, ComponentWithProperty.class); + + PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class); + assertThat(propertyDefinitions.getProperty("foo"), notNullValue()); + assertThat(container.getComponentByType(ComponentWithProperty.class), nullValue()); + } + + @Test + public void shouldDeclareExtensionWhenAdding() { + ComponentContainer container = new ComponentContainer(); + PluginMetadata plugin = mock(PluginMetadata.class); + container.addExtension(plugin, ComponentWithProperty.class); + + PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class); + assertThat(propertyDefinitions.getProperty("foo"), notNullValue()); + assertThat(container.getComponentByType(ComponentWithProperty.class), notNullValue()); + } + + public static class StartableComponent { + public boolean started = false, stopped = false; + + public void start() { + started = true; + } + + public void stop() { + stopped = true; + } + } + + @Property(key = "foo", defaultValue = "bar", name = "Foo") + public static class ComponentWithProperty { + + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java index e40cffe871b..257b52b90ab 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java @@ -19,15 +19,14 @@ */ package org.sonar.api.utils; -import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.SystemUtils; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.mortbay.jetty.testing.ServletTester; +import org.sonar.api.config.Settings; import org.sonar.api.platform.Server; import java.io.File; @@ -37,10 +36,8 @@ import java.util.Arrays; import java.util.Properties; import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.not; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; -import static org.junit.Assume.assumeThat; import static org.junit.internal.matchers.StringContains.containsString; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.mock; @@ -161,20 +158,20 @@ public class HttpDownloaderTest { @Test public void downloadBytes() throws URISyntaxException { - byte[] bytes = new HttpDownloader().download(new URI(baseUrl)); + byte[] bytes = new HttpDownloader(new Settings()).download(new URI(baseUrl)); assertThat(bytes.length, greaterThan(10)); } @Test public void downloadPlainText() throws URISyntaxException { - String text = new HttpDownloader().downloadPlainText(new URI(baseUrl), "UTF-8"); + String text = new HttpDownloader(new Settings()).downloadPlainText(new URI(baseUrl), "UTF-8"); assertThat(text.length(), greaterThan(10)); } @Test(expected = SonarException.class) public void failIfServerDown() throws URISyntaxException { // I hope that the port 1 is not used ! - new HttpDownloader().download(new URI("http://localhost:1/unknown")); + new HttpDownloader(new Settings()).download(new URI("http://localhost:1/unknown")); } @Test @@ -184,7 +181,7 @@ public class HttpDownloaderTest { FileUtils.cleanDirectory(toDir); File toFile = new File(toDir, "downloadToFile.txt"); - new HttpDownloader().download(new URI(baseUrl), toFile); + new HttpDownloader(new Settings()).download(new URI(baseUrl), toFile); assertThat(toFile.exists(), is(true)); assertThat(toFile.length(), greaterThan(10l)); } @@ -198,7 +195,7 @@ public class HttpDownloaderTest { try { // I hope that the port 1 is not used ! - new HttpDownloader().download(new URI("http://localhost:1/unknown"), toFile); + new HttpDownloader(new Settings()).download(new URI("http://localhost:1/unknown"), toFile); } catch (SonarException e) { assertThat(toFile.exists(), is(false)); } @@ -209,7 +206,7 @@ public class HttpDownloaderTest { Server server = mock(Server.class); when(server.getVersion()).thenReturn("2.2"); - byte[] bytes = new HttpDownloader(server, new PropertiesConfiguration()).download(new URI(baseUrl)); + byte[] bytes = new HttpDownloader(server, new Settings()).download(new URI(baseUrl)); Properties props = new Properties(); props.load(IOUtils.toInputStream(new String(bytes))); assertThat(props.getProperty("agent"), is("Sonar 2.2")); @@ -217,7 +214,7 @@ public class HttpDownloaderTest { @Test public void followRedirect() throws URISyntaxException { - byte[] bytes = new HttpDownloader().download(new URI(baseUrl + "/redirect/")); + byte[] bytes = new HttpDownloader(new Settings()).download(new URI(baseUrl + "/redirect/")); assertThat(new String(bytes), containsString("count")); } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/IocContainerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/IocContainerTest.java deleted file mode 100644 index abe3578c40c..00000000000 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/IocContainerTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2011 SonarSource - * 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.utils; - -import static junit.framework.Assert.assertTrue; -import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.core.IsNot.not; -import static org.junit.Assert.assertThat; -import org.junit.Test; -import org.picocontainer.MutablePicoContainer; - -public class IocContainerTest { - - @Test - public void injectContainerAsComponent() { - MutablePicoContainer container = IocContainer.buildPicoContainer(); - assertThat(container.getComponent(IocContainer.class), not(nullValue())); - - // only one instance - assertTrue(container.getComponent(IocContainer.class) == container.getComponent(IocContainer.class)); - } -} |