Fixed removal / replacement of annotated ThingActions (#1536)

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
Christoph Weitkamp 2020-07-21 19:44:55 +02:00 committed by GitHub
parent 4585dac84a
commit c747f7f9bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 368 additions and 105 deletions

View File

@ -169,8 +169,8 @@ public class AnnotatedActionModuleTypeProvider extends BaseModuleHandlerFactory
for (ModuleInformation mi : moduleInformations) {
mi.setConfigName(configName);
ModuleType oldType = null;
ModuleType oldType = null;
Set<ModuleInformation> availableModuleConfigs = moduleInformation.get(mi.getUID());
if (availableModuleConfigs != null) {
if (availableModuleConfigs.size() > 1) {
@ -181,11 +181,14 @@ public class AnnotatedActionModuleTypeProvider extends BaseModuleHandlerFactory
}
ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation);
for (ProviderChangeListener<ModuleType> l : changeListeners) {
if (oldType != null) {
l.updated(this, oldType, mt);
} else {
l.removed(this, mt);
// localize moduletype -> remove from map
if (mt != null) {
for (ProviderChangeListener<ModuleType> l : changeListeners) {
if (oldType != null) {
l.updated(this, oldType, mt);
} else {
l.removed(this, mt);
}
}
}
}
@ -201,8 +204,6 @@ public class AnnotatedActionModuleTypeProvider extends BaseModuleHandlerFactory
return configName;
}
// HandlerFactory:
@Override
public Collection<String> getTypes() {
return moduleInformation.keySet();
@ -212,11 +213,9 @@ public class AnnotatedActionModuleTypeProvider extends BaseModuleHandlerFactory
protected @Nullable ModuleHandler internalCreate(Module module, String ruleUID) {
if (module instanceof Action) {
Action actionModule = (Action) module;
if (moduleInformation.containsKey(actionModule.getTypeUID())) {
ModuleInformation finalMI = helper.getModuleInformationForIdentifier(actionModule, moduleInformation,
false);
if (finalMI != null) {
ActionType moduleType = helper.buildModuleType(module.getTypeUID(), moduleInformation);
return new AnnotationActionHandler(actionModule, moduleType, finalMI.getMethod(),

View File

@ -26,7 +26,10 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.Action;
import org.openhab.core.automation.AnnotatedActions;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.ActionOutput;
import org.openhab.core.automation.annotation.ActionOutputs;
@ -34,6 +37,7 @@ import org.openhab.core.automation.annotation.ActionScope;
import org.openhab.core.automation.annotation.RuleAction;
import org.openhab.core.automation.type.ActionType;
import org.openhab.core.automation.type.Input;
import org.openhab.core.automation.type.ModuleTypeProvider;
import org.openhab.core.automation.type.Output;
import org.openhab.core.config.core.ConfigDescriptionParameter;
import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
@ -44,10 +48,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper methods for annotated ActionModuleType provider
* Helper methods for {@link AnnotatedActions} {@link ModuleTypeProvider}
*
* @author Stefan Triller - Initial contribution
*/
@NonNullByDefault
public class AnnotationActionModuleTypeHelper {
private final Logger logger = LoggerFactory.getLogger(AnnotationActionModuleTypeHelper.class);
@ -56,20 +61,18 @@ public class AnnotationActionModuleTypeHelper {
private static final String SELECT_THING_LABEL = "Select Thing";
public static final String CONFIG_PARAM = "config";
@SuppressWarnings({ "rawtypes", "unchecked" })
public Collection<ModuleInformation> parseAnnotations(Object actionProvider) {
Class clazz = actionProvider.getClass();
Class<?> clazz = actionProvider.getClass();
if (clazz.isAnnotationPresent(ActionScope.class)) {
ActionScope scope = (ActionScope) clazz.getAnnotation(ActionScope.class);
ActionScope scope = clazz.getAnnotation(ActionScope.class);
return parseAnnotations(scope.name(), actionProvider);
}
return Collections.emptyList();
}
@SuppressWarnings({ "rawtypes" })
public Collection<ModuleInformation> parseAnnotations(String name, Object actionProvider) {
Collection<ModuleInformation> moduleInformation = new ArrayList<>();
Class clazz = actionProvider.getClass();
Class<?> clazz = actionProvider.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(RuleAction.class)) {
@ -103,11 +106,10 @@ public class AnnotationActionModuleTypeHelper {
for (int i = 0; i < annotations.length; i++) {
Parameter param = params[i];
Annotation[] paramAnnotations = annotations[i];
Input input = null;
if (paramAnnotations.length == 0) {
// we do not have an annotation with a name for this parameter
input = new Input("p" + i, param.getType().getCanonicalName(), "", "", Collections.<String> emptySet(),
false, "", "");
inputs.add(new Input("p" + i, param.getType().getCanonicalName(), "", "", Collections.emptySet(), false,
"", ""));
} else if (paramAnnotations.length == 1) {
Annotation a = paramAnnotations[0];
if (a instanceof ActionInput) {
@ -121,12 +123,11 @@ public class AnnotationActionModuleTypeHelper {
type = param.getType().getCanonicalName();
}
input = new Input(inp.name(), type, inp.label(), inp.description(),
inputs.add(new Input(inp.name(), type, inp.label(), inp.description(),
Arrays.stream(inp.tags()).collect(Collectors.toSet()), inp.required(), inp.reference(),
inp.defaultValue());
inp.defaultValue()));
}
}
inputs.add(input);
}
return inputs;
}
@ -146,7 +147,7 @@ public class AnnotationActionModuleTypeHelper {
return outputs;
}
public ActionType buildModuleType(String UID, Map<String, Set<ModuleInformation>> moduleInformation) {
public @Nullable ActionType buildModuleType(String UID, Map<String, Set<ModuleInformation>> moduleInformation) {
Set<ModuleInformation> mis = moduleInformation.get(UID);
List<ConfigDescriptionParameter> configDescriptions = new ArrayList<>();
@ -177,7 +178,7 @@ public class AnnotationActionModuleTypeHelper {
return null;
}
private ConfigDescriptionParameter buildConfigParam(Set<ModuleInformation> moduleInformations,
private @Nullable ConfigDescriptionParameter buildConfigParam(Set<ModuleInformation> moduleInformations,
ActionModuleKind kind) {
List<ParameterOption> options = new ArrayList<>();
if (kind == ActionModuleKind.SINGLE) {
@ -206,8 +207,8 @@ public class AnnotationActionModuleTypeHelper {
return null;
}
public ModuleInformation getModuleInformationForIdentifier(Action module,
Map<String, Set<ModuleInformation>> moduleInformation, boolean thing) {
public @Nullable ModuleInformation getModuleInformationForIdentifier(Action module,
Map<String, Set<ModuleInformation>> moduleInformation, boolean isThing) {
Configuration c = module.getConfiguration();
String config = (String) c.get(AnnotationActionModuleTypeHelper.CONFIG_PARAM);
@ -218,7 +219,7 @@ public class AnnotationActionModuleTypeHelper {
finalMI = (ModuleInformation) mis.toArray()[0];
} else {
for (ModuleInformation mi : mis) {
if (thing) {
if (isThing) {
if (Objects.equals(mi.getThingUID(), config)) {
finalMI = mi;
break;

View File

@ -43,9 +43,12 @@ import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ModuleTypeProvider that collects actions for {@link ThingHandler}s
@ -56,6 +59,8 @@ import org.osgi.service.component.annotations.ReferencePolicy;
@Component(service = { ModuleTypeProvider.class, ModuleHandlerFactory.class })
public class AnnotatedThingActionModuleTypeProvider extends BaseModuleHandlerFactory implements ModuleTypeProvider {
private final Logger logger = LoggerFactory.getLogger(AnnotatedThingActionModuleTypeProvider.class);
private final Collection<ProviderChangeListener<ModuleType>> changeListeners = ConcurrentHashMap.newKeySet();
private final Map<String, Set<ModuleInformation>> moduleInformation = new ConcurrentHashMap<>();
private final AnnotationActionModuleTypeHelper helper = new AnnotationActionModuleTypeHelper();
@ -67,72 +72,10 @@ public class AnnotatedThingActionModuleTypeProvider extends BaseModuleHandlerFac
this.moduleTypeI18nService = moduleTypeI18nService;
}
@Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE)
public void addAnnotatedThingActions(ThingActions annotatedThingActions) {
if (annotatedThingActions.getClass().isAnnotationPresent(ThingActionsScope.class)) {
ThingActionsScope scope = annotatedThingActions.getClass().getAnnotation(ThingActionsScope.class);
Collection<ModuleInformation> moduleInformations = helper.parseAnnotations(scope.name(),
annotatedThingActions);
String thingUID = annotatedThingActions.getThingHandler().getThing().getUID().getAsString();
for (ModuleInformation mi : moduleInformations) {
mi.setThingUID(thingUID);
ModuleType oldType = null;
if (moduleInformation.containsKey(mi.getUID())) {
oldType = helper.buildModuleType(mi.getUID(), moduleInformation);
Set<ModuleInformation> availableModuleConfigs = moduleInformation.get(mi.getUID());
availableModuleConfigs.add(mi);
} else {
Set<ModuleInformation> configs = ConcurrentHashMap.newKeySet();
configs.add(mi);
moduleInformation.put(mi.getUID(), configs);
}
ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation);
if (mt != null) {
for (ProviderChangeListener<ModuleType> l : changeListeners) {
if (oldType != null) {
l.updated(this, oldType, mt);
} else {
l.added(this, mt);
}
}
}
}
}
}
public void removeAnnotatedThingActions(ThingActions annotatedThingActions, Map<String, Object> properties) {
Collection<ModuleInformation> moduleInformations = helper.parseAnnotations(annotatedThingActions);
String thingUID = annotatedThingActions.getThingHandler().getThing().getUID().getAsString();
for (ModuleInformation mi : moduleInformations) {
mi.setThingUID(thingUID);
ModuleType oldType = null;
Set<ModuleInformation> availableModuleConfigs = moduleInformation.get(mi.getUID());
if (availableModuleConfigs != null) {
if (availableModuleConfigs.size() > 1) {
oldType = helper.buildModuleType(mi.getUID(), moduleInformation);
availableModuleConfigs.remove(mi);
} else {
moduleInformation.remove(mi.getUID());
}
ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation);
// localize moduletype -> remove from map
for (ProviderChangeListener<ModuleType> l : changeListeners) {
if (oldType != null) {
l.updated(this, oldType, mt);
} else {
l.removed(this, mt);
}
}
}
}
@Override
@Deactivate
protected void deactivate() {
moduleInformation.clear();
}
@Override
@ -140,6 +83,11 @@ public class AnnotatedThingActionModuleTypeProvider extends BaseModuleHandlerFac
changeListeners.add(listener);
}
@Override
public void removeProviderChangeListener(ProviderChangeListener<ModuleType> listener) {
changeListeners.remove(listener);
}
@Override
public Collection<ModuleType> getAll() {
Collection<ModuleType> moduleTypes = new ArrayList<>();
@ -152,16 +100,6 @@ public class AnnotatedThingActionModuleTypeProvider extends BaseModuleHandlerFac
return moduleTypes;
}
@Override
public void removeProviderChangeListener(ProviderChangeListener<ModuleType> listener) {
changeListeners.remove(listener);
}
@Override
public Collection<String> getTypes() {
return moduleInformation.keySet();
}
@SuppressWarnings("unchecked")
@Override
public <T extends ModuleType> T getModuleType(String UID, @Nullable Locale locale) {
@ -195,15 +133,107 @@ public class AnnotatedThingActionModuleTypeProvider extends BaseModuleHandlerFac
return null;
}
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
public void addAnnotatedThingActions(ThingActions annotatedThingActions) {
if (annotatedThingActions.getClass().isAnnotationPresent(ThingActionsScope.class)) {
ThingActionsScope scope = annotatedThingActions.getClass().getAnnotation(ThingActionsScope.class);
Collection<ModuleInformation> moduleInformations = helper.parseAnnotations(scope.name(),
annotatedThingActions);
String thingUID = getThingUID(annotatedThingActions);
for (ModuleInformation mi : moduleInformations) {
mi.setThingUID(thingUID);
ModuleType oldType = null;
if (moduleInformation.containsKey(mi.getUID())) {
oldType = helper.buildModuleType(mi.getUID(), moduleInformation);
Set<ModuleInformation> availableModuleConfigs = moduleInformation.get(mi.getUID());
availableModuleConfigs.add(mi);
} else {
Set<ModuleInformation> configs = ConcurrentHashMap.newKeySet();
configs.add(mi);
moduleInformation.put(mi.getUID(), configs);
}
ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation);
if (mt != null) {
for (ProviderChangeListener<ModuleType> l : changeListeners) {
if (oldType != null) {
l.updated(this, oldType, mt);
} else {
l.added(this, mt);
}
}
}
}
} else {
logger.error("Missing 'ThingActionsScope' for '{}'. Please add it to your class definition.",
annotatedThingActions.getClass());
}
}
public void removeAnnotatedThingActions(ThingActions annotatedThingActions) {
if (annotatedThingActions.getClass().isAnnotationPresent(ThingActionsScope.class)) {
ThingActionsScope scope = annotatedThingActions.getClass().getAnnotation(ThingActionsScope.class);
Collection<ModuleInformation> moduleInformations = helper.parseAnnotations(scope.name(),
annotatedThingActions);
String thingUID = getThingUID(annotatedThingActions);
for (ModuleInformation mi : moduleInformations) {
mi.setThingUID(thingUID);
ModuleType oldType = null;
Set<ModuleInformation> availableModuleConfigs = moduleInformation.get(mi.getUID());
if (availableModuleConfigs != null) {
if (availableModuleConfigs.size() > 1) {
oldType = helper.buildModuleType(mi.getUID(), moduleInformation);
availableModuleConfigs.remove(mi);
} else {
moduleInformation.remove(mi.getUID());
}
ModuleType mt = helper.buildModuleType(mi.getUID(), moduleInformation);
// localize moduletype -> remove from map
if (mt != null) {
for (ProviderChangeListener<ModuleType> l : changeListeners) {
if (oldType != null) {
l.updated(this, oldType, mt);
} else {
l.removed(this, mt);
}
}
}
}
}
} else {
logger.error("Missing 'ThingActionsScope' for '{}'. Please add it to your class definition.",
annotatedThingActions.getClass());
}
}
private String getThingUID(ThingActions annotatedThingActions) {
ThingHandler handler = annotatedThingActions.getThingHandler();
if (handler == null) {
throw new RuntimeException(
String.format("ThingHandler for '%s' is missing.", annotatedThingActions.getClass()));
}
return handler.getThing().getUID().getAsString();
}
@Override
public Collection<String> getTypes() {
return moduleInformation.keySet();
}
@Override
protected @Nullable ModuleHandler internalCreate(Module module, String ruleUID) {
if (module instanceof Action) {
Action actionModule = (Action) module;
if (moduleInformation.containsKey(actionModule.getTypeUID())) {
ModuleInformation finalMI = helper.getModuleInformationForIdentifier(actionModule, moduleInformation,
true);
if (finalMI != null) {
ActionType moduleType = helper.buildModuleType(module.getTypeUID(), moduleInformation);
return new AnnotationActionHandler(actionModule, moduleType, finalMI.getMethod(),

View File

@ -0,0 +1,233 @@
/**
* 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.automation.thingsupport;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.openhab.core.automation.Visibility;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.ActionOutput;
import org.openhab.core.automation.annotation.RuleAction;
import org.openhab.core.automation.module.provider.AnnotationActionModuleTypeHelper;
import org.openhab.core.automation.module.provider.i18n.ModuleTypeI18nService;
import org.openhab.core.automation.type.ActionType;
import org.openhab.core.automation.type.Input;
import org.openhab.core.automation.type.ModuleType;
import org.openhab.core.automation.type.Output;
import org.openhab.core.config.core.ConfigDescriptionParameter;
import org.openhab.core.config.core.ParameterOption;
import org.openhab.core.test.java.JavaTest;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.ThingActions;
import org.openhab.core.thing.binding.ThingActionsScope;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* Tests for the {@link AnnotatedThingActionModuleTypeProvider}
*
* @author Christoph Weitkamp - Initial contribution
*/
public class AnnotatedThingActionModuleTypeProviderTest extends JavaTest {
private static final ThingTypeUID TEST_THING_TYPE_UID = new ThingTypeUID("binding", "thing-type");
private static final String TEST_ACTION_TYPE_ID = "test.testMethod";
private static final String ACTION_LABEL = "Test Label";
private static final String ACTION_DESCRIPTION = "My Description";
private static final String ACTION_INPUT1 = "input1";
private static final String ACTION_INPUT1_DESCRIPTION = "input1 description";
private static final String ACTION_INPUT1_LABEL = "input1 label";
private static final String ACTION_INPUT1_DEFAULT_VALUE = "input1 default";
private static final String ACTION_INPUT1_REFERENCE = "input1 reference";
private static final String ACTION_INPUT2 = "input2";
private static final String ACTION_OUTPUT1 = "output1";
private static final String ACTION_OUTPUT1_DESCRIPTION = "output1 description";
private static final String ACTION_OUTPUT1_LABEL = "output1 label";
private static final String ACTION_OUTPUT1_DEFAULT_VALUE = "output1 default";
private static final String ACTION_OUTPUT1_REFERENCE = "output1 reference";
private static final String ACTION_OUTPUT1_TYPE = "java.lang.Integer";
private static final String ACTION_OUTPUT2 = "output2";
private static final String ACTION_OUTPUT2_TYPE = "java.lang.String";
private ModuleTypeI18nService moduleTypeI18nService;
private ThingHandler mockHandler1;
private ThingHandler mockHandler2;
private ThingActions actionProviderConf1;
private ThingActions actionProviderConf2;
@Before
public void setUp() {
mockHandler1 = mock(ThingHandler.class);
when(mockHandler1.getThing()).thenReturn(ThingBuilder.create(TEST_THING_TYPE_UID, "test1").build());
actionProviderConf1 = new TestThingActionProvider();
actionProviderConf1.setThingHandler(mockHandler1);
mockHandler2 = mock(ThingHandler.class);
when(mockHandler2.getThing()).thenReturn(ThingBuilder.create(TEST_THING_TYPE_UID, "test2").build());
actionProviderConf2 = new TestThingActionProvider();
actionProviderConf2.setThingHandler(mockHandler2);
moduleTypeI18nService = mock(ModuleTypeI18nService.class);
when(moduleTypeI18nService.getModuleTypePerLocale(any(ModuleType.class), any(), any()))
.thenAnswer(i -> i.getArguments()[0]);
}
@Test
public void testMultiServiceAnnotationActions() {
AnnotatedThingActionModuleTypeProvider prov = new AnnotatedThingActionModuleTypeProvider(moduleTypeI18nService);
prov.addAnnotatedThingActions(actionProviderConf1);
Collection<String> types = prov.getTypes();
assertEquals(1, types.size());
assertTrue(types.contains(TEST_ACTION_TYPE_ID));
prov.addAnnotatedThingActions(actionProviderConf2);
// we only have ONE type but TWO configurations for it
types = prov.getTypes();
assertEquals(1, types.size());
assertTrue(types.contains(TEST_ACTION_TYPE_ID));
ModuleType mt = prov.getModuleType(TEST_ACTION_TYPE_ID, null);
assertTrue(mt instanceof ActionType);
ActionType at = (ActionType) mt;
assertEquals(ACTION_LABEL, at.getLabel());
assertEquals(ACTION_DESCRIPTION, at.getDescription());
assertEquals(Visibility.HIDDEN, at.getVisibility());
assertEquals(TEST_ACTION_TYPE_ID, at.getUID());
Set<String> tags = at.getTags();
assertTrue(tags.contains("tag1"));
assertTrue(tags.contains("tag2"));
List<Input> inputs = at.getInputs();
assertEquals(2, inputs.size());
for (Input in : inputs) {
if (ACTION_INPUT1.equals(in.getName())) {
assertEquals(ACTION_INPUT1_LABEL, in.getLabel());
assertEquals(ACTION_INPUT1_DEFAULT_VALUE, in.getDefaultValue());
assertEquals(ACTION_INPUT1_DESCRIPTION, in.getDescription());
assertEquals(ACTION_INPUT1_REFERENCE, in.getReference());
assertEquals(true, in.isRequired());
assertEquals("Item", in.getType());
Set<String> inputTags = in.getTags();
assertTrue(inputTags.contains("tagIn11"));
assertTrue(inputTags.contains("tagIn12"));
} else if (ACTION_INPUT2.equals(in.getName())) {
// if the annotation does not specify a type, we use the java type
assertEquals("java.lang.String", in.getType());
}
}
List<Output> outputs = at.getOutputs();
assertEquals(2, outputs.size());
for (Output o : outputs) {
if (ACTION_OUTPUT1.equals(o.getName())) {
assertEquals(ACTION_OUTPUT1_LABEL, o.getLabel());
assertEquals(ACTION_OUTPUT1_DEFAULT_VALUE, o.getDefaultValue());
assertEquals(ACTION_OUTPUT1_DESCRIPTION, o.getDescription());
assertEquals(ACTION_OUTPUT1_REFERENCE, o.getReference());
assertEquals(ACTION_OUTPUT1_TYPE, o.getType());
Set<String> outputTags = o.getTags();
assertTrue(outputTags.contains("tagOut11"));
assertTrue(outputTags.contains("tagOut12"));
} else if (ACTION_INPUT2.equals(o.getName())) {
assertEquals(ACTION_OUTPUT2_TYPE, o.getType());
}
}
// remove the first configuration
prov.removeAnnotatedThingActions(actionProviderConf1);
types = prov.getTypes();
assertEquals(1, types.size());
// check of the second configuration is still valid
mt = prov.getModuleType(TEST_ACTION_TYPE_ID, null);
List<ConfigDescriptionParameter> configParams = mt.getConfigurationDescriptions();
boolean found = false;
for (ConfigDescriptionParameter cdp : configParams) {
if (AnnotationActionModuleTypeHelper.CONFIG_PARAM.equals(cdp.getName())) {
found = true;
List<ParameterOption> parameterOptions = cdp.getOptions();
assertEquals(1, parameterOptions.size());
ParameterOption po = parameterOptions.get(0);
assertEquals("binding:thing-type:test2", po.getValue());
}
}
assertTrue(found);
// remove the second configuration and there should be none left
prov.removeAnnotatedThingActions(actionProviderConf2);
types = prov.getTypes();
assertEquals(0, types.size());
mt = prov.getModuleType(TEST_ACTION_TYPE_ID, null);
assertNull(mt);
}
@ThingActionsScope(name = "test")
private class TestThingActionProvider implements ThingActions {
private @Nullable ThingHandler handler;
@RuleAction(label = ACTION_LABEL, description = ACTION_DESCRIPTION, visibility = Visibility.HIDDEN, tags = {
"tag1", "tag2" })
public @ActionOutput(name = ACTION_OUTPUT1, type = ACTION_OUTPUT1_TYPE, description = ACTION_OUTPUT1_DESCRIPTION, label = ACTION_OUTPUT1_LABEL, defaultValue = ACTION_OUTPUT1_DEFAULT_VALUE, reference = ACTION_OUTPUT1_REFERENCE, tags = {
"tagOut11",
"tagOut12" }) @ActionOutput(name = ACTION_OUTPUT2, type = ACTION_OUTPUT2_TYPE) Map<String, Object> testMethod(
@ActionInput(name = ACTION_INPUT1, label = ACTION_INPUT1_LABEL, defaultValue = ACTION_INPUT1_DEFAULT_VALUE, description = ACTION_INPUT1_DESCRIPTION, reference = ACTION_INPUT1_REFERENCE, required = true, type = "Item", tags = {
"tagIn11", "tagIn12" }) String input1,
@ActionInput(name = ACTION_INPUT2) String input2) {
Map<String, Object> result = new HashMap<>();
result.put("output1", 23);
result.put("output2", "hello world");
return result;
}
@Override
public void setThingHandler(ThingHandler handler) {
this.handler = handler;
}
@Override
public @Nullable ThingHandler getThingHandler() {
return handler;
}
}
}