diff --git a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/ConfigurableService.java b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/ConfigurableService.java index 497585f1b..696dd5ad3 100644 --- a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/ConfigurableService.java +++ b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/ConfigurableService.java @@ -17,6 +17,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.osgi.framework.Constants; import org.osgi.service.component.ComponentConstants; import org.osgi.service.component.annotations.ComponentPropertyType; @@ -25,9 +26,8 @@ import org.osgi.service.component.annotations.ComponentPropertyType; *

* {@link ConfigurableService} can be used as a marker interface for configurable services. But the interface itself is * not relevant for the runtime. Each service which has the property - * {@link ConfigurableService#SERVICE_PROPERTY_DESCRIPTION_URI} set will be considered as a configurable service. The - * properties {@link ConfigurableService#SERVICE_PROPERTY_LABEL} and - * {@link ConfigurableService#SERVICE_PROPERTY_CATEGORY} are optional. + * {@link ConfigurableService#description_uri} set will be considered as a configurable service. The + * properties {@link ConfigurableService#label} and {@link ConfigurableService#category} are optional. * *

* The services are configured through the OSGi configuration admin. Therefore each service must provide a PID or a @@ -41,6 +41,7 @@ import org.osgi.service.component.annotations.ComponentPropertyType; @ComponentPropertyType @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) +@NonNullByDefault public @interface ConfigurableService { String PREFIX_ = "service.config."; @@ -64,36 +65,4 @@ public @interface ConfigurableService { * Marker for multiple configurations for this service ("true" = multiple configurations possible) */ boolean factory() default false; - - /** - * The config description URI for the configurable service. See also {@link ConfigDescription}. - * - * @deprecated annotate classes with @ConfigurableService instead - */ - @Deprecated - public static final String SERVICE_PROPERTY_DESCRIPTION_URI = PREFIX_ + "description.uri"; - - /** - * The label of the service to be configured. - * - * @deprecated annotate classes with @ConfigurableService instead - */ - @Deprecated - public static final String SERVICE_PROPERTY_LABEL = PREFIX_ + "label"; - - /** - * The category of the service to be configured (e.g. binding). - * - * @deprecated annotate classes with @ConfigurableService instead - */ - @Deprecated - public static final String SERVICE_PROPERTY_CATEGORY = PREFIX_ + "category"; - - /** - * Marker for multiple configurations for this service ("true" = multiple configurations possible) - * - * @deprecated annotate classes with @ConfigurableService instead - */ - @Deprecated - public static final String SERVICE_PROPERTY_FACTORY_SERVICE = PREFIX_ + "factory"; } diff --git a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/ConfigurableServiceUtil.java b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/ConfigurableServiceUtil.java new file mode 100644 index 000000000..d015f260c --- /dev/null +++ b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/ConfigurableServiceUtil.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.config.core; + +import java.lang.annotation.Annotation; +import java.util.function.Function; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Provides utility methods for working with {@link ConfigurableService} so the property names can remain hidden. + * These methods cannot be part of {@link ConfigurableService} as that introduces an annotation cycle. + * + * @author Wouter Born - Initial contribution + */ +@NonNullByDefault +public class ConfigurableServiceUtil { + + /** + * The config description URI for the configurable service. See also {@link ConfigDescription}. + */ + static final String SERVICE_PROPERTY_DESCRIPTION_URI = ConfigurableService.PREFIX_ + "description.uri"; + + /** + * The label of the service to be configured. + */ + static final String SERVICE_PROPERTY_LABEL = ConfigurableService.PREFIX_ + "label"; + + /** + * The category of the service to be configured (e.g. binding). + */ + static final String SERVICE_PROPERTY_CATEGORY = ConfigurableService.PREFIX_ + "category"; + + /** + * Marker for multiple configurations for this service ("true" = multiple configurations possible) + */ + static final String SERVICE_PROPERTY_FACTORY_SERVICE = ConfigurableService.PREFIX_ + "factory"; + + // all singleton services without multi-config services + public static final String CONFIGURABLE_SERVICE_FILTER = "(&(" + SERVICE_PROPERTY_DESCRIPTION_URI + "=*)(!(" + + SERVICE_PROPERTY_FACTORY_SERVICE + "=*)))"; + + // all multi-config services without singleton services + public static final String CONFIGURABLE_MULTI_CONFIG_SERVICE_FILTER = "(" + SERVICE_PROPERTY_FACTORY_SERVICE + + "=*)"; + + public static ConfigurableService asConfigurableService(Function propertyResolver) { + return new ConfigurableService() { + @Override + public Class annotationType() { + return ConfigurableService.class; + } + + @Override + public String label() { + return resolveString(propertyResolver, SERVICE_PROPERTY_LABEL); + } + + @Override + public boolean factory() { + return resolveBoolean(propertyResolver, SERVICE_PROPERTY_FACTORY_SERVICE); + } + + @Override + public String description_uri() { + return resolveString(propertyResolver, SERVICE_PROPERTY_DESCRIPTION_URI); + } + + @Override + public String category() { + return resolveString(propertyResolver, SERVICE_PROPERTY_CATEGORY); + } + }; + } + + private static String resolveString(Function propertyResolver, String key) { + String value = (String) propertyResolver.apply(key); + return value == null ? "" : value; + } + + private static boolean resolveBoolean(Function propertyResolver, String key) { + Boolean value = (Boolean) propertyResolver.apply(key); + return value == null ? false : value.booleanValue(); + } +} diff --git a/bundles/org.openhab.core.config.core/src/test/java/org/openhab/core/config/core/ConfigurableServiceUtilTest.java b/bundles/org.openhab.core.config.core/src/test/java/org/openhab/core/config/core/ConfigurableServiceUtilTest.java new file mode 100644 index 000000000..681445deb --- /dev/null +++ b/bundles/org.openhab.core.config.core/src/test/java/org/openhab/core/config/core/ConfigurableServiceUtilTest.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.config.core; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.text.IsEmptyString.emptyString; +import static org.openhab.core.config.core.ConfigurableServiceUtil.*; + +import java.util.Properties; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link ConfigurableServiceUtil}. + * + * @author Wouter Born - Initial contribution + */ +@NonNullByDefault +public class ConfigurableServiceUtilTest { + + @Test + public void asConfigurableServiceDefinedProperties() { + String category = "system"; + String descriptionURI = "system:inbox"; + boolean factory = true; + String label = "Inbox"; + + Properties properties = new Properties(); + properties.put(SERVICE_PROPERTY_CATEGORY, category); + properties.put(SERVICE_PROPERTY_DESCRIPTION_URI, descriptionURI); + properties.put(SERVICE_PROPERTY_FACTORY_SERVICE, factory); + properties.put(SERVICE_PROPERTY_LABEL, label); + + ConfigurableService configurableService = ConfigurableServiceUtil + .asConfigurableService((key) -> properties.get(key)); + + assertThat(configurableService.annotationType(), is(ConfigurableService.class)); + assertThat(configurableService.category(), is(category)); + assertThat(configurableService.description_uri(), is(descriptionURI)); + assertThat(configurableService.factory(), is(factory)); + assertThat(configurableService.label(), is(label)); + } + + @Test + public void asConfigurableServiceUndefinedProperties() { + Properties properties = new Properties(); + + ConfigurableService configurableService = ConfigurableServiceUtil + .asConfigurableService((key) -> properties.get(key)); + + assertThat(configurableService.annotationType(), is(ConfigurableService.class)); + assertThat(configurableService.category(), is(emptyString())); + assertThat(configurableService.description_uri(), is(emptyString())); + assertThat(configurableService.factory(), is(false)); + assertThat(configurableService.label(), is(emptyString())); + } +} diff --git a/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/service/ConfigurableServiceResource.java b/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/service/ConfigurableServiceResource.java index 9af62bc24..0a8060982 100644 --- a/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/service/ConfigurableServiceResource.java +++ b/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/service/ConfigurableServiceResource.java @@ -43,6 +43,7 @@ import org.openhab.core.config.core.ConfigDescription; import org.openhab.core.config.core.ConfigDescriptionRegistry; import org.openhab.core.config.core.ConfigUtil; import org.openhab.core.config.core.ConfigurableService; +import org.openhab.core.config.core.ConfigurableServiceUtil; import org.openhab.core.config.core.Configuration; import org.openhab.core.io.rest.RESTConstants; import org.openhab.core.io.rest.RESTResource; @@ -98,15 +99,6 @@ public class ConfigurableServiceResource implements RESTResource { /** The URI path to this resource */ public static final String PATH_SERVICES = "services"; - // all singleton services without multi-config services - private static final String CONFIGURABLE_SERVICE_FILTER = "(&(" - + ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=*)(!(" - + ConfigurableService.SERVICE_PROPERTY_FACTORY_SERVICE + "=*)))"; - - // all multi-config services without singleton services - private static final String CONFIGURABLE_MULTI_CONFIG_SERVICE_FILTER = "(" - + ConfigurableService.SERVICE_PROPERTY_FACTORY_SERVICE + "=*)"; - private final Logger logger = LoggerFactory.getLogger(ConfigurableServiceResource.class); private final BundleContext bundleContext; @@ -284,24 +276,24 @@ public class ConfigurableServiceResource implements RESTResource { if (serviceReferences != null) { for (ServiceReference serviceReference : serviceReferences) { String id = getServiceId(serviceReference); - String label = (String) serviceReference.getProperty(ConfigurableService.SERVICE_PROPERTY_LABEL); - if (label == null) { // for multi context services the label can be changed and must be read from config - // admin. + ConfigurableService configurableService = ConfigurableServiceUtil + .asConfigurableService((key) -> serviceReference.getProperty(key)); + + String label = configurableService.label(); + if (label.isEmpty()) { // for multi context services the label can be changed and must be read from + // config admin. label = configurationService.getProperty(id, OpenHAB.SERVICE_CONTEXT); } - String category = (String) serviceReference.getProperty(ConfigurableService.SERVICE_PROPERTY_CATEGORY); - String configDescriptionURI = (String) serviceReference - .getProperty(ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI); - if (configDescriptionURI == null) { + String category = configurableService.category(); + + String configDescriptionURI = configurableService.description_uri(); + if (configDescriptionURI.isEmpty()) { String factoryPid = (String) serviceReference.getProperty(ConfigurationAdmin.SERVICE_FACTORYPID); configDescriptionURI = getConfigDescriptionByFactoryPid(factoryPid); } - Object factoryProperty = serviceReference - .getProperty(ConfigurableService.SERVICE_PROPERTY_FACTORY_SERVICE); - boolean multiple = factoryProperty instanceof Boolean ? (Boolean) factoryProperty - : Boolean.parseBoolean((String) factoryProperty); + boolean multiple = configurableService.factory(); services.add(new ConfigurableServiceDTO(id, label, category, configDescriptionURI, multiple)); } @@ -318,8 +310,9 @@ public class ConfigurableServiceResource implements RESTResource { ServiceReference[] refs = bundleContext.getServiceReferences((String) null, filter); if (refs != null && refs.length > 0) { - configDescriptionURI = (String) refs[0] - .getProperty(ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI); + ConfigurableService configurableService = ConfigurableServiceUtil + .asConfigurableService((key) -> refs[0].getProperty(key)); + configDescriptionURI = configurableService.description_uri(); } } catch (InvalidSyntaxException e) { logger.error("Cannot get service references because the syntax of the filter '{}' is invalid.", filter); @@ -330,8 +323,8 @@ public class ConfigurableServiceResource implements RESTResource { private List getConfigurableServices() { List services = new ArrayList<>(); - services.addAll(getServicesByFilter(CONFIGURABLE_SERVICE_FILTER)); - services.addAll(getServicesByFilter(CONFIGURABLE_MULTI_CONFIG_SERVICE_FILTER)); + services.addAll(getServicesByFilter(ConfigurableServiceUtil.CONFIGURABLE_SERVICE_FILTER)); + services.addAll(getServicesByFilter(ConfigurableServiceUtil.CONFIGURABLE_MULTI_CONFIG_SERVICE_FILTER)); return services; } diff --git a/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/MagicServiceImpl.java b/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/MagicServiceImpl.java index 8c904592d..331160cab 100644 --- a/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/MagicServiceImpl.java +++ b/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/MagicServiceImpl.java @@ -38,11 +38,9 @@ import org.slf4j.LoggerFactory; * @author Henning Treu - Initial contribution */ @NonNullByDefault -@Component(configurationPid = "org.openhab.magic", service = ConfigOptionProvider.class, immediate = true, property = { - Constants.SERVICE_PID + "=org.openhab.core.magic", - ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=test:magic", - ConfigurableService.SERVICE_PROPERTY_LABEL + "=Magic", - ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=test" }) +@Component(configurationPid = "org.openhab.magic", service = ConfigOptionProvider.class, immediate = true, // + property = Constants.SERVICE_PID + "=org.openhab.core.magic") +@ConfigurableService(category = "test", label = "Magic", description_uri = "test:magic") public class MagicServiceImpl implements MagicService { private final Logger logger = LoggerFactory.getLogger(MagicServiceImpl.class); diff --git a/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/automation/modules/MagicMultiActionMarker.java b/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/automation/modules/MagicMultiActionMarker.java index 1ac0d1faa..bbbf2b9fb 100644 --- a/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/automation/modules/MagicMultiActionMarker.java +++ b/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/automation/modules/MagicMultiActionMarker.java @@ -21,12 +21,9 @@ import org.osgi.service.component.annotations.Component; * * @author Stefan Triller - Initial contribution */ -@Component(immediate = true, service = MagicMultiActionMarker.class, property = { - Constants.SERVICE_PID + "=org.openhab.MagicMultiAction", - ConfigurableService.SERVICE_PROPERTY_FACTORY_SERVICE + "=true", - ConfigurableService.SERVICE_PROPERTY_LABEL + "=MagicMultiActionsService", - ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=RuleActions", - ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=automationAction:magicMultiAction" }) +@Component(immediate = true, service = MagicMultiActionMarker.class, // + property = Constants.SERVICE_PID + "=org.openhab.MagicMultiAction") +@ConfigurableService(category = "RuleActions", label = "MagicMultiActionsService", description_uri = "automationAction:magicMultiAction", factory = true) public class MagicMultiActionMarker { } diff --git a/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/automation/modules/MagicSingleActionService.java b/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/automation/modules/MagicSingleActionService.java index a50a9796b..d6df70ef7 100644 --- a/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/automation/modules/MagicSingleActionService.java +++ b/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/binding/internal/automation/modules/MagicSingleActionService.java @@ -33,11 +33,9 @@ import org.slf4j.LoggerFactory; * * @author Stefan Triller - Initial contribution */ -@Component(configurationPid = "org.openhab.magicsingleaction", property = { - Constants.SERVICE_PID + "=org.openhab.automation.action.magicSingleActionService", - ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=automationAction:magicSingleAction", - ConfigurableService.SERVICE_PROPERTY_LABEL + "=Magic Single Action Service", - ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=RuleActions" }) +@Component(configurationPid = "org.openhab.magicsingleaction", // + property = Constants.SERVICE_PID + "=org.openhab.automation.action.magicSingleActionService") +@ConfigurableService(category = "RuleActions", label = "Magic Single Action Service", description_uri = "automationAction:magicSingleAction") @ActionScope(name = "binding.magicService") public class MagicSingleActionService implements AnnotatedActions { diff --git a/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/service/MagicMultiInstanceServiceMarker.java b/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/service/MagicMultiInstanceServiceMarker.java index 0b53cfd47..aa03e3b83 100644 --- a/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/service/MagicMultiInstanceServiceMarker.java +++ b/bundles/org.openhab.core.test.magic/src/main/java/org/openhab/core/magic/service/MagicMultiInstanceServiceMarker.java @@ -21,12 +21,9 @@ import org.osgi.service.component.annotations.Component; * @author Stefan Triller - Initial contribution */ -@Component(immediate = true, service = MagicMultiInstanceServiceMarker.class, property = { - Constants.SERVICE_PID + "=org.openhab.magicMultiInstance", - ConfigurableService.SERVICE_PROPERTY_FACTORY_SERVICE + "=true", - ConfigurableService.SERVICE_PROPERTY_LABEL + "=MagicMultiInstanceService", - ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=test", - ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=test:multipleMagic" }) +@Component(immediate = true, service = MagicMultiInstanceServiceMarker.class, // + property = Constants.SERVICE_PID + "=org.openhab.magicMultiInstance") +@ConfigurableService(category = "test", label = "MagicMultiInstanceService", description_uri = "test:multipleMagic", factory = true) public class MagicMultiInstanceServiceMarker { // this is a marker service and represents a service factory so multiple configuration instances of type // "org.openhab.core.magicMultiInstance" can be created.