From 4afe63f4416f835be57a198548d9336fe9fd7b6e Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 15 Aug 2017 09:17:56 -0700 Subject: [PATCH 1/2] Docs --- README.md | 10 ++-- docs/DeveloperGuide.md | 43 ++++++-------- .../main/java/org/iot/dsa/dslink/DSLink.java | 17 ++++-- .../java/org/iot/dsa/dslink/DSLinkConfig.java | 56 ++----------------- .../org/iot/dsa/dslink/DSLinkSession.java | 2 +- .../org/iot/dsa/dslink/InvokeWrapper.java | 2 +- .../org/iot/dsa/dslink/ListSubscriber.java | 3 +- .../java/org/iot/dsa/dslink/ListWrapper.java | 2 +- .../java/org/iot/dsa/dslink/SetWrapper.java | 2 +- .../org/iot/dsa/dslink/SubscribeWrapper.java | 2 +- .../org/iot/dsa/dslink/ValueSubscriber.java | 2 +- dslink-java-template/README.md | 35 +++++++----- dslink-java-template/dslink.json | 34 +++++------ .../org/iot/dsa/dslink/template/Main.java | 4 +- 14 files changed, 88 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index b7460a12..1c900035 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,15 @@ ## Overview -This repository contains a Java SDK for creating [DSA](http://iot-dsa.org) links. -learn about the DSA architecture, please visit [this page](http://iot-dsa.org/get-started/how-dsa-works). +This repository contains a Java SDK for creating [DSA](http://iot-dsa.org) links. to learn about +the DSA architecture, please visit [this page](http://iot-dsa.org/get-started/how-dsa-works). -## Contents +## Modules - **/dslink-core** - The APIs used to build new links. - - **/dslink-link-template** - Can be copied and modified to create a new dslink. + - **/dslink-java-template** - Can be copied to create a new dslink. - **/dslink-websocket-standalone** - Used by links that run in their own process rather - than in a container which provides it's own websocket implementation. + than in a container which provides it's a different websocket implementation. ## Link Development diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 070e164b..5b8d25bd 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,41 +1,34 @@ # DSLink Java Developer Guide -[Home](https://github.com/iot-dsa-v2/dslink-java) - [Javadoc](https://iot-dsa-v2.github.io/dslink-java/javadoc/) +[Home](https://github.com/iot-dsa-v2/sdk-dslink-java) - [Javadoc](https://iot-dsa-v2.github.io/sdk-dslink-java/javadoc/) + +## Warning + +Only use org.iot.dsa APIs, do not use or depend on anything in the com.* packages. + +## Overview The purpose of this document is to guide the reader through the development of DSA links. -Developers will build links using the org.iot.dsa.* packages found in -dslink-java/modules/core/dslink-core. +Developers will build links using the org.iot.dsa.* packages found in sdk-dslink-java/dslink-core. -Key objectives of the SDK: +Key objectives of this SDK: - Pluggable architecture: - Protocol independence. Primarily to support DSA V1 and V2. - Transport independence. Websockets, plain sockets, http, and whatever else comes along. - Support multiple links in the same process. - - Support JDK 1.6 for Distech. - - High performance for activities such as video streaming. - - 3rd party library independence. Some environments provide transport libraries while others - do not. SLF4J and Netty were explicitly bound to the original SDK but can not be used in Niagara. - -## Warning - -Only use org.iot.dsa APIs, do not use or depend on anything in the com.* packages. - -## Overview + - Support JDK 1.6 for Distech Controls. + - High performance for video streaming. + - 3rd party library independence. Some environments such as Niagara provide transport libraries + while others do not. SLF4J and Netty were explicitly bound to the original SDK but can not be + used in Niagara because of it's strict Security Manager. -Distributed Services Architecture (DSA), is an open source IoT platform that facilitates device -inter-communication, logic and applications at every layer of the Internet of Things -infrastructure. The objective is to unify the disparate devices, services and applications -into a structured and adaptable real-time data model. The premise of the open source DSA -initiative is to build a community of manufacturers, makers and solution providers that will -contribute to an ever-expanding library of Distributed Service Links which allow protocol -translation and data integration to and from 3rd party data-sources. +There are two ways to create a link. -From the developer's perspective a link is composed of the following: +1. The easy way. Create nodes and use values defined in org.iot.dsa.node. You will get +free persistence and DSA protocol conversion. - - The root DSLink object. - - The connection to the upstream broker. - - A node tree representing functionality such a protocol drivers and applications. +2. Implement org.iot.dsa.dslink.DSResponder ## Link Structure diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLink.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLink.java index 17b49b18..8cd51c29 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLink.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLink.java @@ -17,9 +17,18 @@ import org.iot.dsa.util.DSException; /** - * Represents an upstream connection, a node hierarchy, and manages the lifecycle of both.

Links - * are created with DSLinkConfig object. The main method of the process is responsible for creating - * the config. After instantiation, the link should call DSLink.start()

Lifecycle: TODO + * Represents an upstream connection, a node hierarchy, and manages the lifecycle of both. + * + *

+ * + * Links are created with DSLinkConfig object. The main method of the process is responsible for + * creating the config. After instantiation, the link should call DSLink.run() + * + *

+ * + * Lifecycle: + * + * TODO * * @author Aaron Hansen */ @@ -80,7 +89,7 @@ public DSLink(DSLinkConfig config) throws Exception { throw new IllegalStateException("Config missing the root node type"); } config(config() ? "Root type: " + type : null); - DSNode tmp = (DSNode) Class.forName(type).newInstance(); + DSNode tmp = (DSNode) Class.forName(type).newInstance(); if (!(tmp instanceof DSResponder)) { throw new IllegalStateException("Root type not a responder: " + type); } diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkConfig.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkConfig.java index ae61e056..bc98cd6e 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkConfig.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkConfig.java @@ -27,7 +27,6 @@ public class DSLinkConfig { public static final String CFG_LOG_LEVEL = "log"; public static final String CFG_NODE_FILE = "nodes"; public static final String CFG_READ_TIMEOUT = "readTimeout"; - public static final String CFG_ROOT_NAME = "rootName"; public static final String CFG_ROOT_TYPE = "rootType"; public static final String CFG_STABLE_DELAY = "stableDelay"; public static final String CFG_TRANSPORT_FACTORY = "transportFactory"; @@ -45,7 +44,6 @@ public class DSLinkConfig { private File logFile; private Level logLevel; private File nodesFile; - private String rootName; private String rootType; private String token; private File workingDir = new File("."); @@ -263,22 +261,15 @@ public File getNodesFile() { return nodesFile; } - /** - * The name of the root node. - */ - public String getRootName() { - if (rootName == null) { - setRootName(getConfig(CFG_ROOT_NAME, null)); - } - return rootName; - } - /** * The type of the root node. */ public String getRootType() { if (rootType == null) { - setRootType(getConfig(CFG_ROOT_TYPE, null)); + rootType = getConfig(CFG_ROOT_TYPE, null); + if (rootType == null) { + throw new IllegalStateException("Missing rootType config."); + } } return rootType; } @@ -343,10 +334,6 @@ private void processParsed(String key, String value) { setLinkName(value); } else if (key.equals("--nodes")) { setNodesFile(new File(value)); - } else if (key.equals("--rootName")) { - setRootName(value); - } else if (key.equals("--rootType")) { - setRootType(value); } else if (key.equals("--token")) { setToken(value); } else { @@ -503,29 +490,6 @@ public DSLinkConfig setNodesFile(File file) { return setConfig(CFG_NODE_FILE, file.getAbsolutePath()); } - /** - * Overrides dslink.json. - */ - public DSLinkConfig setRootType(Class arg) { - return setRootType(arg.getName()); - } - - /** - * Overrides dslink.json. - */ - public DSLinkConfig setRootName(String arg) { - rootName = arg; - return setConfig(CFG_ROOT_NAME, arg); - } - - /** - * Overrides dslink.json. - */ - public DSLinkConfig setRootType(String arg) { - rootType = arg; - return setConfig(CFG_ROOT_TYPE, arg); - } - /** * Overrides dslink.json. */ @@ -533,18 +497,6 @@ public DSLinkConfig setToken(String arg) { return setConfig(CFG_AUTH_TOKEN, arg); } - /** - * Validates that the configuration represents a viable link startup state. This only needs to - * be called if the public no-arg constructor is used. - * - * @throws IllegalStateException whose message will describe the problem. - */ - public void validate() { - if (getBrokerUri() == null) { - throw new IllegalStateException("Missing broker uri."); - } - } - /** * Whether or not -h or --help was provided. */ diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkSession.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkSession.java index 4a4c483f..245a047c 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkSession.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkSession.java @@ -19,4 +19,4 @@ public interface DSLinkSession { */ public boolean shouldEndMessage(); -} //class +} diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java index 1862d72d..63f7f06b 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java @@ -6,7 +6,7 @@ import org.iot.dsa.security.DSPermission; /** - * Maps a request path into a node tree. + * Used by DSRootNode to handle an invoke request. * * @author Aaron Hansen */ diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java index a44af1d2..eed921e8 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java @@ -10,7 +10,8 @@ import org.iot.dsa.node.DSNode; /** - * Subscribes to the target of a list request and updates the stream when changes are detected. + * Used by DSRootNode to subscribe to the target of a list request and update the stream when + * changes are detected. * * @author Aaron Hansen */ diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java index 541f1acc..81715f4b 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java @@ -4,7 +4,7 @@ import org.iot.dsa.dslink.responder.InboundListRequest; /** - * Maps a request path into a node tree. + * Used by DSRootNode to handle and list request. * * @author Aaron Hansen */ diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java index 501067c7..4f0e20a0 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java @@ -5,7 +5,7 @@ import org.iot.dsa.security.DSPermission; /** - * Maps a request path into a node tree. + * Used by DSRootNode to handle a set request. * * @author Aaron Hansen */ diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java index 21de3402..5cfa1946 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java @@ -5,7 +5,7 @@ import org.iot.dsa.node.DSQuality; /** - * Maps a request path into a node tree. + * Used by DSRootNode to handle subscribe requests. * * @author Aaron Hansen */ diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java index d80ed2e0..01b68528 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java @@ -14,7 +14,7 @@ import org.iot.dsa.node.DSString; /** - * Maps a request path into a node tree. + * Used by DSRootNode to handle value subscriptions. * * @author Aaron Hansen */ diff --git a/dslink-java-template/README.md b/dslink-java-template/README.md index 984bf0db..ad2f38a1 100644 --- a/dslink-java-template/README.md +++ b/dslink-java-template/README.md @@ -1,34 +1,43 @@ # dslink-java-template * Version: 0.0.0 -* JDK 1.6 Compatibility is required. -* [Developer Guide](https://iot-dsa-v2.github.io/dslink-java/DeveloperGuide) -* [Javadoc](https://iot-dsa-v2.github.io/dslink-java/javadoc/) +* JDK 1.6 and up. * [License](https://en.wikipedia.org/wiki/ISC_license) ## Overview This project can be used to create a new link. In a nutshell, copy it, change -org.iot.dsa.dlink.template to your package structure and globally replace 'template' with you link -name +org.iot.dsa.dlink.template to your package structure and globally replace 'template' with your link +name. + +## Files Requiring Modification +1. build.gradle + - Group - Your organization's identifier. + - Version - major.minor.patch[-pre-release] + - mainClassName - You will probably use the main class, but your package will be + different. + - dependencies - Replace the sdk-dslink-java project dependencies with the commented out + versions on the following line. +2. dslink.json + - TODO +3. README.md + - Please maintain a readme with a version history. +4. Main.java + - Be sure to change the directory structure to match your package. -## Repository Contents - - - **src** - - - **build.gradle** - - - **dslink.json** - - ## Link Development Please see the [developer guide](https://iot-dsa-v2.github.io/sdk-dslink-java/DeveloperGuide) and -then the [Javadoc](https://iot-dsa-v2.github.io/sdk-dslink-java/javadoc/). +the [Javadoc](https://iot-dsa-v2.github.io/sdk-dslink-java/javadoc/) for the core SDK. ## Acknowledgements +Attributions belong here. + ## History -* Version 0.1.0 +* Version 0.0.0 - Hello World diff --git a/dslink-java-template/dslink.json b/dslink-java-template/dslink.json index 5ce80ad8..b6fb1e8c 100644 --- a/dslink-java-template/dslink.json +++ b/dslink-java-template/dslink.json @@ -10,10 +10,10 @@ "main": "bin/dslink-java-template", "repository": { "type": "git", - "url": "https://github.com/IOT-DSA/dslink-java" + "url": "https://github.com/IOT-DSA/sdk-dslink-java" }, "bugs": { - "url": "https://github.com/IOT-DSA/dslink-java/issues" + "url": "https://github.com/IOT-DSA/sdk-dslink-java/issues" }, "configs": { "section1": { @@ -22,27 +22,15 @@ "value": null }, "broker": { - "description": "URL to broker.", + "description": "URL to broker. Can be specified on the command line.", "type": "url", "value": null, "required": true }, - "key": { - "description": "Path to public/private key pair.", - "type": "path", - "value": ".key", - "required": true - }, - "nodes": { - "description": "Path to configuration database.", - "type": "path", - "value": "nodes.json", - "required": true - }, "rootType": { - "description": "Type of the root node of the link.", + "description": "Type of the root node of the link. Must be specified here.", "type": "string", - "value": "org.iot.dsa.node.DSNode", + "value": "org.iot.dsa.dslink.template.Main", "required": true }, "section2": { @@ -56,6 +44,12 @@ "value": null, "required": false }, + "key": { + "description": "Path to public/private key pair.", + "type": "path", + "value": ".key", + "required": true + }, "log": { "description": "finest, finer, fine, config, info, warning or severe", "type": "string", @@ -68,6 +62,12 @@ "value": null, "required": false }, + "nodes": { + "description": "Path to configuration database.", + "type": "path", + "value": "nodes.json", + "required": true + }, "readTimeout": { "description": "Millis timeout for reading from the connection.", "type": "number", diff --git a/dslink-java-template/src/main/java/org/iot/dsa/dslink/template/Main.java b/dslink-java-template/src/main/java/org/iot/dsa/dslink/template/Main.java index c63aa5f1..b4a1c223 100644 --- a/dslink-java-template/src/main/java/org/iot/dsa/dslink/template/Main.java +++ b/dslink-java-template/src/main/java/org/iot/dsa/dslink/template/Main.java @@ -53,9 +53,7 @@ protected void declareDefaults() { * Launch the link. */ public static void main(String[] args) throws Exception { - DSLinkConfig cfg = new DSLinkConfig(args) - .setRootName("Test") - .setRootType(Main.class); + DSLinkConfig cfg = new DSLinkConfig(args); DSLink link = new DSLink(cfg); link.run(); } From 362d313e3de8f476a934baec2b8a2741d9f458f8 Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 15 Aug 2017 16:34:26 -0700 Subject: [PATCH 2/2] Docs --- .gitignore | 1 + docs/DeveloperGuide.md | 39 +++++++++++++++---- .../java/org/iot/dsa/dslink/DSRootNode.java | 6 ++- .../main/java/org/iot/dsa/node/DSNode.java | 14 +++++++ dslink-java-template/README.md | 2 +- dslink-java-template/build.gradle | 2 +- .../org/iot/dsa/dslink/template/Main.java | 3 ++ 7 files changed, 57 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index a0161463..a63d8410 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ **/.key **/db **/nodes.json +**/nodes*.zip **/test.json diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 5b8d25bd..22aa0b31 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -8,8 +8,9 @@ Only use org.iot.dsa APIs, do not use or depend on anything in the com.* package ## Overview -The purpose of this document is to guide the reader through the development of DSA links. -Developers will build links using the org.iot.dsa.* packages found in sdk-dslink-java/dslink-core. +The purpose of this document is to guide the reader through the development of Java DSA links using +this SDK. Developers will build links using the org.iot.dsa.* packages found in +sdk-dslink-java/dslink-core. Key objectives of this SDK: @@ -23,15 +24,39 @@ Key objectives of this SDK: while others do not. SLF4J and Netty were explicitly bound to the original SDK but can not be used in Niagara because of it's strict Security Manager. -There are two ways to create a link. +There are three ways to create a link. -1. The easy way. Create nodes and use values defined in org.iot.dsa.node. You will get -free persistence and DSA protocol conversion. +1. The easy way. Use nodes and values defined in org.iot.dsa.node. You will get +free configuration persistence and DSA protocol translation. -2. Implement org.iot.dsa.dslink.DSResponder +2. The hard way. Implement your own org.iot.dsa.dslink.DSResponder. -## Link Structure +3. A combination of 1 and 2. +## Creating Links the Easy Way + +1. Boiler plate. Copy the dslink-java-template module from this repo. It has everything needed +to create a standalone link. It's README talks about what needs to be modified. + +2. Create a root node. This node should subclass org.iot.dsa.dslink.DSRootNode. The main class +in the template module already does this. + +3. Create nodes and values specific to your link's functionality. + +### Project Boiler Plate + +The dslink-java-template subproject in this repository can be copied to make a standalone link +repository. + +### Create a Root Node + +The root node type is specified in dslink.json + +### Creating Nodes and Values + +## Link + +This package defines a data model that supports persistent There are a few key classes / interfaces that define the general structure of a link and the SDK. - Link (org.iot.dsa.dslink.DSLink) diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSRootNode.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSRootNode.java index 8281bcc0..8894239d 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSRootNode.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/DSRootNode.java @@ -80,6 +80,10 @@ public void onSet(InboundSetRequest request) { } DSNode parent = path.getParent(); DSInfo info = path.getInfo(); + if (info.isReadOnly()) { + throw new DSRequestException("Not writable: " + getPath()); + } + //TODO verify incoming permission DSIValue value = info.getValue(); if (value == null) { if (info.getDefaultObject() instanceof DSIValue) { @@ -93,7 +97,7 @@ public void onSet(InboundSetRequest request) { } else { throw new DSRequestException("Cannot decode value: " + parent.getPath()); } - parent.put(info.getName(), value); + parent.onSet(info, value); } /////////////////////////////////////////////////////////////////////////// diff --git a/dslink-core/src/main/java/org/iot/dsa/node/DSNode.java b/dslink-core/src/main/java/org/iot/dsa/node/DSNode.java index 8062e4f8..e218e004 100644 --- a/dslink-core/src/main/java/org/iot/dsa/node/DSNode.java +++ b/dslink-core/src/main/java/org/iot/dsa/node/DSNode.java @@ -8,6 +8,7 @@ import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; +import org.iot.dsa.dslink.responder.InboundSetRequest; import org.iot.dsa.logging.DSLogger; import org.iot.dsa.logging.DSLogging; import org.iot.dsa.node.action.ActionInvocation; @@ -562,6 +563,19 @@ public ActionResult onInvoke(DSInfo actionInfo, ActionInvocation invocation) { throw new IllegalStateException("onInvoke not overridden"); } + /** + * Override point, called when a value child is being set by the responder. The default + * implementation calls put(info, value). If you throw an exception, an error will be + * reported to the requester. + * + * @param info The child being changed. + * @param value The new value. + * @see org.iot.dsa.dslink.DSResponder#onSet(InboundSetRequest) + */ + public void onSet(DSInfo info, DSIValue value) { + put(info, value); + } + /** * Called when this container transitions from unsubscribed to subscribed, but is not called for * subsequent subscribers. diff --git a/dslink-java-template/README.md b/dslink-java-template/README.md index ad2f38a1..21bedb34 100644 --- a/dslink-java-template/README.md +++ b/dslink-java-template/README.md @@ -24,7 +24,7 @@ name. 3. README.md - Please maintain a readme with a version history. 4. Main.java - - Be sure to change the directory structure to match your package. + - Also be sure to change the directory structure to match your package. ## Link Development diff --git a/dslink-java-template/build.gradle b/dslink-java-template/build.gradle index db1e4e13..5e79fcfc 100644 --- a/dslink-java-template/build.gradle +++ b/dslink-java-template/build.gradle @@ -18,7 +18,7 @@ repositories { } dependencies { - compile project (':dslink-core') //replace this with the next linke + compile project (':dslink-core') //replace this with the next line //compile 'org.iot.dsa:dslink-core:+' compile project (':dslink-websocket-standalone') //replace this with the next line //compile 'org.iot.dsa:dslink-websocket-standalone:+' diff --git a/dslink-java-template/src/main/java/org/iot/dsa/dslink/template/Main.java b/dslink-java-template/src/main/java/org/iot/dsa/dslink/template/Main.java index b4a1c223..b65e0b31 100644 --- a/dslink-java-template/src/main/java/org/iot/dsa/dslink/template/Main.java +++ b/dslink-java-template/src/main/java/org/iot/dsa/dslink/template/Main.java @@ -39,6 +39,9 @@ public class Main extends DSRootNode implements Runnable { // Methods /////////////////////////////////////////////////////////////////////////// + public void actionInvoked(ActionInvocation invoke) { + } + @Override protected void declareDefaults() { declareDefault("First", DSInt.valueOf(1)).setReadOnly(true);