Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions bundles/org.openhab.automation.java223/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ A Java223 script does not need to have any dependency to anything in order to be

```java
public class SimpleClass {
public void main() {
void main() {
int sum = 2 + 2;
}
}
Expand Down Expand Up @@ -150,7 +150,7 @@ For example, if we imagine a library `MyLibrary` that need access to the items a
...
ItemRegistry ir; // <- auto-injected in your script
ThingRegistry things; // <- auto-injected in your script
public void main() {
void main() {
var myUsefulParameter1 = ...
var myUsefulParameter2 = ...
MyLibrary myLib = new MyLibrary();
Expand All @@ -167,7 +167,7 @@ It can even work recursively: a lib can reference another lib, itself referencin
Getting back to our example : As the library instantiation and injection with the items and things registries are taken care of, the same code can then become:

```java
public void main(MyLibrary myLib) { // <- myLib will be instantiated by the bundle, and auto-injected with the openHAB input variables declared in it. (You can also use other injection methods)
void main(MyLibrary myLib) { // <- myLib will be instantiated by the bundle, and auto-injected with the openHAB input variables declared in it. (You can also use other injection methods)
var myUsefulParameter1 = ...
var myUsefulParameter2 = ...
myLib.doSomethingInteresting(myUsefulParameter1, myUsefulParameter2); // <-- myLib already have been instantiated and injected with registries reference, and so we do not need to pass them here
Expand Down Expand Up @@ -232,7 +232,7 @@ public class MyRule extends Java223Script {

@Rule
@ItemStateUpdateTrigger(itemName = Items.my_detector_item, state = EnumStrings.OnOffType.ON)
public void myRule() {
void myRule() {
_items.my_bulb_item.send(ON);
}
}
Expand All @@ -258,7 +258,7 @@ public class MyRule extends Java223Script {
@Rule(name = "detecting.people", description = "Detecting people and light")
@ItemStateUpdateTrigger(itemName = Items.my_detector_item, state = EnumStrings.OnOffType.ON)
@ItemStateUpdateTrigger(itemName = Items.my_otherdetector_item, state = EnumStrings.OnOffType.ON)
public void myRule(ItemStateUpdate inputs) { // <-- HERE, strongly typed parameter
void myRule(ItemStateUpdate inputs) { // <-- HERE, strongly typed parameter
_items.my_bulb_item.send(ON);
logger.info("Movement detected at " + inputs.getItemName()); // inputs.getItemName() gives the triggering detector name
}
Expand Down Expand Up @@ -512,7 +512,7 @@ Tip: to access a remote openHAB installation scripts folder, you can copy, use W
import helper.generated.Java223Script;

public class BasicExample extends Java223Script {
public void main() {
void main() {
_items.myitem().send(ON); // let there be light
}
}
Expand Down Expand Up @@ -546,7 +546,7 @@ public class MyRule extends Java223Script {

@Rule
@ItemStateUpdateTrigger(itemName = Items.my_detector_item, state = EnumStrings.OnOffType.ON)
public void myRule() {
void myRule() {
_items.my_bulb_item.send(OnOffType.ON);
}
}
Expand Down Expand Up @@ -579,7 +579,7 @@ public class M {
ItemRegistry ir;
ScriptThingActions actions;

public Object main() {
Object main() {
automationManager.addRule(new SimpleRule() {
{
name = "abc"; //If not set, openHAB will assign random name
Expand Down Expand Up @@ -629,7 +629,7 @@ actions:
script: >-
public class anything {
org.openhab.core.thing.ThingRegistry things; // from the "default" preset
public Object main(java.util.Map<String, Object> bindings) { // use bindings to get access to all variables
Object main(java.util.Map<String, Object> bindings) { // use bindings to get access to all variables
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger("Bindings");
logger.error("Is things the same as bindings('things')? " + Boolean.valueOf(things == bindings.get("things"))); // true
logger.error("lastStateUpdate as reported by the trigger: " + bindings.get("lastStateUpdate") + " is of " + bindings.get("lastStateUpdate").getClass());
Expand Down Expand Up @@ -664,7 +664,7 @@ public class MyRule extends Java223Script {
@Rule(name = "detecting.people", description = "Detecting people and light")
@ItemStateUpdateTrigger(itemName = Items.my_detector_item, state = EnumStrings.OnOffType.ON)
@ItemStateUpdateTrigger(itemName = Items.my_otherdetector_item, state = EnumStrings.OnOffType.ON)
public void myRule(ItemStateUpdate inputs) { // here, a strongly typed parameter
void myRule(ItemStateUpdate inputs) { // here, a strongly typed parameter
_items.my_bulb_item.send(ON);
logger.info("Movement detected at {}", inputs.getItemName());
}
Expand All @@ -686,7 +686,7 @@ import org.openhab.core.library.types.OnOffType;

public class FieldInjectionExample {
ItemRegistry itemRegistry; // <-- the injection will happen here, 'itemRegistry' is a valid openHAB input name
public void main() {
void main() {
((SwitchItem)itemRegistry).get("myitem").send(OnOffType.ON);
}
}
Expand All @@ -701,7 +701,7 @@ import org.openhab.core.items.ItemRegistry;
import static org.openhab.core.library.types.OnOffType.ON;

public class MethodInjectionExample {
public void main(ItemRegistry itemRegistry) { // <-- the injection will happen here, 'itemRegistry' is a valid openHAB input name
void main(ItemRegistry itemRegistry) { // <-- the injection will happen here, 'itemRegistry' is a valid openHAB input name
try {
((org.openhab.core.library.items.SwitchItem)itemRegistry.getItem("myitem")).send(ON);
} catch (org.openhab.core.items.ItemNotFoundException e) {}
Expand All @@ -723,11 +723,11 @@ public class ConstructorInjectionExample {
// Alternatively, by using @InjectBinding(enable=false) you can prevent the Java223 bundle from even trying to inject an openHAB value.
ItemRegistry myItemRegistry;

public ConstructorInjectionExample(ItemRegistry itemRegistry) { // <-- the injection will happen here, 'itemRegistry' is a valid openHAB input name
ConstructorInjectionExample(ItemRegistry itemRegistry) { // <-- the injection will happen here, 'itemRegistry' is a valid openHAB input name
this.myItemRegistry = itemRegistry;
}

public void main() {
void main() {
((SwitchItem)myItemRegistry.get("myitem")).send(OnOffType.ON);
}
}
Expand All @@ -750,7 +750,7 @@ import java.util.Map;
import org.openhab.core.automation.RuleManager;

public class RunAnotherRule {
public void main(RuleManager ruleManager) { // <-- Injection by the constructor. RuleManager is an OSGi service and so is available as a candidate for injection
void main(RuleManager ruleManager) { // <-- Injection by the constructor. RuleManager is an OSGi service and so is available as a candidate for injection
// simple execution :
ruleManager.runNow("myruleid");
// execution with parameters in a key / value map :
Expand All @@ -766,7 +766,7 @@ As with the RuleManager, you first inject it (or use the inherited field from Ja

```java
public class DisableThing extends helper.generated.Java223Script { // <-- Java223Script already has a thingManager field
public void main() {
void main() {
thingManager.setEnabled(_things.network_pingdevice_mything().getUID(), false);
}
}
Expand Down Expand Up @@ -813,7 +813,7 @@ public class InjectBindingExample {
// make it mandatory (the script will not run if the value cannot be found). Note: mandatory = true is the default value when using the annotation.
protected @InjectBinding(mandatory = true) ThingRegistry things;

public void main(ItemRegistry itemRegistry) {
void main(ItemRegistry itemRegistry) {
myItemRegistry.get("myitem");
}
}
Expand All @@ -831,7 +831,7 @@ import org.openhab.core.items.ItemRegistry;
public class MyGreatLibrary {
ItemRegistry itemRegistry; // will be auto-injected if instantiation is taken care of by the bundle

public void myUsefullLibraryMethod(String itemName) {
void myUsefullLibraryMethod(String itemName) {
itemRegistry.get(itemName);
//something usefull...
}
Expand All @@ -846,7 +846,7 @@ public class MyScript {

MyGreatLibrary mylib; // will be auto-instantiated and then auto-injected with all necessary openHAB input value

public void exec() {
void exec() {
mylib.myUsefullLibraryMethod("myitemName");
}
}
Expand All @@ -866,7 +866,7 @@ import helper.generated.Java223Script;
public class ItemsAndThingAccessExample extends Java223Script { // <-- take the Java223Script class as a base class
// to access _items and _things more easily

public void exec() {
void exec() {
_items.myLightItem().send(ON); // <-- light on !
logger.info(_things.zwave_device_2ecfa3a2_node68().getStatus().toString()); // <-- get thing info
}
Expand All @@ -886,7 +886,7 @@ import helper.generated.Things;
public class ActionExample extends Java223Script { // <-- take the Java223Script class as a base class
// to access _actions more easily

public void exec() {
void exec() {
_actions.getSmsmodem_SMSModemActions(Things.mySMSthing).sendSMS("+3312345678", "Hello world");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -137,9 +136,19 @@ private Object construct(Class<?> compiledClass, Map<String, Object> bindings) {

// create real instance from compiled class
// use the empty constructor if available, or the first one otherwise
Constructor<?>[] constructors = compiledClass.getDeclaredConstructors();
Constructor<?> constructor = Arrays.stream(constructors).filter(c -> c.getParameterCount() == 0).findFirst()
.orElseGet(() -> constructors[0]);
final boolean unnamedPackage = compiledClass.getPackageName().isEmpty();
Constructor<?> constructor;
try {
constructor = unnamedPackage ? compiledClass.getDeclaredConstructor() : compiledClass.getConstructor();
} catch (NoSuchMethodException e) {
Constructor<?>[] ctors = unnamedPackage ? compiledClass.getDeclaredConstructors()
: compiledClass.getConstructors();
if (ctors.length == 0)
throw new Java223Exception("No public constructor in " + compiledClass.getCanonicalName());
constructor = ctors[0];
}
if (unnamedPackage)
constructor.trySetAccessible();

try {
ClassLoader classLoader = compiledClass.getClassLoader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,17 @@ public Java223CompiledScript compile(@Nullable String originalScript) throws Scr
var localArgs = args == null ? new Object[0] : args;
Class<?>[] argClasses = Arrays.stream(localArgs).map(Object::getClass).toArray(Class[]::new);

Method method = null;
method = compiledScript.getCompiledClass().getMethod(name, argClasses);
Method method;
if (compiledScript.getCompiledClass().getPackageName().isEmpty())
try {
method = compiledScript.getCompiledClass().getDeclaredMethod(name, argClasses);
method.trySetAccessible();
} catch (NoSuchMethodException e) {
method = compiledScript.getCompiledClass().getMethod(name, argClasses);
}
else
method = compiledScript.getCompiledClass().getMethod(name, argClasses);

try {
if (Modifier.isStatic(method.getModifiers())) {
return method.invoke(new Object(), localArgs); // new object() required (but value ignored) to avoid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,16 @@ public void associateBindings(@Nullable Class<?> compiledClass, @Nullable Object
// find methods to execute
// noinspection OptionalAssignedToNull
Optional<Object> returned = null;
for (Method method : instance.getClass().getMethods()) {
final boolean unnamedPackage = instance.getClass().getPackageName().isEmpty();
for (Method method : unnamedPackage ? instance.getClass().getDeclaredMethods()
: instance.getClass().getMethods()) {
// methods with a special name, or methods with a special annotation
if (METHOD_NAMES_TO_EXECUTE.contains(method.getName()) || method.getAnnotation(RunScript.class) != null) {
try {
Object[] parameterValues = BindingInjector.getParameterValuesFor(classLoader, method, bindings,
null);
if (unnamedPackage)
method.trySetAccessible();
var returnedLocal = method.invoke(instance, parameterValues);
// keep arbitrarily only the first returned value
// comparing this optional to null is OK. Null value means no method was yet executed.
Expand Down