Own classes for use in rules

For the new mail binding I need to define a new class MailBuilder. I put that in a package org.openhab.binding.mail.builder and added that package to Export in MANIFEST.MF. From my understanding other bundles should now be able to use that.

In a .rules-file I used import org.openhab.binding.mail.builder.MailBuilder and tried val mail = new MailBuilder(”Test“). That results in an error null.

The same works perfectly for java.util.Date.

What am I missing?

I can’t answer your specific question but if this is something that is exposed in Rules, shouldn’t it be an Action? You can look at how MQTT2 built its mqttPublish Action. From the rule the user doesn’t import the class like in the old 1.x way, but has a system call to get a handle for the Action.

e. g.

val mqttActions = getActions("mqtt","mqtt:systemBroker:embedded-mqtt-broker")
mqttActions.publishMQTT("mytopic","myvalue")

This is how Actions are required to be accesses in NGRE I believe. Other bindings have implemented Actions like this too; i think Hue is one of them.

The actions are easy and work fine. But a builder for creating the mail was requested, so you can do something like

val mail = new MailBuilder("foo@bar.zinga")
mail.withSubject("foo").withContent(htmlcontent)
mailActions.sendMail(mail.build)

I need to import the MailBuilder.

@J-N-K, I’m working on creating a ConditionType and ActionType. I think what you may be missing are ConfigDescriptionParameters, but that may be what you’re doing with your builder. Have you pushed your code to a repo?

I also across this document that may be useful… https://github.com/openhab/openhab-core/blob/master/bundles/org.openhab.core.automation/README.md.

I also found this very helpful… https://github.com/eclipse/smarthome/tree/master/bundles/automation/org.eclipse.smarthome.automation.sample.extension.java/src/main/java/org/eclipse/smarthome/automation/sample/extension/java/internal.

Code is here.

What I want to do is:

import org.openhab.binding.mail.builder.MailBuilder
import org.apache.commons.mail.Email
	
rule "mailtest"
when
	Time cron "30 19 17 * * ?"
then
    val mailAction = getActions("mail", "mail:smtp:087d5265")
    val mail = new MailBuilder("foo@bar.zinga")
    mail.withSubject("Test Subject").withText("test text")
    mailAction.sendMail(mail.build)
end

The builder will be extended with withAttachment, withHtmlContent and probably others. MailBuilder.build returns a Email (sendMail action currently accepts String and build an Email to test the other components). The rule fails on new MailBuilder with error during execution: null. Since the MailBuilder itself works fine inside java, I guess it is not properly imported to the rule.

val date = new Date() works fine if I import java.util.Date, so the problem seems to be that the rule engine is not able to import the class.

I would be really surprised if the maintainers accept a binding with actions that can’t be used in the new rule engine. Also, IMO using the builder in a rule will overly complicates things. I suggest you recreate the functionality of the old action. In Paper UI, it would look something like this…

Just trying to give some feedback!

The builder was not my idea but requested by a maintainer…

If it’s supposed to be an updated 1.x action, e.g. the Pushover action uses a builder class, check how it’s done there:

Unfortunately that is not working for OH2 addons. I tried to mimic that, but it fails at the same point: I can’t resolve the MailBuilder class.

IIRC you have to go for a ThingAction. Similar to the MQTT binding. Add a static method to retrieve a MailBuilder instance. To send the mail the builder should maybe provide a send() method instead of a build() method.

I did that, but it doesn’t work (have a look at my latest code). The mailBuilder-method should return a MailBuilder but it doesn’t.

import org.openhab.binding.mail.builder.MailBuilder

rule "mailtest"
when
	Time cron "00 32 15 * * ?"
then
	val mailAction = getActions("mail", "mail:smtp:087d5265")
	mailAction.sendMail(mailAction.mailBuilder("jan@janessa.me").withSubject("Test"))
end

results in

2019-03-03 15:31:17.651 [INFO ] [.e.s.m.c.i.ModelRepositoryImpl:287  ] - Validation issues found in configuration model 'test.rules', using it anyway:
The method sendMail(ThingActions, Object) from the type SendMailAction refers to the missing type Object
The method mailBuilder(ThingActions, String) from the type SendMailAction refers to the missing type Object
2019-03-03 15:32:00.084 [ERROR] [.m.r.r.i.engine.ExecuteRuleJob:69   ] - Error during the execution of rule 'mailtest': 'withSubject' is not a member of 'Object'; line 26, column 25, length 60

If I remove the .withSubject-part, the validation issues are still there, and it fails with

2019-03-03 15:34:00.031 [ERROR] [.m.r.r.i.engine.ExecuteRuleJob:69   ] - Error during the execution of rule 'mailtest': An error occurred during the script execution: Cannot resolve proxy: java:/Objects/org.openhab.binding.mail.builder.MailBuilder#org.openhab.binding.mail.builder.MailBuilder

So the whole issue really is that the new class cannot be resolved. If I just use String (e.g. for the recipient and use the builder inside the ThingAction-method), the mail is sending out perfectly fine.

Short feedback: I am missing the annotations for the mailBuilder method in your code. And my proposal was to add the send() method to the MailBuilder class. Do not introduce a second action for it. In other words: Get rid of the sendMail action. I will take a closer look into your code tomorrow.

I adjusted the code so that the rule is now:

	val mailAction = getActions("mail", "mail:smtp:087d5265")
	mailAction.mailBuilder("jan@janessa.me").send

the result is the same:

Error during the execution of rule 'mailtest': 'send' is not a member of 'Object'; line 26, 

Hi Jan,

I did some research and found this piece of documentation. But I am afraid that it tells us the desired builder solution will not work out-of-the-box (see ActionOutput annotation). We probably have to extend the framework for using own classes in rules.

Usually the type should be the fully qualified Java type, but in the future it will be extented to support further types.

On the other hand there is a reference to some examples in the magic bundle.

I did try returning a HashMap, casting to the new class and so on. But it does not work out for me. Do you think we should proceed with the “classic” actions and come back to the builder when the core has been enhanced?

Too bad.

I think in general it is a bad idea to introduce something new and deprecate it shortly afterwards.

If we cannot use a builder pattern we can try at least to keep it simple and use separate methodd and try to keep the method chaining by returning the ThingActions instance in all methods. Might that work? If not we have to live with it.

For this I would need to store the already set data for the mail in a private member of SendMailAction. Unfortunately getActions is not thread-safe, we always get the same ThingActions (SendMailAction)-instance for a given thing. We might run into trouble if two rules try to send mail via the same server at the same time.

I still do not understand why it is not working. Maybe you have an idea. I tried @ActionOutput(name = "mailBuilder", type = "org.openhab.binding.mail.builder.MailBuilder"), which is a fully qualified java class, which is also exported from the bundle (code updated in GitHUB). My rule is then (without vaildation errors)

import org.openhab.binding.mail.builder.MailBuilder

rule "test2"
when
    Time cron "0 04 17 * * ?"
then
    val mailAction = getActions("mail", "mail:smtp:test")
    val MailBuilder mailBuilder = mailAction.mailBuilder("foo@bar.zinga").get("retval") as MailBuilder
    mailBuilder.send
end

but results in

2019-03-05 17:04:00.045 [ERROR] [.m.r.r.i.engine.ExecuteRuleJob:69   ] - Error during the execution of rule 'test2': Could not cast org.openhab.binding.mail.builder.MailBuilder@30a0673a to void; line 8, column 35, length 69

I don’t understand what ExecuteRuleJob tries to cast to void. Unfortunately I can’t properly debug that since I cannot (or don’t know how to) run core bundles within the same IDE as the addon.

Sry for letting you alone with this problem. I tinkered around a little bit more on my own but to be honest I yet did neither find a proper solution nor the right place in the core framework where to start to implement a fix for it. Very sad.

In the end it looks like we have to switch back to the conventional way.