JSR223 Java Scripting - some demo code

The other day I stumbled over Eric Obermühlner’s Java JSR 223 ScriptEngine. java-scriptengine uses the javax ToolProvider SystemJavaCompiler to compile a script into a class in memory, no class file in the file system.

I tried if it would work with openHAB’s JSR 223 engine. I used the Groovy scripting addon as template. My hack soon could load and run simple classes with only JRE imports.

The openHAB JSR 223 engine sets some 70 bindings for the script, which java-scriptengine expects that there are attributes in the classes for each key. I hacked it so that a method setBindings(Map) would be called with all bindings.

If anybody wants to have a look, it’s at GitHub - weberjn/org.openhab.automation.javarules

This is by far not production ready, I did it just to play with the ScriptEngine. Now it would need some more classloader fiddling to access openHAB’s classes to make it useful.

A script in real scripting language gets executed on loading. This does not work for Java, in Java there is no code outside of methods. So one needs an entry method, javarules calls the usual main after the Java script is loaded. So, to do something useful, main would have to register its class in the automation manager.

1 Like

Hello,
Did you check the work on JRule ?
The concept is very close. But I’m also very interested in your work, as the approach differs and can fix some drawbacks.

JRule doesn’t use the JSR223 as closely as openHAB does with the other langages. It listens on the event bus and implement it’s own logic of triggering event. It is very good, but one thing keep bothering me : it doesn’t follow the same principle, and thus doesn’t have the same facility (i.e. displaying rule on the GUI, benefit from the 70 injection variable, etc)

After checking JRule, I took an interest in the subject, and I took exactly the same path as you. In fact, I did the same work with Eric Obermühlner JSR 223 script engine :sweat_smile: ! I started a proof of concept, not advanced as yours, but nonetheless we were on the same path. So thank you for your work. I think it is a good idea, and maybe it is possible to merge some efforts.

Did you also check jshell script engine ?
It is also from Eric Obermühlner and it could remove the need for an entry method / class. It could also (I think ?) set value dynamically, with no need of a binding map method.
I did not have time to try it but it is definitely on my todo list !

Yes, I had a look at JRule, I also committed to it. JRule does a very good job. But JRule does a lot by itself not based on the framework automation, so it has a lot of code compared to the Groovy automation engine.

I am a Java guy and like to have rules in Java (or Groovy). With Java and Groovy rules you can develop in Eclipse and remote-debug them…

Even if I have to concede that the classic DSL rule engine is rock solid and does its job.

I am playing with JSR 223 Groovy, and so I’m intrigued by Eric Obermühlner’s java-scriptengine which promises a Java implementation very much along the Groovy and Jython engines. And also I like that writing javarules helps me understand the Groovy engine better.

Actually I stumbled over jshell script engine first, but Eric recommends java-scriptengine as jshell is slower.

The need for an entry point doesn’t look like a problem, why not start from an entry point instead of top down in the script?
Perhaps even a static {} block might work instead of an entry point, I didn’t try it yet.

Writing rules that only used JRE libs very soon worked. Now I am stuck at the OSGI classloader refusing to provide openHAB classes to the script. I don’t know yet if this is possible at all, but if it works for Groovy, it should work for Java, too, I hope.
Also the javax compiler api is challenging.

I found a Stackoverflow post which gives a good path to follow. The author has written some interesting OSGI code, too.

Also there is still

[WARN ] [core.karaf.internal.FeatureInstaller] - The automation add-on 'javarules' does not exist - ignoring it.

I’m not sure where I have to register it. But it works anyway.

Excellent ! I should have read the JRule thread better :sweat_smile:
I will try your code as soon as possible.

Classloading via the OSGI classLoader works now.
I had ignored groovyscripting’s package-info.java
with the crucial DYNAMICIMPORT_PACKAGE *

2022-01-13 22:37:53.686 [INFO ] [rg.openhab.core.automation.javarules] - Hello java world!

I also did this work once with the Kotlin JSR223 engine and got some simple file loaded and executed, but in the end I had to stop due to a restriction within the Kotlin engine code that prevented the access to OH core classes (OSGI and classloaders is a lot of fun, I spent hours trying to understand how this works :stuck_out_tongue: ). I hope you don’t hit a similar wall with the java engine.

Why don’t you use the Groovy engine? Its close enough to Java (I’m in the same situation as you, Java developer, wanting to use the IDE, etc.) and works.
Here are some quite fresh links with some info, I have the feeling there is some movement going on… a year ago I felt I was one of 3 guys using Groovy at all.

No, only partially. I can load org.openhab.core.config.core.Configuration but not the classes from the binding like org.openhab.core.automation.module.script.internal.ScriptExtensionManagerWrapper
But in Groovy they cannot be imported, either.

Are internal classes not visible? Where is this defined?

Actually I am good with the Groovy engine. I was just playing with java-scriptengine and the low hanging fruit were so promising that I got stuck and then spent some hours with the secrets of javax.tools.JavaFileManager :wink:

Anyway, thanks for your Groovy examples.

JSR223 Java is somewhat working now, at least this script ported from the JSR 223 Groovy sample.

Scripts have to extend ScriptBase and implement onLoad().

Contrary to Groovy Java cannot dynamically infer the methods of an object, so ScriptBase does some reflection hacks.

public class Script extends ScriptBase {

	private Logger logger = LoggerFactory.getLogger("org.openhab.core.automation.javarules.script");

	public int counter = 1;
	
	protected void onLoad() {
		try {
			ThingActions thingActions = actions.get("mqtt","mqtt:broker:nico");
			actions.invoke(thingActions, "publishMQTT", "about/java","Java script onload()");
			
			SimpleRule sr = new SimpleRule() {

				@Override
				public Object execute(Action module, Map<String, ?> inputs) {

					logger.info("Java execute {},  inputs: {} ", counter++, inputs);

					return null;
				}
			};
			
			sr.setName("Java-One");

			List<Trigger> triggers = new ArrayList<Trigger>(1);

			Map<String, Object> triggerConf = new HashMap<String, Object>();
			triggerConf.put("cronExpression", "0 * * * * ?");

			Trigger trigger = TriggerBuilder.create().withId("aTimerTrigger").withTypeUID("timer.GenericCronTrigger")
					.withConfiguration(new Configuration(triggerConf)).build();

			triggers.add(trigger);

			sr.setTriggers(triggers);

			automationManager.addRule(sr);
		} catch (Throwable e) {
			e.printStackTrace(System.err);
			logger.trace(e.getMessage(), e);

			throw e;
		}
	}
}

There is a Beta 1 release now at GitHub - weberjn/org.openhab.automation.javarules

I’d be grateful if you’d play with and test the Java rules addon. I have included several mini samples that should get people started.

javarules depends on several 3.2.0 bundles, so it only works under 3.2.0 release.