Reputation: 1
I'm developing a custom rule node for ThingsBoard, and I need to configure various Kafka-related parameters through thingsboard.yml file. Instead of directly using @Value annotations, I want to read these values into a separate configuration class and then use its object in my custom rule node.
thingsboard.yml
custom:
node.enabled: true
config:
bootstrap.server: localhost:9092
topic.pattern: custom-*
retries: 3
batch.size: 16384
linger.ms: 0
buffer.memory: 33554432
CustomNodeConfig.java
package com.example;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
@Data
@Configuration
public class CustomNodeConfig {
@Value("${custom.node.enabled}")
private boolean enabled;
@Value("${custom.config.bootstrap.server}")
private String bootstrapServers;
@Value("${custom.config.topic.pattern}")
private String topicPattern;
@Value("${custom.config.retries}")
private int retries;
@Value("${custom.config.batch.size}")
private int batchSize;
@Value("${custom.config.linger.ms}")
private int linger;
@Value("${custom.config.buffer.memory}")
private int bufferMemory;
CustomNode.java
package com.example;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.*;
import javax.annotation.PostConstruct;
@Slf4j
@Data
@RuleNode(
type = ComponentType.EXTERNAL,
name = "Custom Node",
configClazz = CustomRuleNodeConfiguration.class,
nodeDescription = "Publish messages to Kafka server",
nodeDetails = "Outbound message will contain response fields from Kafka in the Message Metadata.",
uiResources = {"static/rulenode/rulenode-core-config.js"}
)
@Component
public class CustomNode implements TbNode {
@Autowired
private CustomNodeConfig kafkaConfig;
@PostConstruct
public void init() {
log.info("CustomRuleNode initialized with retries: {}, lingerMs: {}, bootstrapServers: {}, topicPattern: {}",
kafkaConfig.getRetries(), kafkaConfig.getLingerMs(), kafkaConfig.getBootstrapServers(), kafkaConfig.getTopicPattern());
}
@Override
public void init(TbNodeConfiguration configuration) throws TbNodeException
log.info("CustomRuleNode configuration initialized");
}
@Override
protected void onMsg(TbContext ctx, TbMsg msg) {
log.info("Received message: {}", msg.getData());
}
@Override
public void destroy() {
log.info("CustomRuleNode destroyed!");
}
}
The above implementation did not work as expected, the node actor does not get initialized and throws the below exception after attempting 10 retries, as the Autowired variable is not initialized.
2024-07-04 11:36:03.060 INFO 356309 --- [-dispatcher-3-3] o.t.server.actors.TbActorMailbox : [RULE_NODE|8ad08a10-39f8-11ef-8131-5d958a56f0d4] Failed to init actor, attempt 10, going to stop attempts.
org.thingsboard.server.actors.TbActorException: Failed to init actor
at org.thingsboard.server.actors.service.ComponentActor.initProcessor(ComponentActor.java:72) ~[classes!/:3.5.1]
at org.thingsboard.server.actors.service.ComponentActor.init(ComponentActor.java:57) ~[classes!/:3.5.1]
at org.thingsboard.server.actors.TbActorMailbox.tryInit(TbActorMailbox.java:66) ~[actor-3.5.1.jar!/:3.5.1]
at org.thingsboard.server.actors.TbActorMailbox.lambda$tryInit$1(TbActorMailbox.java:85) ~[actor-3.5.1.jar!/:3.5.1]
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426) ~[na:na]
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) ~[na:na]
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) ~[na:na]
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) ~[na:na]
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) ~[na:na]
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183) ~[na:na]
Caused by: java.lang.NullPointerException: null
at org.thingsboard.rule.engine.custom.CustomNode.init(CustomNode.java:92) ~[rule-engine-components-3.5.1.jar!/:3.5.1]
at org.thingsboard.server.actors.ruleChain.RuleNodeActorMessageProcessor.initComponent(RuleNodeActorMessageProcessor.java:173) ~[classes!/:3.5.1]
at org.thingsboard.server.actors.ruleChain.RuleNodeActorMessageProcessor.start(RuleNodeActorMessageProcessor.java:64) ~[classes!/:3.5.1]
at org.thingsboard.server.actors.service.ComponentActor.initProcessor(ComponentActor.java:63) ~[classes!/:3.5.1]
... 9 common frames omitted
Questions
Upvotes: 0
Views: 75
Reputation: 1
I don't think you can get access to the Spring application context from a TbNode object as the whole rule chain execution is sandboxed and all these objects are not instantiated by Spring. The best you could do, I think, is add your parameter properties in the configuration class CustomRuleNodeConfiguration
and fill in the values from the rule chain editor.
Upvotes: 0