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 extends Annotation> 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.