Expose three classes used as bindings in JSR-223 rules as interfaces (#2723)

Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
J-N-K 2022-02-13 09:57:22 +01:00 committed by GitHub
parent c0b033b95c
commit 9a9217eab8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 320 additions and 141 deletions

View File

@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2022 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.module.script;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.internal.ScriptExtensionManager;
/**
*
* @author Simon Merschjohann - Initial contribution
*/
@NonNullByDefault
public interface ScriptExtensionManagerWrapper {
List<String> getTypes();
List<String> getPresets();
@Nullable
Object get(String type);
String getScriptIdentifier();
List<String> getDefaultPresets();
/**
* Imports a collection of named host objects/classes into a script engine instance. Sets of objects are provided
* under their object name, and categorized by preset name. This method will import all named objects for a specific
* preset name.
*
* @param preset the name of the preset to import
* @return a map of host object names to objects
* @see ScriptExtensionManager
*/
Map<String, Object> importPreset(String preset);
}

View File

@ -0,0 +1,129 @@
/**
* Copyright (c) 2010-2022 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.module.script.defaultscope;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.items.Item;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
/**
* The static methods of this class are made available as functions in the scripts.
* This gives direct write access to the event bus from within scripts.
* Items should not be updated directly (setting the state property), but updates should
* be sent to the bus, so that all interested bundles are notified.
*
* Note: This class is a copy from the {@link org.openhab.core.model.script.actions.BusEvent} class
*
* @author Kai Kreuzer - Initial contribution
* @author Jan N. Klug - Refactored to interface
*/
@NonNullByDefault
public interface ScriptBusEvent {
/**
* Sends a command for a specified item to the event bus.
*
* @param item the item to send the command to
* @param commandString the command to send
*/
@Nullable
Object sendCommand(@Nullable Item item, @Nullable String commandString);
/**
* Sends a number as a command for a specified item to the event bus.
*
* @param item the item to send the command to
* @param number the number to send as a command
*/
@Nullable
Object sendCommand(@Nullable Item item, @Nullable Number number);
/**
* Sends a command for a specified item to the event bus.
*
* @param itemName the name of the item to send the command to
* @param commandString the command to send
*/
@Nullable
Object sendCommand(@Nullable String itemName, @Nullable String commandString);
/**
* Sends a command for a specified item to the event bus.
*
* @param item the item to send the command to
* @param command the command to send
*/
@Nullable
Object sendCommand(@Nullable Item item, @Nullable Command command);
/**
* Posts a status update for a specified item to the event bus.
*
* @param item the item to send the status update for
* @param state the new state of the item as a number
*/
@Nullable
Object postUpdate(@Nullable Item item, @Nullable Number state);
/**
* Posts a status update for a specified item to the event bus.
*
* @param item the item to send the status update for
* @param stateAsString the new state of the item
*/
@Nullable
Object postUpdate(@Nullable Item item, @Nullable String stateAsString);
/**
* Posts a status update for a specified item to the event bus.
*
* @param itemName the name of the item to send the status update for
* @param stateAsString the new state of the item
*/
@Nullable
Object postUpdate(@Nullable String itemName, @Nullable String stateAsString);
/**
* Posts a status update for a specified item to the event bus.
* t
*
* @param item the item to send the status update for
* @param state the new state of the item
*/
@Nullable
Object postUpdate(@Nullable Item item, @Nullable State state);
/**
* Stores the current states for a list of items in a map.
* A group item is not itself put into the map, but instead all its members.
*
* @param items the items for which the state should be stored
* @return the map of items with their states
*/
Map<Item, State> storeStates(Item @Nullable... items);
/**
* Restores item states from a map.
* If the saved state can be interpreted as a command, a command is sent for the item
* (and the physical device can send a status update if occurred). If it is no valid
* command, the item state is directly updated to the saved value.
*
* @param statesMap a map with ({@link Item}, {@link State}) entries
* @return null
*/
@Nullable
Object restoreStates(@Nullable Map<Item, State> statesMap);
}

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2022 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.module.script.defaultscope;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.binding.ThingActions;
/**
* The methods of this class are made available as functions in the scripts.
*
* Note: This class is a copy from the {@link org.openhab.core.model.script.internal.engine.action.ThingActionService}
* class
*
* @author Kai Kreuzer - Initial contribution
* @author Jan N. Klug - Refactored to interface
*/
@NonNullByDefault
public interface ScriptThingActions {
/**
* Gets an actions instance of a certain scope for a given thing UID
*
* @param scope the action scope
* @param thingUid the UID of the thing
* @return actions the actions instance or null, if not available
*/
@Nullable
ThingActions get(@Nullable String scope, @Nullable String thingUid);
}

View File

@ -33,6 +33,7 @@ import org.openhab.core.automation.module.script.ScriptDependencyListener;
import org.openhab.core.automation.module.script.ScriptEngineContainer;
import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptEngineManager;
import org.openhab.core.automation.module.script.ScriptExtensionManagerWrapper;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
@ -136,8 +137,8 @@ public class ScriptEngineManagerImpl implements ScriptEngineManager {
if (engine != null) {
Map<String, Object> scriptExManager = new HashMap<>();
result = new ScriptEngineContainer(engine, engineFactory, engineIdentifier);
ScriptExtensionManagerWrapper wrapper = new ScriptExtensionManagerWrapper(scriptExtensionManager,
result);
ScriptExtensionManagerWrapper wrapper = new ScriptExtensionManagerWrapperImpl(
scriptExtensionManager, result);
scriptExManager.put("scriptExtension", wrapper);
scriptExManager.put("se", wrapper);
engineFactory.scopeValues(engine, scriptExManager);

View File

@ -18,7 +18,7 @@ import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.ScriptEngineContainer;
import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptExtensionManagerWrapper;
import org.openhab.core.automation.module.script.ScriptExtensionProvider;
/**
@ -26,12 +26,12 @@ import org.openhab.core.automation.module.script.ScriptExtensionProvider;
* @author Simon Merschjohann - Initial contribution
*/
@NonNullByDefault
public class ScriptExtensionManagerWrapper {
public class ScriptExtensionManagerWrapperImpl implements ScriptExtensionManagerWrapper {
private final ScriptEngineContainer container;
private final ScriptExtensionManager manager;
public ScriptExtensionManagerWrapper(ScriptExtensionManager manager, ScriptEngineContainer container) {
public ScriptExtensionManagerWrapperImpl(ScriptExtensionManager manager, ScriptEngineContainer container) {
this.manager = manager;
this.container = container;
}
@ -44,38 +44,32 @@ public class ScriptExtensionManagerWrapper {
manager.removeExtension(provider);
}
@Override
public List<String> getTypes() {
return manager.getTypes();
}
@Override
public String getScriptIdentifier() {
return container.getIdentifier();
}
@Override
public List<String> getPresets() {
return manager.getPresets();
}
@Override
public @Nullable Object get(String type) {
return manager.get(type, container.getIdentifier());
}
@Override
public List<String> getDefaultPresets() {
return manager.getDefaultPresets();
}
/**
* Imports a collection of named host objects/classes into a script engine instance. Sets of objects are provided
* under their object name, and categorized by preset name. This method will import all named objects for a specific
* preset name.
*
* @implNote This call both returns the imported objects, and requests that the {@link ScriptEngineFactory} import
* them. The mechanism of how they are imported by the ScriptEngineFactory, or whether they are imported
* at all (aside from eing returned by this call) is dependent of the implementation of the
* ScriptEngineFactory.
*
* @apiNote Objects may appear in multiple named presets.
* @see ScriptExtensionManager
*
* @param preset the name of the preset to import
* @return a map of host object names to objects
*/
@Override
public Map<String, Object> importPreset(String preset) {
return manager.importPreset(preset, container.getFactory(), container.getScriptEngine(),
container.getIdentifier());

View File

@ -83,15 +83,15 @@ public class DefaultScriptScopeProvider implements ScriptExtensionProvider {
private final Map<String, Object> elements = new ConcurrentHashMap<>();
private final ScriptBusEvent busEvent;
private final ScriptThingActions thingActions;
private final ScriptBusEventImpl busEvent;
private final ScriptThingActionsImpl thingActions;
@Activate
public DefaultScriptScopeProvider(final @Reference ItemRegistry itemRegistry,
final @Reference ThingRegistry thingRegistry, final @Reference RuleRegistry ruleRegistry,
final @Reference EventPublisher eventPublisher) {
this.busEvent = new ScriptBusEvent(itemRegistry, eventPublisher);
this.thingActions = new ScriptThingActions(thingRegistry);
this.busEvent = new ScriptBusEventImpl(itemRegistry, eventPublisher);
this.thingActions = new ScriptThingActionsImpl(thingRegistry);
elements.put("State", State.class);
elements.put("Command", Command.class);

View File

@ -16,6 +16,9 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.defaultscope.ScriptBusEvent;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.GroupItem;
import org.openhab.core.items.Item;
@ -33,32 +36,29 @@ import org.slf4j.LoggerFactory;
* Items should not be updated directly (setting the state property), but updates should
* be sent to the bus, so that all interested bundles are notified.
*
* Note: This class is a copy from the {@link BusEvent} class, which resides in the model.script bundle.
* Note: This class is a copy from the {@link org.openhab.core.model.script.actions.BusEvent} class
*
* @author Kai Kreuzer - Initial contribution
* @author Jan N. Klug - Moved implementation to internal class
*/
public class ScriptBusEvent {
@NonNullByDefault
public class ScriptBusEventImpl implements ScriptBusEvent {
ScriptBusEvent(ItemRegistry itemRegistry, EventPublisher eventPublisher) {
private @Nullable ItemRegistry itemRegistry;
private @Nullable EventPublisher eventPublisher;
ScriptBusEventImpl(ItemRegistry itemRegistry, EventPublisher eventPublisher) {
this.itemRegistry = itemRegistry;
this.eventPublisher = eventPublisher;
}
private ItemRegistry itemRegistry;
private EventPublisher eventPublisher;
public void dispose() {
this.itemRegistry = null;
this.eventPublisher = null;
}
/**
* Sends a command for a specified item to the event bus.
*
* @param item the item to send the command to
* @param commandString the command to send
*/
public Object sendCommand(Item item, String commandString) {
@Override
public @Nullable Object sendCommand(@Nullable Item item, @Nullable String commandString) {
if (item != null) {
return sendCommand(item.getName(), commandString);
} else {
@ -66,13 +66,8 @@ public class ScriptBusEvent {
}
}
/**
* Sends a number as a command for a specified item to the event bus.
*
* @param item the item to send the command to
* @param number the number to send as a command
*/
public Object sendCommand(Item item, Number number) {
@Override
public @Nullable Object sendCommand(@Nullable Item item, @Nullable Number number) {
if (item != null && number != null) {
return sendCommand(item.getName(), number.toString());
} else {
@ -80,50 +75,38 @@ public class ScriptBusEvent {
}
}
/**
* Sends a command for a specified item to the event bus.
*
* @param itemName the name of the item to send the command to
* @param commandString the command to send
*/
public Object sendCommand(String itemName, String commandString) {
if (eventPublisher != null && itemRegistry != null) {
@Override
public @Nullable Object sendCommand(@Nullable String itemName, @Nullable String commandString) {
EventPublisher eventPublisher = this.eventPublisher;
ItemRegistry itemRegistry = this.itemRegistry;
if (eventPublisher != null && itemRegistry != null && itemName != null && commandString != null) {
try {
Item item = itemRegistry.getItem(itemName);
Command command = TypeParser.parseCommand(item.getAcceptedCommandTypes(), commandString);
if (command != null) {
eventPublisher.post(ItemEventFactory.createCommandEvent(itemName, command));
} else {
LoggerFactory.getLogger(ScriptBusEvent.class).warn("Command '{}' cannot be parsed for item '{}'.",
commandString, item);
LoggerFactory.getLogger(ScriptBusEventImpl.class)
.warn("Command '{}' cannot be parsed for item '{}'.", commandString, item);
}
} catch (ItemNotFoundException e) {
LoggerFactory.getLogger(ScriptBusEvent.class).warn("Item '{}' does not exist.", itemName);
LoggerFactory.getLogger(ScriptBusEventImpl.class).warn("Item '{}' does not exist.", itemName);
}
}
return null;
}
/**
* Sends a command for a specified item to the event bus.
*
* @param item the item to send the command to
* @param command the command to send
*/
public Object sendCommand(Item item, Command command) {
if (eventPublisher != null && item != null) {
@Override
public @Nullable Object sendCommand(@Nullable Item item, @Nullable Command command) {
EventPublisher eventPublisher = this.eventPublisher;
if (eventPublisher != null && item != null && command != null) {
eventPublisher.post(ItemEventFactory.createCommandEvent(item.getName(), command));
}
return null;
}
/**
* Posts a status update for a specified item to the event bus.
*
* @param item the item to send the status update for
* @param state the new state of the item as a number
*/
public Object postUpdate(Item item, Number state) {
@Override
public @Nullable Object postUpdate(@Nullable Item item, @Nullable Number state) {
if (item != null && state != null) {
return postUpdate(item.getName(), state.toString());
} else {
@ -131,13 +114,8 @@ public class ScriptBusEvent {
}
}
/**
* Posts a status update for a specified item to the event bus.
*
* @param item the item to send the status update for
* @param stateAsString the new state of the item
*/
public Object postUpdate(Item item, String stateAsString) {
@Override
public @Nullable Object postUpdate(@Nullable Item item, @Nullable String stateAsString) {
if (item != null) {
return postUpdate(item.getName(), stateAsString);
} else {
@ -145,52 +123,38 @@ public class ScriptBusEvent {
}
}
/**
* Posts a status update for a specified item to the event bus.
*
* @param itemName the name of the item to send the status update for
* @param stateAsString the new state of the item
*/
public Object postUpdate(String itemName, String stateString) {
if (eventPublisher != null && itemRegistry != null) {
@Override
public @Nullable Object postUpdate(@Nullable String itemName, @Nullable String stateString) {
EventPublisher eventPublisher = this.eventPublisher;
ItemRegistry itemRegistry = this.itemRegistry;
if (eventPublisher != null && itemRegistry != null && itemName != null && stateString != null) {
try {
Item item = itemRegistry.getItem(itemName);
State state = TypeParser.parseState(item.getAcceptedDataTypes(), stateString);
if (state != null) {
eventPublisher.post(ItemEventFactory.createStateEvent(itemName, state));
} else {
LoggerFactory.getLogger(ScriptBusEvent.class).warn("State '{}' cannot be parsed for item '{}'.",
LoggerFactory.getLogger(ScriptBusEventImpl.class).warn("State '{}' cannot be parsed for item '{}'.",
stateString, itemName);
}
} catch (ItemNotFoundException e) {
LoggerFactory.getLogger(ScriptBusEvent.class).warn("Item '{}' does not exist.", itemName);
LoggerFactory.getLogger(ScriptBusEventImpl.class).warn("Item '{}' does not exist.", itemName);
}
}
return null;
}
/**
* Posts a status update for a specified item to the event bus.
* t
*
* @param item the item to send the status update for
* @param state the new state of the item
*/
public Object postUpdate(Item item, State state) {
if (eventPublisher != null && item != null) {
@Override
public @Nullable Object postUpdate(@Nullable Item item, @Nullable State state) {
EventPublisher eventPublisher = this.eventPublisher;
if (eventPublisher != null && item != null && state != null) {
eventPublisher.post(ItemEventFactory.createStateEvent(item.getName(), state));
}
return null;
}
/**
* Stores the current states for a list of items in a map.
* A group item is not itself put into the map, but instead all its members.
*
* @param items the items for which the state should be stored
* @return the map of items with their states
*/
public Map<Item, State> storeStates(Item... items) {
@Override
public Map<Item, State> storeStates(Item @Nullable... items) {
Map<Item, State> statesMap = new HashMap<>();
if (items != null) {
for (Item item : items) {
@ -207,16 +171,8 @@ public class ScriptBusEvent {
return statesMap;
}
/**
* Restores item states from a map.
* If the saved state can be interpreted as a command, a command is sent for the item
* (and the physical device can send a status update if occurred). If it is no valid
* command, the item state is directly updated to the saved value.
*
* @param statesMap a map with ({@link Item}, {@link State}) entries
* @return null
*/
public Object restoreStates(Map<Item, State> statesMap) {
@Override
public @Nullable Object restoreStates(@Nullable Map<Item, State> statesMap) {
if (statesMap != null) {
for (Entry<Item, State> entry : statesMap.entrySet()) {
if (entry.getValue() instanceof Command) {

View File

@ -15,6 +15,9 @@ package org.openhab.core.automation.module.script.internal.defaultscope;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.defaultscope.ScriptThingActions;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingUID;
@ -25,16 +28,19 @@ import org.openhab.core.thing.binding.ThingHandler;
/**
* The methods of this class are made available as functions in the scripts.
*
* Note: This class is a copy from the {@link ThingActions} class, which resides in the model.script bundle.
* Note: This class is a copy from the {@link org.openhab.core.model.script.internal.engine.action.ThingActionService}
* class
*
* @author Kai Kreuzer - Initial contribution
* @author Jan N. Klug - Moved implementation to internal class
*/
public class ScriptThingActions {
@NonNullByDefault
public class ScriptThingActionsImpl implements ScriptThingActions {
private static final Map<String, ThingActions> THING_ACTIONS_MAP = new HashMap<>();
private ThingRegistry thingRegistry;
private @Nullable ThingRegistry thingRegistry;
ScriptThingActions(ThingRegistry thingRegistry) {
ScriptThingActionsImpl(ThingRegistry thingRegistry) {
this.thingRegistry = thingRegistry;
}
@ -42,22 +48,17 @@ public class ScriptThingActions {
this.thingRegistry = null;
}
/**
* Gets an actions instance of a certain scope for a given thing UID
*
* @param scope the action scope
* @param thingUid the UID of the thing
*
* @return actions the actions instance or null, if not available
*/
public ThingActions get(String scope, String thingUid) {
ThingUID uid = new ThingUID(thingUid);
Thing thing = thingRegistry.get(uid);
if (thing != null) {
ThingHandler handler = thing.getHandler();
if (handler != null) {
ThingActions thingActions = THING_ACTIONS_MAP.get(getKey(scope, thingUid));
return thingActions;
@Override
public @Nullable ThingActions get(@Nullable String scope, @Nullable String thingUid) {
ThingRegistry thingRegistry = this.thingRegistry;
if (thingUid != null && scope != null && thingRegistry != null) {
ThingUID uid = new ThingUID(thingUid);
Thing thing = thingRegistry.get(uid);
if (thing != null) {
ThingHandler handler = thing.getHandler();
if (handler != null) {
return THING_ACTIONS_MAP.get(getKey(scope, thingUid));
}
}
}
return null;
@ -65,7 +66,9 @@ public class ScriptThingActions {
void addThingActions(ThingActions thingActions) {
String key = getKey(thingActions);
THING_ACTIONS_MAP.put(key, thingActions);
if (key != null) {
THING_ACTIONS_MAP.put(key, thingActions);
}
}
void removeThingActions(ThingActions thingActions) {
@ -73,18 +76,26 @@ public class ScriptThingActions {
THING_ACTIONS_MAP.remove(key);
}
private static String getKey(ThingActions thingActions) {
private static @Nullable String getKey(ThingActions thingActions) {
String scope = getScope(thingActions);
String thingUID = getThingUID(thingActions);
return getKey(scope, thingUID);
if (thingUID == null) {
return null;
} else {
return getKey(scope, thingUID);
}
}
private static String getKey(String scope, String thingUID) {
return scope + "-" + thingUID;
}
private static String getThingUID(ThingActions actions) {
return actions.getThingHandler().getThing().getUID().getAsString();
private static @Nullable String getThingUID(ThingActions actions) {
ThingHandler thingHandler = actions.getThingHandler();
if (thingHandler == null) {
return null;
}
return thingHandler.getThing().getUID().getAsString();
}
private static String getScope(ThingActions actions) {