Error when creating a rule inside an interval after its cancellation

  • Platform information:
    • Hardware: Raspberry 4
    • OS: Raspian, openHAB running within a docker container
    • openHAB version: Snapshot, openHAB 3.3.0,

Hello.
I have a function that runs at a certain interval. Under certain conditions, I cancel the interval execution of this function and create a rule.

const { JSRule } = require("openhab/rules/rules");
const { ItemStateChangeTrigger } = require("openhab/triggers");

var interval = setInterval(() => {
    clearInterval(interval);
    // interval.cancel();
    try {
        JSRule({
            name: "test",
            triggers: ItemStateChangeTrigger("test1"),
            execute: () => {
                console.log("execute");
            }
        })
    } catch (err) {
        console.log('err' + err);
    }

}, 1000, 1000);

During the creation of this rule, I get an error.

[ore.internal.scheduler.SchedulerImpl] - Scheduled job ‘’ failed and stopped
java.lang.IllegalStateException: The Context is already closed.

The error occurs in this line of code when calling

https://github.com/openhab/openhab-js/blob/main/rules/rules.js#L228

ruleConfig.name.replace(/[^\w]/g, '-')

If you remove the replacement, the code works successfully.

If you implement the timer in a “standard way”

setInterval = function (fn, delays, period) {
    var timer = new Timer('jsInterval', false);    
    var tt = new TimerTask{ 
        run: fn       
    };
    timer.schedule(tt, delays, period);
    return timer;
}

Then there is no error.

Also, if you wrap the rule creation call with a timeout of 1 millisecond, then everything works correctly

const { JSRule } = require("openhab/rules/rules");
const { ItemStateChangeTrigger } = require("openhab/triggers");

var interval = setInterval(() => {
    clearInterval(interval);
    // interval.cancel();
    try {
        setTimeout(() => {
            JSRule({
                name: "test",
                triggers: ItemStateChangeTrigger("test1"),
                execute: () => {
                    console.log("execute");
                }
            })
        }, 1);
    } catch (err) {
        console.log('err' + err);
    }

}, 1000, 1000);

Am I doing something wrong?

What’s the use case for this? I can think of all sorts of cases where one might want to dynamically create a rule, but I can’t imagine why one would want to create a rule after a timer goes off.

In UI rules I usually see that error when a rule puts an Object into cache, then later on that rule is recreated (e.g. edited and saved). Any subsequent attempt to reference that original Object from cache throws this error because the rule (i.e. context) that put it there no longer exists.

I’d expect something similar to be happening here. By the time the timer actually runs, something that it references no longer exists. Maybe none of that stuff exists any more (JSRule, ItemStateChangeTrigger, console, etc.).

Beyond that, I can’t help much. I spend so much time trying to help people dealing with text files that I just don’t think they are worth messing with any more.

But I suspect there are alternative ways to achieve the same end goal that does not involve creating a rule from a timer.

I will cite close, but not directly my case.
There is a rule that controls the water pressure in the tank by turning on and off the pump. 90% of the time it should be within the pressure range A1 and A2. The remaining time is 10%, it should be within the pressures B1 and B2.
Some kind of command sequence works and there is a timer in it, at the end of which you need to create a pump control rule so that it creates pressure in the tank not between A1 and A2, but between B1 and B2.

I would also suggest not trying to dynamically create rules like this. I am not specifically against their creation in general, however:

  • the JS library works with managed rules, meaning that openHAB tries to manage the lifecycle of rules itself, which can clash with scripts also trying to do this. I have had problems here myself and I believe that the solution is for the script library to support unmanaged rules (for which I began adding support but I don’t believe that it’s complete).

  • I suspect that you can create a simpler system without them in this case. Better to have one or two rules that operate based on the current state of the system instead of rules that are created based on the system state. For example a single rule that can determine whether it needs to use the A or B limits at the start of it’s execution, or a rule for A and one for B, each which do nothing if they should not be active. You can store state either in an item or in the script cache.

One other option is to toggle the enabled state of fixed rules, but even this seems overkill here.

I’ll second @jpg0’s advice. Rules are event driven and can be further controlled using state. You should lean into that. Create both rules at startup and control which one runs when the right event occurs and the state is right.

There’s no need to create a new rule on demand like this for this use case (which is why I asked for the use case). There are simpler and more straight forward ways to achieve the same result.

Me too. I have some use cases where it would improve the usability of some of my rule templates if I could dynamically create the rule triggers (e.g. create a trigger for all Items with a given tag) but I’ve not found a way to do that to an existing rule. I need to delete and recreate.

And I’ve found, at least for the UI rules, once I create the rule I can’t delete it. When/if it becomes a big deal, I might write a library to edit the rule through the REST API, assuming we can’t make it work through the openhab-js library.

I’m slowly starting to transition all my rule templates away from Nashorn and making them depend on my openhab-rules-tools library for timer management and the like. That might give me the kick to revisit this.