Running `org.openhab.demo.app` with `org.openhab.automation.jsscripting` enabled

I’m doing development using Eclipse, and use the demo.app to run OH within Eclipse.

I’m currently working with things related to add-ons and the community marketplace, and a lot of these add-ons rely on jsscripting, so getting the jsscripting add-on working in the development environment would make testing things much easier. I’ve tried to get it working in the past, but have given up.

I have given it another go now, and gotten stuck again. I’ve done what I’ve done with numerous other add-ons to make them work in this environment, I’ve added

    <dependency>
      <groupId>org.openhab.addons.bundles</groupId>
      <artifactId>org.openhab.automation.jsscripting</artifactId>
      <version>${project.version}</version>
      <scope>runtime</scope>
    </dependency>

to the “demo.app” pom.xml. Then, I’ve added org.openhab.automation.jsscripting to the run requirements in the bnd/OSGi “Resolve/Run” view, saved and clicked “Resolve”. This often requires some refreshes, “Update Maven Project”, perhaps some rebuilds of some components, but I usually get it to resolve in the end.

This isn’t the case for org.openhab.automation.jsscripting. The error output is very long, but it seems like it boils down to this:

Unable to resolve <<INITIAL>>: missing requirement osgi.identity;filter:='(osgi.identity=org.openhab.automation.jsscripting)' [caused by: Unable to resolve org.openhab.automation.jsscripting version=5.0.0.202502140010: missing requirement osgi.wiring.bundle;filter:='(&(osgi.wiring.bundle=org.graalvm.sdk.collections)(bundle-version>=24.1.2))']

I didn’t have this particular problem the last time I tried, so I looked in the commit log and found 8c68fb7, which seemed related, so I reverted that commit and rebuilt the jsscripting add-on. The question remains why it won’t resolve with 8c68fb7 though.

After reverting this commit, it did resolve and I was able to start “demo.app”. The add-on still doesn’t work even though OH starts, there are various errors in the log. This seems to be the “root problem”:

java.lang.NoClassDefFoundError: Could not initialize class org.openhab.automation.jsscripting.internal.OpenhabGraalJSScriptEngine
	at org.openhab.automation.jsscripting.internal.GraalJSScriptEngineFactory.createScriptEngine(GraalJSScriptEngineFactory.java:90) ~[?:?]
	at org.openhab.core.automation.module.script.internal.ScriptEngineFactoryHelper.getParameterOption(ScriptEngineFactoryHelper.java:45) ~[?:?]
	at org.openhab.core.automation.module.script.internal.provider.ScriptModuleTypeProvider.setScriptEngineFactory(ScriptModuleTypeProvider.java:150) ~[?:?]
	at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:580) ~[?:?]
	at org.apache.felix.scr.impl.inject.methods.BaseMethod.invokeMethod(BaseMethod.java:245) ~[bundleFile:?]
	at org.apache.felix.scr.impl.inject.methods.BaseMethod.access$500(BaseMethod.java:41) ~[bundleFile:?]
	at org.apache.felix.scr.impl.inject.methods.BaseMethod$Resolved.invoke(BaseMethod.java:687) ~[bundleFile:?]
	at org.apache.felix.scr.impl.inject.methods.BaseMethod.invoke(BaseMethod.java:531) [bundleFile:?]
	at org.apache.felix.scr.impl.inject.methods.BindMethod.invoke(BindMethod.java:42) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager.doInvokeBindMethod(DependencyManager.java:2086) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager.invokeBindMethod(DependencyManager.java:2061) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.invokeBindMethod(SingleComponentManager.java:443) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager$MultipleDynamicCustomizer.addedService(DependencyManager.java:336) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager$MultipleDynamicCustomizer.addedService(DependencyManager.java:304) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1232) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1152) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.trackAdding(ServiceTracker.java:959) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.track(ServiceTracker.java:895) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:1184) [bundleFile:?]
	at org.apache.felix.scr.impl.BundleComponentActivator$ListenerInfo.serviceChanged(BundleComponentActivator.java:116) [bundleFile:?]
	at org.eclipse.osgi.internal.serviceregistry.FilteredServiceListener.serviceChanged(FilteredServiceListener.java:123) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:961) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEventPrivileged(ServiceRegistry.java:937) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEvent(ServiceRegistry.java:874) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.register(ServiceRegistrationImpl.java:141) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.registerService(ServiceRegistry.java:262) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.registerService(BundleContextImpl.java:500) [org.eclipse.osgi-3.18.0.jar:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:929) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:915) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.RegistrationManager.changeRegistration(RegistrationManager.java:133) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.registerService(AbstractComponentManager.java:984) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:752) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager$SingleStaticCustomizer.addedService(DependencyManager.java:1274) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager$SingleStaticCustomizer.addedService(DependencyManager.java:1225) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1232) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1152) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.trackAdding(ServiceTracker.java:959) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.track(ServiceTracker.java:895) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:1184) [bundleFile:?]
	at org.apache.felix.scr.impl.BundleComponentActivator$ListenerInfo.serviceChanged(BundleComponentActivator.java:116) [bundleFile:?]
	at org.eclipse.osgi.internal.serviceregistry.FilteredServiceListener.serviceChanged(FilteredServiceListener.java:123) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:961) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEventPrivileged(ServiceRegistry.java:937) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEvent(ServiceRegistry.java:874) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.register(ServiceRegistrationImpl.java:141) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.registerService(ServiceRegistry.java:262) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.registerService(BundleContextImpl.java:500) [org.eclipse.osgi-3.18.0.jar:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:929) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:915) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.RegistrationManager.changeRegistration(RegistrationManager.java:133) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.registerService(AbstractComponentManager.java:984) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:752) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.enableInternal(AbstractComponentManager.java:674) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.enable(AbstractComponentManager.java:437) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ConfigurableComponentHolder.enableComponents(ConfigurableComponentHolder.java:671) [bundleFile:?]
	at org.apache.felix.scr.impl.BundleComponentActivator.initialEnable(BundleComponentActivator.java:310) [bundleFile:?]
	at org.apache.felix.scr.impl.Activator.loadComponents(Activator.java:593) [bundleFile:?]
	at org.apache.felix.scr.impl.Activator.access$200(Activator.java:74) [bundleFile:?]
	at org.apache.felix.scr.impl.Activator$ScrExtension.start(Activator.java:460) [bundleFile:?]
	at org.apache.felix.scr.impl.AbstractExtender.createExtension(AbstractExtender.java:196) [bundleFile:?]
	at org.apache.felix.scr.impl.AbstractExtender.modifiedBundle(AbstractExtender.java:169) [bundleFile:?]
	at org.apache.felix.scr.impl.AbstractExtender.modifiedBundle(AbstractExtender.java:49) [bundleFile:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:488) [org.eclipse.osgi-3.18.0.jar:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:1) [org.eclipse.osgi-3.18.0.jar:?]
	at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:232) [org.eclipse.osgi-3.18.0.jar:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.bundleChanged(BundleTracker.java:450) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:949) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEventPrivileged(EquinoxEventPublisher.java:229) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:138) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:130) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor.publishModuleEvent(EquinoxContainerAdaptor.java:217) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.container.Module.publishEvent(Module.java:499) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.container.Module.start(Module.java:486) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:445) [org.eclipse.osgi-3.18.0.jar:?]
	at aQute.launcher.Launcher.start(Launcher.java:699) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.startBundles(Launcher.java:679) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.activate(Launcher.java:585) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.launch(Launcher.java:403) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.run(Launcher.java:185) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.main(Launcher.java:161) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.pre.EmbeddedLauncher.executeWithRunPath(EmbeddedLauncher.java:170) [biz.aQute.launcher.pre.jar:?]
	at aQute.launcher.pre.EmbeddedLauncher.findAndExecute(EmbeddedLauncher.java:135) [biz.aQute.launcher.pre.jar:?]
	at aQute.launcher.pre.EmbeddedLauncher.main(EmbeddedLauncher.java:52) [biz.aQute.launcher.pre.jar:?]
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.NoClassDefFoundError: jdk/internal/module/Modules [in thread "main"]
	at com.oracle.truffle.runtime.ModulesSupport.addExports0(Native Method) ~[?:?]
	at com.oracle.truffle.runtime.ModulesSupport.<clinit>(ModulesSupport.java:64) ~[?:?]
	at com.oracle.truffle.runtime.hotspot.HotSpotTruffleRuntimeAccess.createRuntime(HotSpotTruffleRuntimeAccess.java:84) ~[?:?]
	at com.oracle.truffle.runtime.hotspot.HotSpotTruffleRuntimeAccess.getRuntime(HotSpotTruffleRuntimeAccess.java:75) ~[?:?]
	at com.oracle.truffle.api.Truffle.createRuntime(Truffle.java:145) ~[?:?]
	at com.oracle.truffle.api.Truffle$1.run(Truffle.java:176) ~[?:?]
	at com.oracle.truffle.api.Truffle$1.run(Truffle.java:174) ~[?:?]
	at java.security.AccessController.doPrivileged(AccessController.java:319) ~[?:?]
	at com.oracle.truffle.api.Truffle.initRuntime(Truffle.java:174) ~[?:?]
	at com.oracle.truffle.api.Truffle.<clinit>(Truffle.java:63) ~[?:?]
	at com.oracle.truffle.api.impl.Accessor.getTVMCI(Accessor.java:1505) ~[?:?]
	at com.oracle.truffle.api.impl.Accessor$Constants.<clinit>(Accessor.java:1370) ~[?:?]
	at com.oracle.truffle.api.impl.Accessor.engineSupport(Accessor.java:1434) ~[?:?]
	at com.oracle.truffle.api.library.LibraryAccessor.engineAccessor(LibraryAccessor.java:57) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.loadExternalDefaultProviders(LibraryFactory.java:434) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.getExternalDefaultProviders(LibraryFactory.java:425) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.initDefaultExports(LibraryFactory.java:213) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.<init>(LibraryFactory.java:208) ~[?:?]
	at com.oracle.truffle.api.library.DynamicDispatchLibraryGen.<init>(DynamicDispatchLibraryGen.java:33) ~[?:?]
	at com.oracle.truffle.api.library.DynamicDispatchLibraryGen.<clinit>(DynamicDispatchLibraryGen.java:25) ~[?:?]
	at java.lang.Class.forName0(Native Method) ~[?:?]
	at java.lang.Class.forName(Class.java:534) ~[?:?]
	at java.lang.Class.forName(Class.java:513) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.loadGeneratedClass(LibraryFactory.java:770) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.resolveImpl(LibraryFactory.java:751) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.resolve(LibraryFactory.java:744) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.<init>(LibraryFactory.java:202) ~[?:?]
	at com.oracle.truffle.api.interop.InteropLibraryGen.<init>(InteropLibraryGen.java:180) ~[?:?]
	at com.oracle.truffle.api.interop.InteropLibraryGen.<clinit>(InteropLibraryGen.java:171) ~[?:?]
	at java.lang.Class.forName0(Native Method) ~[?:?]
	at java.lang.Class.forName(Class.java:534) ~[?:?]
	at java.lang.Class.forName(Class.java:513) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.loadGeneratedClass(LibraryFactory.java:770) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.resolveImpl(LibraryFactory.java:751) ~[?:?]
	at com.oracle.truffle.api.library.LibraryFactory.resolve(LibraryFactory.java:744) ~[?:?]
	at com.oracle.truffle.api.interop.InteropLibrary.<clinit>(InteropLibrary.java:2972) ~[?:?]
	at com.oracle.truffle.polyglot.PolyglotValueDispatch.<clinit>(PolyglotValueDispatch.java:168) ~[?:?]
	at com.oracle.truffle.polyglot.PolyglotImpl.initialize(PolyglotImpl.java:202) ~[?:?]
	at org.graalvm.polyglot.Engine.loadAndValidateProviders(Engine.java:1691) ~[?:?]
	at org.graalvm.polyglot.Engine$1.run(Engine.java:1717) ~[?:?]
	at org.graalvm.polyglot.Engine$1.run(Engine.java:1712) ~[?:?]
	at java.security.AccessController.doPrivileged(AccessController.java:319) ~[?:?]
	at org.graalvm.polyglot.Engine.initEngineImpl(Engine.java:1712) ~[?:?]
	at org.graalvm.polyglot.Engine$ImplHolder.<clinit>(Engine.java:170) ~[?:?]
	at org.graalvm.polyglot.Engine.getImpl(Engine.java:422) ~[?:?]
	at org.graalvm.polyglot.Source.getImpl(Source.java:167) ~[?:?]
	at org.graalvm.polyglot.Source$Builder.build(Source.java:1027) ~[?:?]
	at org.openhab.automation.jsscripting.internal.OpenhabGraalJSScriptEngine.<clinit>(OpenhabGraalJSScriptEngine.java:81) ~[?:?]
	at org.openhab.automation.jsscripting.internal.GraalJSScriptEngineFactory.createScriptEngine(GraalJSScriptEngineFactory.java:90) ~[?:?]
	at org.openhab.core.automation.module.script.ScriptTransformationServiceFactory.lambda$1(ScriptTransformationServiceFactory.java:73) ~[?:?]
	at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) ~[?:?]
	at org.openhab.core.automation.module.script.ScriptTransformationServiceFactory.setScriptEngineFactory(ScriptTransformationServiceFactory.java:72) ~[?:?]
	... 87 more

I’m not entirely sure exactly what class it can’t find the definitions for, but the result is that it can’t start OpenhabGraalJSScriptEngine it sees. I’m not very “into” the different JS “script engines” (Nashorn, Graal etc.), but do I have to “install” GraalVM separately somehow? I’m using Adoptium OpenJDK 21.0.5.11.

I now downloaded and installed GraalVM JDK 21.0.6+8.1. I configured it in Eclipse and configured “demo.app” to use that JVM. This results in a different error:

java.lang.NoClassDefFoundError: com/oracle/truffle/js/scriptengine/GraalJSEngineFactory
	at org.openhab.automation.jsscripting.internal.GraalJSScriptEngineFactory.<clinit>(GraalJSScriptEngineFactory.java:50) ~[?:?]
	at jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method) ~[?:?]
	at jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1160) ~[?:?]
	at jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:300) ~[?:?]
	at jdk.internal.reflect.MethodHandleAccessorFactory.newConstructorAccessor(MethodHandleAccessorFactory.java:103) ~[?:?]
	at jdk.internal.reflect.ReflectionFactory.newConstructorAccessor(ReflectionFactory.java:200) ~[?:?]
	at java.lang.reflect.Constructor.acquireConstructorAccessor(Constructor.java:549) ~[?:?]
	at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?]
	at java.lang.reflect.Constructor.newInstance(Constructor.java:486) ~[?:?]
	at org.apache.felix.scr.impl.inject.internal.ComponentConstructorImpl.newInstance(ComponentConstructorImpl.java:326) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.createImplementationObject(SingleComponentManager.java:286) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.createComponent(SingleComponentManager.java:115) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:1002) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getServiceInternal(SingleComponentManager.java:975) ~[?:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:920) ~[?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse$1.run(ServiceFactoryUse.java:220) ~[?:?]
	at java.security.AccessController.doPrivileged(AccessController.java:319) [?:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.factoryGetService(ServiceFactoryUse.java:217) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.getService(ServiceFactoryUse.java:118) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceConsumer$2.getService(ServiceConsumer.java:48) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.getService(ServiceRegistrationImpl.java:547) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.getService(ServiceRegistry.java:534) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.getService(BundleContextImpl.java:660) [org.eclipse.osgi-3.18.0.jar:?]
	at org.apache.felix.scr.impl.manager.SingleRefPair.getServiceObject(SingleRefPair.java:88) [bundleFile:?]
	at org.apache.felix.scr.impl.inject.methods.BindMethod.getServiceObject(BindMethod.java:675) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager.getServiceObject(DependencyManager.java:2612) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager.doInvokeBindMethod(DependencyManager.java:2078) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager.invokeBindMethod(DependencyManager.java:2061) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.SingleComponentManager.invokeBindMethod(SingleComponentManager.java:443) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager$MultipleDynamicCustomizer.addedService(DependencyManager.java:336) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager$MultipleDynamicCustomizer.addedService(DependencyManager.java:304) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1232) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1152) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.trackAdding(ServiceTracker.java:959) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.track(ServiceTracker.java:895) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:1184) [bundleFile:?]
	at org.apache.felix.scr.impl.BundleComponentActivator$ListenerInfo.serviceChanged(BundleComponentActivator.java:116) [bundleFile:?]
	at org.eclipse.osgi.internal.serviceregistry.FilteredServiceListener.serviceChanged(FilteredServiceListener.java:123) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:961) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEventPrivileged(ServiceRegistry.java:937) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEvent(ServiceRegistry.java:874) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.register(ServiceRegistrationImpl.java:141) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.registerService(ServiceRegistry.java:262) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.registerService(BundleContextImpl.java:500) [org.eclipse.osgi-3.18.0.jar:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:929) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:915) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.RegistrationManager.changeRegistration(RegistrationManager.java:133) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.registerService(AbstractComponentManager.java:984) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:752) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager$SingleStaticCustomizer.addedService(DependencyManager.java:1274) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.DependencyManager$SingleStaticCustomizer.addedService(DependencyManager.java:1225) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1232) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1152) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.trackAdding(ServiceTracker.java:959) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$AbstractTracked.track(ServiceTracker.java:895) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:1184) [bundleFile:?]
	at org.apache.felix.scr.impl.BundleComponentActivator$ListenerInfo.serviceChanged(BundleComponentActivator.java:116) [bundleFile:?]
	at org.eclipse.osgi.internal.serviceregistry.FilteredServiceListener.serviceChanged(FilteredServiceListener.java:123) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:961) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEventPrivileged(ServiceRegistry.java:937) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEvent(ServiceRegistry.java:874) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.register(ServiceRegistrationImpl.java:141) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.registerService(ServiceRegistry.java:262) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.registerService(BundleContextImpl.java:500) [org.eclipse.osgi-3.18.0.jar:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:929) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:915) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.RegistrationManager.changeRegistration(RegistrationManager.java:133) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.registerService(AbstractComponentManager.java:984) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:752) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.enableInternal(AbstractComponentManager.java:674) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.AbstractComponentManager.enable(AbstractComponentManager.java:437) [bundleFile:?]
	at org.apache.felix.scr.impl.manager.ConfigurableComponentHolder.enableComponents(ConfigurableComponentHolder.java:671) [bundleFile:?]
	at org.apache.felix.scr.impl.BundleComponentActivator.initialEnable(BundleComponentActivator.java:310) [bundleFile:?]
	at org.apache.felix.scr.impl.Activator.loadComponents(Activator.java:593) [bundleFile:?]
	at org.apache.felix.scr.impl.Activator.access$200(Activator.java:74) [bundleFile:?]
	at org.apache.felix.scr.impl.Activator$ScrExtension.start(Activator.java:460) [bundleFile:?]
	at org.apache.felix.scr.impl.AbstractExtender.createExtension(AbstractExtender.java:196) [bundleFile:?]
	at org.apache.felix.scr.impl.AbstractExtender.modifiedBundle(AbstractExtender.java:169) [bundleFile:?]
	at org.apache.felix.scr.impl.AbstractExtender.modifiedBundle(AbstractExtender.java:49) [bundleFile:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:488) [org.eclipse.osgi-3.18.0.jar:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:1) [org.eclipse.osgi-3.18.0.jar:?]
	at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:232) [org.eclipse.osgi-3.18.0.jar:?]
	at org.osgi.util.tracker.BundleTracker$Tracked.bundleChanged(BundleTracker.java:450) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:949) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEventPrivileged(EquinoxEventPublisher.java:229) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:138) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:130) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor.publishModuleEvent(EquinoxContainerAdaptor.java:217) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.container.Module.publishEvent(Module.java:499) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.container.Module.start(Module.java:486) [org.eclipse.osgi-3.18.0.jar:?]
	at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:445) [org.eclipse.osgi-3.18.0.jar:?]
	at aQute.launcher.Launcher.start(Launcher.java:699) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.startBundles(Launcher.java:679) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.activate(Launcher.java:585) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.launch(Launcher.java:403) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.run(Launcher.java:185) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.Launcher.main(Launcher.java:161) [biz.aQute.launcher-7.0.0.jar:?]
	at aQute.launcher.pre.EmbeddedLauncher.executeWithRunPath(EmbeddedLauncher.java:170) [biz.aQute.launcher.pre.jar:?]
	at aQute.launcher.pre.EmbeddedLauncher.findAndExecute(EmbeddedLauncher.java:135) [biz.aQute.launcher.pre.jar:?]
	at aQute.launcher.pre.EmbeddedLauncher.main(EmbeddedLauncher.java:52) [biz.aQute.launcher.pre.jar:?]
Caused by: java.lang.ClassNotFoundException: com.oracle.truffle.js.scriptengine.GraalJSEngineFactory cannot be found by org.openhab.automation.jsscripting_5.0.0.202502140058
	at org.eclipse.osgi.internal.loader.BundleLoader.generateException(BundleLoader.java:541) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass0(BundleLoader.java:536) ~[?:?]
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:416) ~[?:?]
	at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:168) ~[?:?]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[?:?]
	... 106 more

The difference now is that at least OH considers jsscripting “installed”, despite the error. Do I have to build the jsscripting add-on using “GraalVM” too, or is there some other magic involved?

All in all, does this mean that the jsscripting add-on doesn’t work on a standard JVM?

After looking in pom.xml for jsscripting, it does look to me like it’s supposed to work with a standard JVM, pulling in the GraalVM stuff as dependencies instead:

    <dependency>
      <groupId>org.graalvm.polyglot</groupId>
      <artifactId>polyglot</artifactId>
      <version>${graaljs.version}</version>
    </dependency>
    <!-- Graal JavaScript ScriptEngine JSR 223 support -->
    <dependency>
      <groupId>org.graalvm.js</groupId>
      <artifactId>js-scriptengine</artifactId>
      <version>${graaljs.version}</version>
    </dependency>
    <!-- Graal TRegex engine (internally used by Graal JavaScript engine) -->
    <dependency>
      <groupId>org.graalvm.regex</groupId>
      <artifactId>regex</artifactId>
      <version>${graaljs.version}</version>
    </dependency>
    <!-- Graal JavaScript engine (depends on Graal TRegex engine, must be added after it) -->
    <dependency>
      <groupId>org.graalvm.polyglot</groupId>
      <artifactId>js-community</artifactId>
      <version>${graaljs.version}</version>
      <type>pom</type>
      <scope>runtime</scope>
    </dependency>

This doesn’t seem to work for the “demo.app” though.

I’ll add that I have similar problems with the new Python scripting add-on, which also relies on the Graal engine.

My feeling is that this should be possible to get to work also within the Eclipse development environment, but I don’t know how to figure out what needs to change. Most likely, it’s only some dependency issue related to OSGi/Bnd, but really figuring out the details of this seems to be reserved for a few hard-core experts in the field.

Lo and behold, I got JS scripting to work (after quite a few hours of trying). I’ve tried so many things that I can’t explain how I got here, but by looking at the changes to my files vs before it was working, can hopefully cover the essentials to get this up and running.

In the Demo App’s POM, the osgiified versions of the Graal dependencies must be added, because they aren’t defined in the add-on itself (which I think perhaps would have been a more appropriate location).

First I added a GraalVM version so that I didn’t have to repeat that everywhere, and so that it’s easy to change. Next, I added org.openhab.automation.jsscripting as an add-on as you’d do with any add-on. Then, further down, but still in the <dependencies> section, I added all the GraaVM dependencies. These aren’t needed in our code, but GraalJS won’t work without them. And, they have to be the org.openhab.osgiify version, not their normal Maven group ID:

 launch/app/pom.xml | 102 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 96 insertions(+), 6 deletions(-)

diff --git a/launch/app/pom.xml b/launch/app/pom.xml
index ce573ca..aaa9ab8 100644
--- a/launch/app/pom.xml
+++ b/launch/app/pom.xml
@@ -17,6 +17,7 @@
     <oh.java.version>21</oh.java.version>
     <maven.compiler.release>${oh.java.version}</maven.compiler.release>
     <bnd.version>7.1.0</bnd.version>
+    <graalvm.version>25.0.1</graalvm.version>
   </properties>
 
   <dependencies>
@@ -56,6 +57,19 @@
       <scope>runtime</scope>
     </dependency>
 
+    <dependency>
+      <groupId>org.openhab.addons.bundles</groupId>
+      <artifactId>org.openhab.automation.jsscripting</artifactId>
+      <version>${project.version}</version>
+      <scope>runtime</scope>
+    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.automation.jrubyscripting</artifactId>

@@ -213,6 +221,88 @@
       <scope>runtime</scope>
     </dependency>
     
+    <!-- GraalVM dependencies -->
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.polyglot.polyglot</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.js.js-scriptengine</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.js.js-language</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.regex.regex</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.sdk.collections</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.sdk.nativeimage</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.sdk.jniutils</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.sdk.word</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.shadowed.icu4j</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.shadowed.json</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.shadowed.xz</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.tools.chromeinspector-tool</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.tools.profiler-tool</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.truffle.truffle-api</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.truffle.truffle-compiler</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.openhab.osgiify</groupId>
+      <artifactId>org.graalvm.truffle.truffle-runtime</artifactId>
+      <version>${graalvm.version}</version>
+    </dependency>
+    
 <!-- uncomment this and add the name of your binding that you want to work on
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>

Now to the “magic”, in app.bndrun. First, I defined something that corresponds to the Karaf feature that is defined for JS scripting. The benefit of defining it this way is that I can easily include or exclude these dependencies as a group. I called it simply feature.graal-js, and added this “feature” under -runrequires:. I also added org.openhab.automation.jsscripting separately, but I see that it might as well have been part of the “feature” as well.

Until now, it’s reasonably straight forward, now comes the magic. With OSGi’s tightened “class isolation rules”, it took a lot of tries to find a configuration that would give GraalVM the necessary permissions to integrate with the JVM. Setting bootdelegation to be *, unnamed modules and access modifiers are all part of this.

Additional permissions to export and open jdk.internal.access had to be given in the JVM command line arguments as well. Last but not least, some further magic with -runbootdelegation: and -runsystempackages: to make sure that the whole thing works, and the necessary permissions are granted.

 launch/app/app.bndrun | 209 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 133 insertions(+), 76 deletions(-)

diff --git a/launch/app/app.bndrun b/launch/app/app.bndrun
index d94ca35..fa6001d 100644
--- a/launch/app/app.bndrun
+++ b/launch/app/app.bndrun
@@ -43,12 +43,31 @@ feature.openhab-model-runtime-all: \
 	bnd.identity;id='org.openhab.core.model.sitemap.runtime',\
 	bnd.identity;id='org.openhab.core.model.thing.runtime'
 
+feature.graal-js: \
+	bnd.identity;id='org.graalvm.js.js-language',\
+	bnd.identity;id='org.graalvm.js.js-scriptengine',\
+	bnd.identity;id='org.graalvm.polyglot.polyglot',\
+	bnd.identity;id='org.graalvm.regex.regex',\
+	bnd.identity;id='org.graalvm.sdk.collections',\
+	bnd.identity;id='org.graalvm.sdk.jniutils',\
+	bnd.identity;id='org.graalvm.sdk.nativeimage',\
+	bnd.identity;id='org.graalvm.sdk.word',\
+	bnd.identity;id='org.graalvm.shadowed.icu4j',\
+	bnd.identity;id='org.graalvm.shadowed.json',\
+	bnd.identity;id='org.graalvm.shadowed.xz',\
+	bnd.identity;id='org.graalvm.tools.chromeinspector-tool',\
+	bnd.identity;id='org.graalvm.tools.profiler-tool',\
+	bnd.identity;id='org.graalvm.truffle.truffle-api',\
+	bnd.identity;id='org.graalvm.truffle.truffle-compiler',\
+	bnd.identity;id='org.graalvm.truffle.truffle-runtime'
+
 -runrequires: \
 	bnd.identity;id='org.eclipse.equinox.metatype',\
 	${feature.logging},\
 	${feature.debug},\
 	${feature.openhab-base},\
 	${feature.openhab-model-runtime-all},\
+	${feature.graal-js},\
 	bnd.identity;id='org.openhab.core.addon.eclipse',\
 	bnd.identity;id='org.openhab.core.addon.marketplace',\
 	bnd.identity;id='org.openhab.core.automation',\
 
@@ -107,7 +125,13 @@ feature.openhab-model-runtime-all: \
 	bnd.identity;id='org.openhab.binding.zwave',\
 	bnd.identity;id='org.openhab.io.thingtypes',\
 	bnd.identity;id='org.openhab.core.automation.module.script.providersupport',\
-	bnd.identity;id='org.openhab.binding.energidataservice'
+	bnd.identity;id='org.openhab.binding.energidataservice',\
+	bnd.identity;id='org.openhab.automation.jsscripting'
 
 -runfw: org.eclipse.osgi
 
@@ -121,7 +145,7 @@ feature.openhab-model-runtime-all: \
 
 -runproperties: \
 	felix.cm.dir=${.}/runtime/userdata/config,\
-	org.osgi.framework.bootdelegation="sun.misc",\
+	org.osgi.framework.bootdelegation=*,\
 	org.osgi.service.http.port=8080,\
 	osgi.console=,\
 	osgi.console.enable.builtin=false,\
@@ -131,7 +155,9 @@ feature.openhab-model-runtime-all: \
 	openhab.runtime=${.}/runtime/runtime,\
 	openhab.conf=${.}/runtime/conf,\
 	openhab.userdata=${.}/runtime/userdata,\
-	openhab.logdir=${.}/runtime/userdata/logs
+	openhab.logdir=${.}/runtime/userdata/logs,\
+	org.osgi.framework.system.packages.extra=jdk.internal.access,\
+	equinox.osgi.unnamed.modules=true
 
 -runblacklist: \
 	bnd.identity;id='jakarta.ws.rs-api',\
@@ -160,11 +186,23 @@ feature.openhab-model-runtime-all: \
 	--add-opens=java.base/java.util=ALL-UNNAMED,\
 	--add-opens=java.desktop/java.awt.font=ALL-UNNAMED,\
 	--add-opens=java.naming/javax.naming.spi=ALL-UNNAMED,\
-	--add-opens=java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED
+	--add-opens=java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED,\
+	--add-exports=java.base/jdk.internal.access=ALL-UNNAMED,\
+	--add-opens=java.base/jdk.internal.access=ALL-UNNAMED,\
 
 #
 # done
 #
+
+-runbootdelegation: \
+	jdk.internal.access.*,\
+	jdk.internal.access
+-runsystempackages: \
+	jdk.internal.access,\
+	org.graalvm.polyglot,\
+	org.graalvm.polyglot.proxy,\
+	org.graalvm.polyglot.io
+

After saving the file (14 minutes) and waiting for the implicit repo to be created (5 min), I could finally “resolve”. That lead to a number of changes under -runbundles: that I’m not going to repeat here, because it’s resolved by BndTools, not by a human. Once that is done, save (and wait another 19 minutes) and you’re ready for the final bit.

That is to searh for/find org.graalvm.truffle.truffle-api, which should be among the -runbundles: at this time, and add ;extension=framework directly after the last quote. Mine looks like this now (don’t touch the version, it should be what “resolve” came up with):

	org.graalvm.truffle.truffle-api;version='[25.0.1,25.0.2)';extension=framework,\

Save, wait another 19 minutes, and it should be ready to start. About the long waiting times, I know they are much shorter on Linux. It’s because Windows have file locking, so there’s a lot of lookup in “locking” and premission registries going on when files are accessed, that there’s LOT of files. Don’t forget to exclude the path containing the source files from the virus scanning, or it will take even much more time. Regardless how good or bad the explanation/reason for this situation is, it’s extremely frustrating. Every time I make a typo, it’s another 19 minutes to wait until I can edit my mistake and then stat wating for yet another 19 minutes.

Anyway, it’s running! I haven’t seen any probems this far. I will make it possible to test things that involve JS, something that has been a real hindrance for me to not have had access to. Perhaps this shoud be “organized” and put into app.bndrun in openhab-distro Some sections could be commented out, so that it was easy for somebody that wanted to do it to “enable” this configuration.

I’ll try to structure it in a way that makes it easier to copy/paste into a configuration.

Demo App POM

The path to the file is openhab-distro/launch/app/pom.xml.

In the <properties> section, add (adjust the version as needed):

    <graalvm.version>25.0.1</graalvm.version>

Somewhere in the <dependencies> section, add both the add-on itself and the Graal dependencies:

    <dependency>
      <groupId>org.openhab.addons.bundles</groupId>
      <artifactId>org.openhab.automation.jsscripting</artifactId>
      <version>${project.version}</version>
      <scope>runtime</scope>
    </dependency>

    <!-- GraalVM dependencies -->
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.polyglot.polyglot</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.js.js-scriptengine</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.js.js-language</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.regex.regex</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.sdk.collections</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.sdk.nativeimage</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.sdk.jniutils</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.sdk.word</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.shadowed.icu4j</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.shadowed.json</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.shadowed.xz</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.tools.chromeinspector-tool</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.tools.profiler-tool</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.truffle.truffle-api</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.truffle.truffle-compiler</artifactId>
      <version>${graalvm.version}</version>
    </dependency>
    <dependency>
      <groupId>org.openhab.osgiify</groupId>
      <artifactId>org.graalvm.truffle.truffle-runtime</artifactId>
      <version>${graalvm.version}</version>
    </dependency>

Demo App Bndrun

The path to the file is openhab-distro/launch/app/app.bndrun.

Outside any section, early in the file, define the “feature”:

feature.jsscripting: \
	bnd.identity;id='org.openhab.automation.jsscripting',\
	bnd.identity;id='org.graalvm.js.js-language',\
	bnd.identity;id='org.graalvm.js.js-scriptengine',\
	bnd.identity;id='org.graalvm.polyglot.polyglot',\
	bnd.identity;id='org.graalvm.regex.regex',\
	bnd.identity;id='org.graalvm.sdk.collections',\
	bnd.identity;id='org.graalvm.sdk.jniutils',\
	bnd.identity;id='org.graalvm.sdk.nativeimage',\
	bnd.identity;id='org.graalvm.sdk.word',\
	bnd.identity;id='org.graalvm.shadowed.icu4j',\
	bnd.identity;id='org.graalvm.shadowed.json',\
	bnd.identity;id='org.graalvm.shadowed.xz',\
	bnd.identity;id='org.graalvm.tools.chromeinspector-tool',\
	bnd.identity;id='org.graalvm.tools.profiler-tool',\
	bnd.identity;id='org.graalvm.truffle.truffle-api',\
	bnd.identity;id='org.graalvm.truffle.truffle-compiler',\
	bnd.identity;id='org.graalvm.truffle.truffle-runtime'

Then, in the -runrequires: section, add the “feature” by adding:

	${feature.jsscripting},\

In the -runproperties: section, modify bootdelegation so that it looks like this:

	org.osgi.framework.bootdelegation=*,\

…and add these entries (remember to add ,\ on the previous line:

	org.osgi.framework.system.packages.extra=jdk.internal.access,\
	equinox.osgi.unnamed.modules=true

In the -runvm: section, add (still handling ,\ on the previous line)

	--add-exports=java.base/jdk.internal.access=ALL-UNNAMED,\
	--add-opens=java.base/jdk.internal.access=ALL-UNNAMED

And, finally, add these new sections:

-runbootdelegation: \
	jdk.internal.access.*,\
	jdk.internal.access
-runsystempackages: \
	jdk.internal.access,\
	org.graalvm.polyglot,\
	org.graalvm.polyglot.proxy,\
	org.graalvm.polyglot.io

Save, update/refresh and do whatever is neccesary to make this “resolve” in Eclipse. Once that is done, edit the file again and find the entry for org.graalvm.truffle.truffle-api, and add ;extension=framework to it (before the ,\). The version might be different, but mine looks like this now:

	org.graalvm.truffle.truffle-api;version='[25.0.1,25.0.2)';extension=framework,\

Save the edit, and once Eclipse is ready, start openHAB using “Debug OSGi”. Happy testing/coding!

Just as a warning, if anybody tries to replicate this, I currently can’t get it to work, so it seems a bit unstable/volatile. I tried to get GraalPy as well working, but now I can’t even get GraalJS running - so some detail that eludes me must now be wrong.

Trying to “debug” this is extremely painful. For some reason only the BndTools/M2E/Eclipse gods know, saving the bndrun file is extremely slow from within Eclipse, it takes around 19 minutes on my computer - during this time, Eclipse is unresponsive, and one CPU core is at 100%. If I try to edit the file outside Eclipse, when Eclipse finally realizes that it has changed, it starts an even longer “freeze” of “Refreshing workspace” that just stays at 0%.

If I need to change the bndrun file and then resolve (which triggers another save), that takes 38 minutes (and I must hang around to press “resolve” once the initial save is finished). I’m trying to “backstep” the further changes I did to make Python work until hopefully JavaScript starts working again, to find exactly what made it stop working. But, doing incremental changes with these save times is almost unbearable. So, I’ll just do this in “small portions” at a time, to not lose my mind from frustration.

BndTools is just horribly slow on Windows in general, it’s completely different on Linux. It might do that I would save a lot of time just doing this on a VM, but then I couldn’t really be sure that the results would be valid on Windows. There’s also quite a lot to set up on a VM to get “in position” to start troubleshooting this in the first place, but it might ultimately be what I’m forced to do.

Having the add-ons repo constantly triggering and failing the markdown linting certainly doesn’t help with the frustration, but I’ve reverted that commit locally for now, so that there’s at least a small chance that I’ll retain some sanity.

@holger_hees It feels like I’m really close to get GraalPy as well as GraalJS working in Eclipse. It would be very helpful for all kind of testing if it worked. I have actually gotten it working on a Fedora VM, but on my Windows dev computer, it just won’t work.

It doesn’t really produce any real error, no exception or similar, but the log states:

05:26:24.746 [DEBUG] (main                ) [nal.ScriptEngineFactoryBundleTracker] - Modified org.openhab.automation.pythonscripting: STARTING
05:26:24.819 [DEBUG] (main                ) [t.ScriptTransformationServiceFactory] - bundle org.openhab.core.automation.module.script:5.2.0.202605281202 (50)[org.openhab.core.automation.module.script.ScriptTransformationServiceFactory(100)] : dm ScriptEngineFactory tracking 6 MultipleDynamic already active, binding {org.openhab.core.automation.module.script.ScriptEngineFactory, org.openhab.automation.pythonscripting.internal.PythonScriptEngineFactory}={service.id=665, service.bundleid=247, service.scope=bundle, osgi.ds.satisfying.condition.target=(osgi.condition.id=true), service.config.label=Python Scripting, component.name=org.openhab.automation.pythonscripting.internal.PythonScriptEngineFactory, component.id=453, service.config.factory=false, service.config.category=automation, service.pid=org.openhab.automation.pythonscripting, service.config.description.uri=automation:pythonscripting}
05:26:24.843 [INFO ] (main                ) [ng.internal.PythonScriptEngineHelper] - Checking for helper libs version '1.0.19'
05:26:24.971 [ERROR] (main                ) [g.internal.PythonScriptEngineFactory] - Graal Python language not initialized. Restart openHAB to initialize available Graal languages properly.

So, everything appears to “work” except that the engine isn’t there, which is why getLanguage() fails to return a result and the error is logged.

Will the Python add-on work at all under Windows? I find it kind of strange that the setup that works on Fedora don’t work under Windows. I guess it could be because of path length limitations, but I’m not going to reboot just to test that. It would still be “bad” if it required “long paths” to be enabled just to work.

Any ideas?

I never tested it on windows, but I helped another user to get it working there. He reported after, that it was successful. So it should work.

I just remember. The only fix I had to implement was the path separator handling. There should no hardcoded path separator anymore.

sorry, no. I’m not an maven expert, so I had always difficulties to made changes there.

This has nothing to do with Maven. It’s about why the GraalPy engine won’t start, and how to troubleshoot/find the cause. I think I believe that you had some explanation of how GraalJS and GraalPy could get in each other’s way or something like that earlier? It should be mentioned that while GraalPy fails to start, GraalJS is running, so it’s not a failure of the Graal components themselves.

what I want to say is that I got the demo app together with graal never working too. I should rephrase… Instead of “not a maven” expert, I can rephrase “not an expert of all this demo app build process / configuration stuff”. In the beginning, someone helped me, setting up the maven dependencies in a correct way. The rest comes from me. So, maybe I can just help with some guesses.


GraalJS and GraalPy can only interfere with one another during initialization.

Example:

First, the JSScripting binding is enabled. Upon its initial use, a static variable containing the available Graal languages ​​is set internally within the Graal implementation.

If GraalPy is subsequently enabled, it will not be included among the available languages, as the static variable is not reset.

In this scenario, the only solution is to restart the VM.

If both Graal languages ​​are active/available simultaneously, they are “registered” together.

Consequently, the GraalJS and GraalPy add-ons include a warning prompting the user to restart openHAB if this scenario is detected (i.e., if the GraalPy class has been loaded, but Python is not listed as an available language).

I’m not sure if I understand completely what you mean, the result comes from this method in Engine:

    /**
     * Gets a map of all installed languages with the language id as key and the language object as
     * value. The returned map is unmodifiable and might be used from multiple threads.
     *
     * @since 19.0
     */
    @SuppressWarnings("unchecked")
    public Map<String, Language> getLanguages() {
        try {
            return (Map<String, Language>) (Map<String, ?>) dispatch.getLanguages(receiver);
        } finally {
            Reference.reachabilityFence(creatorEngine);
        }
    }

I can’t look up the source code for any implementation of the abstract class that is dispatch (probably yet more isolation BS), but this method does seem to indicate that it’s intended for multithreaded use. It would be extraordinarily stupid by the Graal developers if the source of this information is immutable, so I assume that the immutability only applies to the returned copy..?

Does it ever happen that Python registers, but JS does not, or is it always Python that’s missing?

the call “dispatch.getLanguages()” get the languages internally from a LanguageCache and this is static

I already discussed this a year ago with the graalvm devs on their slack channel and they confirmed, but it has not a high prio on their side. It affects only environments, where the classes are added/removed at runtime. But if they are added and the vm is restartet, everything is fine.

it always depends, which language was added first.

What an incredibly stupid thing to do. It wouldn’t be a problem if the map was static, if they just didn’t “cache” it once and never update it. That can easily be done in a thread-safe manner, and unless it is called constantly internally, it shouldn’t be much of a performance concern. At the very least they could make available some method that would trigger a “cache rebuild”. This basically means that Graal is broken for dynamic use.

what should I say, you are right. I guess, they never think about our use case. But it is like it is and we made best out of it. Lets hope they fix it sometimes!


I also think that, since we are using OSGi, they aren’t particularly motivated either. :slight_smile:

Why does that matter? This is a problem for any application that wants to do any changes after the initial startup, OSGi or not. It is, as I see it, something that should be classified as a bug.

Usually, yes. But the Graal devs initially asked for an example, only to reject it once OSGi came into play. After that, they offered a little help in the chat—but nothing more.

I just confirmed that this is indeed the problem I’m facing. When disabling JS scripting, Python works. That means that I’ve gotten both JS and Python to work, but whether they both work together depends on random timing.

edit: A new write-up on how to get all this working is coming, but I want to try to see if I can use startlevels to get around the timing problem. Given that I must wait 19 minutes for every change to the bndrun file, testing things is very slow - and when I need to resolve, the wait is double that.

A little teaser:

I think I’m close to solving more than just this…