Skip to content

Commit 89692b1

Browse files
authored
[items] ItemPersistence: Convert state to primitive type in persist method (#339)
This fixes an issue, where the asynchronous way persistence services store the passed in state causes a IllegalStateException by GraalJS, because multithreaded access to the script's context is not allowed. See openhab/openhab-core#4268 (comment). --------- Signed-off-by: Florian Hotze <[email protected]>
1 parent d81a61f commit 89692b1

File tree

5 files changed

+33
-18
lines changed

5 files changed

+33
-18
lines changed

src/helpers.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,19 @@ function _getItemName (itemOrName) {
2222
}
2323

2424
/**
25-
* Helper function to convert JS types to a
26-
* {@link https://www.openhab.org/javadoc/latest/org/openhab/core/types/type org.openhab.core.types.Type} or a valid string representation of a type.
25+
* Helper function to convert a JS type to a primitive type accepted by openHAB Core, which often is a string representation of the type.
2726
*
28-
* Number and string are passed through.
29-
* Other objects should implement <code>toOpenHabString</code> (prioritized) or <code>toString</code> to return an openHAB compatible representation.
27+
* Converting any complex type to a primitive type is required to avoid multi-threading issues (as GraalJS does not allow multithreaded access to a script's context),
28+
* e.g. when passing objects to persistence, which then persists asynchronously.
29+
*
30+
* Number and string primitives are passed through.
31+
* Objects should implement <code>toOpenHabString</code> (prioritized) or <code>toString</code> to return an openHAB Core compatible string representation.
3032
*
3133
* @private
3234
* @param {*} value
3335
* @returns {*}
3436
*/
35-
function _toOpenhabType (value) {
37+
function _toOpenhabPrimitiveType (value) {
3638
if (value === null) return 'NULL';
3739
if (value === undefined) return 'UNDEF';
3840
if (typeof value === 'number' || typeof value === 'string') {
@@ -107,7 +109,7 @@ function _isDuration (o) {
107109

108110
module.exports = {
109111
_getItemName,
110-
_toOpenhabType,
112+
_toOpenhabPrimitiveType,
111113
_isItem,
112114
_isQuantity,
113115
_isZonedDateTime,

src/items/item-persistence.js

+16-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const utils = require('../utils');
99
* @private
1010
*/
1111
const { getQuantity, QuantityError } = require('../quantity');
12+
const { _toOpenhabPrimitiveType } = require('../helpers');
1213
const PersistenceExtensions = Java.type('org.openhab.core.persistence.extensions.PersistenceExtensions');
1314

1415
/**
@@ -135,7 +136,7 @@ class ItemPersistence {
135136
}
136137

137138
/**
138-
* Persists the state of a given Item.
139+
* Persists a state of a given Item.
139140
*
140141
* There are four ways to use this method:
141142
* ```js
@@ -158,11 +159,23 @@ class ItemPersistence {
158159
* ```
159160
*
160161
* @param {(time.ZonedDateTime | Date)} [timestamp] the date for the item state to be stored
161-
* @param {string} [state] the state to be stored
162+
* @param {string|number|time.ZonedDateTime|Quantity|HostState} [state] the state to be stored
162163
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
163164
*/
164165
persist (timestamp, state, serviceId) {
165-
PersistenceExtensions.persist(this.rawItem, ...arguments);
166+
switch (arguments.length) {
167+
// persist a given state at a given timestamp
168+
case 2:
169+
PersistenceExtensions.persist(this.rawItem, timestamp, _toOpenhabPrimitiveType(state));
170+
break;
171+
case 3:
172+
PersistenceExtensions.persist(this.rawItem, timestamp, _toOpenhabPrimitiveType(state), serviceId);
173+
break;
174+
// persist the current state or a TimeSeries
175+
default:
176+
PersistenceExtensions.persist(this.rawItem, ...arguments);
177+
break;
178+
}
166179
}
167180

168181
// TODO: Add persist for TimeSeries

src/items/items.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
const osgi = require('../osgi');
88
const utils = require('../utils');
99
const log = require('../log')('items');
10-
const { _toOpenhabType } = require('../helpers');
10+
const { _toOpenhabPrimitiveType } = require('../helpers');
1111
const { getQuantity, QuantityError } = require('../quantity');
1212

1313
const { UnDefType, OnOffType, events, itemRegistry } = require('@runtime');
@@ -234,7 +234,7 @@ class Item {
234234
* @see postUpdate
235235
*/
236236
sendCommand (value) {
237-
events.sendCommand(this.rawItem, _toOpenhabType(value));
237+
events.sendCommand(this.rawItem, _toOpenhabPrimitiveType(value));
238238
}
239239

240240
/**
@@ -245,7 +245,7 @@ class Item {
245245
* @see sendCommand
246246
*/
247247
sendCommandIfDifferent (value) {
248-
value = _toOpenhabType(value);
248+
value = _toOpenhabPrimitiveType(value);
249249
if (value.toString() !== this.state) {
250250
this.sendCommand(value);
251251
return true;
@@ -309,7 +309,7 @@ class Item {
309309
* @see sendCommand
310310
*/
311311
postUpdate (value) {
312-
events.postUpdate(this.rawItem, _toOpenhabType(value));
312+
events.postUpdate(this.rawItem, _toOpenhabPrimitiveType(value));
313313
}
314314

315315
/**

types/items/item-persistence.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ declare class ItemPersistence {
1515
constructor(rawItem: any);
1616
rawItem: any;
1717
/**
18-
* Persists the state of a given Item.
18+
* Persists a state of a given Item.
1919
*
2020
* There are four ways to use this method:
2121
* ```js
@@ -38,10 +38,10 @@ declare class ItemPersistence {
3838
* ```
3939
*
4040
* @param {(time.ZonedDateTime | Date)} [timestamp] the date for the item state to be stored
41-
* @param {string} [state] the state to be stored
41+
* @param {string|number|time.ZonedDateTime|Quantity|HostState} [state] the state to be stored
4242
* @param {string} [serviceId] optional persistence service ID, if omitted, the default persistence service will be used
4343
*/
44-
persist(timestamp?: (time.ZonedDateTime | Date), state?: string, serviceId?: string, ...args: any[]): void;
44+
persist(timestamp?: (time.ZonedDateTime | Date), state?: string | number | time.ZonedDateTime | Quantity | HostState, serviceId?: string, ...args: any[]): void;
4545
/**
4646
* Retrieves the persisted state for a given Item at a certain point in time.
4747
*
@@ -477,6 +477,7 @@ declare namespace time {
477477
type ZonedDateTime = import('@js-joda/core').ZonedDateTime;
478478
}
479479
import time = require("../time");
480+
type Quantity = import('../quantity').Quantity;
480481
/**
481482
* Class representing an instance of {@link https://www.openhab.org/javadoc/latest/org/openhab/core/persistence/historicitem org.openhab.core.persistence.HistoricItem}.
482483
* Extends {@link items.PersistedState}.
@@ -520,5 +521,4 @@ declare class PersistedState {
520521
get quantityState(): import("../quantity").Quantity;
521522
#private;
522523
}
523-
type Quantity = import('../quantity').Quantity;
524524
//# sourceMappingURL=item-persistence.d.ts.map

types/items/item-persistence.d.ts.map

+1-1
Original file line numberDiff line numberDiff line change

0 commit comments

Comments
 (0)