Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing the GraalJS package with OH3 #2

Closed
LukasA83 opened this issue Dec 23, 2020 · 20 comments
Closed

Testing the GraalJS package with OH3 #2

LukasA83 opened this issue Dec 23, 2020 · 20 comments

Comments

@LukasA83
Copy link

LukasA83 commented Dec 23, 2020

hi @jpg0,

as discussed in separate thread, I tried your package, but basically I struggle on the installation, I believe.

Setup:

OH3 Build 2094 (docker debian) on Raspi, installed npm 6.14.10.

"npm i ohj" run successfully from $OPENHAB_CONF/automation/lib/javascript/community.

I created a test rule:
`with(require('ohj').fluent){

//turn on the kitchen light at SUNSET
when(item("Test_Item_Switch").changed()).then(() => { logWarn("testwarn"); });

}`

Somehow it seems not to work, logs:
15:06:27.278 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/openhab/conf/automation/jsr223/javascript/personal/testGraalvm.js': org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: 'ohj'

@LukasA83 LukasA83 changed the title Testing your package with OH3 Testing the GraalJS package with OH3 Dec 23, 2020
@jpg0
Copy link
Owner

jpg0 commented Dec 23, 2020

So you've done everything right, however I don't yet have ohj fully working with OH3 yet.

So I would suggest to either:

  • Wait a little until I have ohj updated, then you can use the library as you are doing here
  • Try doing things without the ohj library. This would require using the OH scriptextensions directly: let simpleRule = require("@runtime/RuleSimple").SimpleRule; ...

Either way, I'll update this once ohj is working (shouldn't be too long, depends on my time during this period!).

@LukasA83
Copy link
Author

Right now I'm still using the javascript helpers provided for OH 2.5 with own Utils and same updates for OH3. Will it work to keep the set-up, maybe with minor adaptions to GraalJS? That would allow me a staggered migration ;-)

@LukasA83
Copy link
Author

Additional question: Going forward will I need to install npm to the Openhab docker image? That feels unhandy in terms of updates.

@jpg0
Copy link
Owner

jpg0 commented Dec 25, 2020

npm shouldn't be required in the docker image. It only downloads and places the JS files, so can do it from the host. It's not needed to run the downloaded JS.

@jpg0
Copy link
Owner

jpg0 commented Dec 25, 2020

As for migration; this is possible, but not as simple as you may expect (it's what I originally did, to try to keep compatibility, but ultimately abandoned). The reason is the global namespacing in the 2.5 helpers; almost all variables in the 2.5 helpers are dropped into the global namespace, whereas the pattern I use for 3.0 is the same as standard module-based JS, where they are put into a specific namespace. So for example:

With namespacing:

let mythings = require('mythings'); //or import scriptExtensions, same pattern
let doorItem = mythings.door;

Without namespacing:

load("../somecrazypath/mythings.js"); //or scriptExtension.importPreset("mythings");
let doorItem = door; //this was just retrieved from the global namespace

I tried to ease migration by explicitly copying namespaced properties into the global namespace to try to emulate the 2.5 approach. This mostly worked, but there were unexpected clashes and overrides all the time that were frustrating. Saying this, if your usage of the 2.5 helpers is simple, it could certainly work. At the very least - examples of how you migrate could certainly help others.

@jpg0 jpg0 closed this as completed Dec 26, 2020
@jpg0 jpg0 reopened this Dec 26, 2020
@jpg0
Copy link
Owner

jpg0 commented Dec 26, 2020

I have published a new version of ohj (3.0.0, to match the OH version that it is targeting). Whilst I have not tested it extensively, it looks to be working fine with OH3.

@LukasA83
Copy link
Author

LukasA83 commented Dec 27, 2020

Today I tried again. Following steps I did:

  1. Upgrade to latest openhab 3 snapshot docker image
  2. Copy jsscript jar from https://github.com/openhab/openhab-addons/files/5718546/org.openhab.automation.jsscripting-3.0.0-SNAPSHOT.jar.zip into addons/
  3. Remove any files in automation/
  4. Install latest ohj via npm
  5. created dummy rule as above w. filename "fritzbox.js"

ohj package.json indicates right version:

{
  "dependencies": {
    "ohj": "^3.0.0"
  }
}

Now I get this error:
During startup:
12:29:11.409 [INFO ] [org.openhab.ui.internal.UIService ] - Started UI on port 50880, 12:29:10.531 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js', 12:29:10.526 [DEBUG] [enhab.core.service.StartLevelService] - Reached start level 20, 12:29:15.535 [INFO ] [e.automation.internal.RuleEngineImpl] - Rule engine started., 12:29:15.532 [DEBUG] [enhab.core.service.StartLevelService] - Reached start level 40, 12:29:15.529 [DEBUG] [enhab.core.service.StartLevelService] - Reached start level 30, 12:29:16.313 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js': <eval>:65:51 Expected an operand but found ), when(item("Test_Item_Switch").changed()).then(() => { logWarn("testwarn"); });, 12:29:17.450 [WARN ] [ty.util.ssl.SslContextFactory.config] - Trusting all certificates configured for Client@7a6c3c46[provider=null,keyStore=null,trustStore=null], ^ in <eval> at line number 65 at column number 51

When changing the rule after startup:

`2:30:15.585 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js'


12:30:19.866 [WARN ] [.internal.OpenhabGraalJSScriptEngine] - Failed to retrieve script script dependency listener from engine bindings. Script dependency tracking will be disabled.


12:30:20.509 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:


org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: 'ohj'


	at <js>.:program(<eval>:41) ~[?:?]


	at org.graalvm.polyglot.Context.eval(Context.java:345) ~[?:?]


	at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:379) ~[?:?]


	at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:343) ~[?:?]


	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:249) ~[java.scripting:?]


	at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:51) ~[bundleFile:?]


	at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:78) ~[bundleFile:?]


	at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:51) ~[bundleFile:?]


	at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:78) [bundleFile:?]


	at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(ScriptEngineManagerImpl.java:170) [bundleFile:?]


	at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.importFile(ScriptFileWatcher.java:191) [bundleFile:?]


	at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.processWatchEvent(ScriptFileWatcher.java:157) [bundleFile:?]


	at org.openhab.core.service.WatchQueueReader.lambda$3(WatchQueueReader.java:322) [bundleFile:?]


	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]


	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]


	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]


	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]


	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]


	at java.lang.Thread.run(Thread.java:834) [?:?]


12:30:20.537 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js': org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: 'ohj'`

@LukasA83
Copy link
Author

I created another js file with some dummy set-up which works, so it seems I'm still struggling with the npm ohj package. Any idea what is going wrong? Dummy script, that works:

const GENERATED_RULE_ITEM_TAG = "GENERATED_RULE_ITEM";


const ModuleBuilder = Java.type("org.openhab.core.automation.util.ModuleBuilder");
const Configuration = Java.type("org.openhab.core.config.core.Configuration");
const LOGGER_PREFIX = "script.js";

let logger = Java.type("org.slf4j.LoggerFactory").getLogger(LOGGER_PREFIX);
//let RuleManager = osgi.getService("org.openhab.core.automation.RuleManager");
const { automationManager } = require('@runtime/RuleSupport');

/**
 * Creates a trigger. Internal function, instead use predefined trigger types.
 * 
 * @memberof triggers
 * @private
 * @param {String} typeString the type of trigger to create
 * @param {String} [name] the name of the trigger
 * @param {Configuration} config the trigger configuration
 */
let createTrigger = function(typeString, name, config) {

    return ModuleBuilder.createTrigger()
        .withId(name)
        .withTypeUID(typeString)
        .withConfiguration(new Configuration(config))
        .build();
}

/**
 * Creates a rule. The rule will be created and immediately available.
 * 
 * @example
 * import { rules, triggers } = require('ohj');
 * 
 * rules.JSRule({
 *  name: "my_new_rule",
 *  description": "this rule swizzles the swallows",
 *  triggers: triggers.GenericCronTrigger("0 30 16 * * ? *"),
 *  execute: triggerConfig => { //do stuff }
 * });
 * 
 * @memberOf rules
 * @param {Object} ruleConfig The rule config describing the rule
 * @param {String} ruleConfig.name the name of the rule
 * @param {String} ruleConfig.description a description of the rule
 * @param {*} ruleConfig.execute callback that will be called when the rule fires
 * @returns {HostRule} the created rule
 */
let JSRule = function (ruleConfig) {
    let ruid = ruleConfig.id || ruleConfig.name.replace(/[^\w]/g, "-") + "-";
    let ruTemplateid = ruleConfig.name.replace(/[^\w]/g, "-") + "-";
    logger.info("Adding rule: {}", ruleConfig.name ? ruleConfig.name : ruid);

    let SimpleRule = Java.extend(Java.type('org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule'));

    let doExecute = function (module, input) {
        try {
            return ruleConfig.execute(input);
        } catch (error) {
            logger.error(`Failed to execute rule ${ruid}: ${error}: ${error.stack}`);
            throw error;
        }
    };

    var rule = new SimpleRule({
        execute: doExecute,
        getUID: () => ruid
    });

    var triggers = ruleConfig.triggers ? ruleConfig.triggers : ruleConfig.getEventTrigger();


    rule.setTemplateUID(ruTemplateid);

    if (ruleConfig.description) {
        rule.setDescription(ruleConfig.description);
    }
    if (ruleConfig.name) {
        rule.setName(ruleConfig.name);
    }

    //Register rule here
    if (triggers && triggers.length > 0) {
        rule.setTriggers(triggers);
        rule = registerRule(rule);
    }

    return rule;
};



let currentProvider = automationManager;

let registerRule = function(rule) {
    return currentProvider.addRule(rule);
}

let testRule = JSRule({
    name: "new rule the old way",
    triggers: [
        createTrigger("timer.GenericCronTrigger","cron-trigger",{
            "cronExpression": "0/30 * * * * ? *"
        })
    ],
    description: "bla",
    execute: triggerConfig => {
        logger.warn("new rule trigger");
    }
});

@LukasA83
Copy link
Author

Ok I think I found one issue. In the readme you refer to install the npm package to the community folder, but it seems it has to be in the personal folder. Now I'm getting this error:

23:21:37.916 [WARN ] [script.js.osgi                      ] - Failed to get service org.openhab.core.items.MetadataRegistry: [object Error]		[osgi at source <unknown>, line 53]


23:21:37.931 [WARN ] [script.js.osgi                      ] - Failed to get service org.eclipse.smarthome.core.items.MetadataRegistry: [object Error]		[osgi at source <unknown>, line 53]


23:21:37.947 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:


org.graalvm.polyglot.PolyglotException: Error: Failed to get any services of type(s): org.openhab.core.items.MetadataRegistry,org.eclipse.smarthome.core.items.MetadataRegistry


	at <js>.getService(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/osgi.js:71) ~[?:?]


	at <js>.:anonymous(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/metadata/metadata.js:13) ~[?:?]


	at <js>.:anonymous(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/items/managed.js:6) ~[?:?]


	at <js>.:anonymous(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/items/items.js:8) ~[?:?]


	at <js>.:anonymous(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/fluent/fluent.js:9) ~[?:?]


	at <js>.get fluent(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/index.js:12) ~[?:?]


	at <js>.:program(<eval>:41) ~[?:?]


	at org.graalvm.polyglot.Context.eval(Context.java:345) ~[?:?]


	at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:379) ~[?:?]


	at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:343) ~[?:?]


	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:249) ~[java.scripting:?]


	at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:51) ~[bundleFile:?]


	at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:78) ~[bundleFile:?]


	at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:51) ~[bundleFile:?]


	at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:78) [bundleFile:?]


	at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(ScriptEngineManagerImpl.java:170) [bundleFile:?]


	at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.importFile(ScriptFileWatcher.java:191) [bundleFile:?]


	at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.processWatchEvent(ScriptFileWatcher.java:157) [bundleFile:?]


	at org.openhab.core.service.WatchQueueReader.lambda$3(WatchQueueReader.java:322) [bundleFile:?]


	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]


	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]


	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]


	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]


	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]


	at java.lang.Thread.run(Thread.java:834) [?:?]


23:21:37.953 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js': org.graalvm.polyglot.PolyglotException: Error: Failed to get any services of type(s): org.openhab.core.items.MetadataRegistry,org.eclipse.smarthome.core.items.MetadataRegistry

@jpg0
Copy link
Owner

jpg0 commented Dec 27, 2020

Sorry that you're having so many issues!

I can see a few causes here:

  • OH3 has changed the startup so that scripts will load too early. This is why you see failures at startup which are not the same as when you add/update the scripts later. This needs to be fixed in openhab-core.
  • You're right that installation must happen in the personal folder, not community. NodeJS only allows a single library folder, and so does GraalJS.
  • I've realised that you're missing the ohj-support addon. I have split the original addon into two - the barebones scripting into the JSScripting addon and various other helpers (that are required by ohj) into ohj-support: https://github.com/jpg0/ohj-support/. I'll upload a build of this...

@jpg0
Copy link
Owner

jpg0 commented Dec 27, 2020

Also: what version of ohj do you have installed? You should be able to see this in the package.json file in the ohj directory. You want 3.0.0.

I ask this because it's failing to load MetadataRegistry, although I am not seeing that error.

@jpg0
Copy link
Owner

jpg0 commented Dec 27, 2020

Here's a build of ohj-support:

org.openhab.automation.ohj-support-3.0.0-SNAPSHOT.jar.zip

@LukasA83
Copy link
Author

I backtraced the error to the osgi.js. the bundleContext seems to be undefined.

@jpg0
Copy link
Owner

jpg0 commented Dec 27, 2020

Oh, and great news that the dummy script works - that signifies that the base JS + modules are working (plus I see it has ES6+ features in there like const, which would fail with the current JS engine). So it's just ohj which is the problem here.

@LukasA83
Copy link
Author

I've added the support addon, still bundleContext seems to be undefined in osgi.js. Any idea?

@LukasA83
Copy link
Author

LukasA83 commented Dec 27, 2020

I got it to work with changing the function "lookupService" in osgi,js like this (quick hack...):

let lookupService = function (classOrName) {
    var bc = bundleContext;
    if(bundleContext === undefined) {    
        log.warn("bundleContext is undefined");
        var FrameworkUtil = Java.type("org.osgi.framework.FrameworkUtil");
        var _bundle = FrameworkUtil.getBundle(scriptExtension.class);
        bc = (_bundle !== null) ? _bundle.getBundleContext() : null;
    }
    if (bc !== null) {
        var classname = (typeof classOrName === "object") ? classOrName.getName() : classOrName;
        var ref = bc.getServiceReference(classname);
        return (ref !== null) ? bc.getService(ref) : null;
    }
}

@jpg0
Copy link
Owner

jpg0 commented Dec 28, 2020

Great! Thanks for letting me know. It looks like you are not resolving require('@runtime/osgi').bundleContext for some reason (it's supplied in ohj-support: https://github.com/jpg0/ohj-support/blob/master/src/main/java/org/openhab/automation/module/script/extension/OSGiScriptExtensionProvider.java).

Either way, I wouldn't worry about it if I were you - your hack should work fine and I'll try to figure out why the standard version is not resolving. ohj is certainly much less stable than the core JS scripting stuff at this point.

@LukasA83
Copy link
Author

I'll use ohj as a starting point and combine with own scripts. For example, I've added most of the other OH triggers and conditions to the helpers...
Are there already helpers in your package to schedule timers, like setTimeout in the former js helper?

@simonihmig
Copy link

For the record: I was now able to sort out the issues I reported in jpg0/ohj#19 (basically a duplicate of this issue) with these steps:

I have just a limited set of rules defined with JS for now, but at least they are working now again w/ OH3!

@jpg0
Copy link
Owner

jpg0 commented Jan 2, 2021

I've merged jpg0/ohj#20 and also included the fix from @LukasA83. Still not really sure why it was failing, but I'm guessing it's something related to the startup ordering issues in OH3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants