├── internal ├── logging │ ├── README.md │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── slf4j │ │ └── impl │ │ ├── Level.java │ │ ├── StaticLoggerBinder.java │ │ ├── LoggerFactoryImpl.java │ │ ├── StaticMDCBinder.java │ │ └── StaticMarkerBinder.java ├── README.md └── runtime_shared │ ├── README.md │ └── build.gradle ├── examples ├── README.md ├── dual │ ├── build.gradle │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── org │ │ │ └── dsa │ │ │ └── iot │ │ │ └── dual │ │ │ └── Main.java │ └── dslink.json ├── requester │ ├── build.gradle │ └── dslink.json └── responder │ ├── build.gradle │ ├── src │ └── main │ │ └── java │ │ └── org │ │ └── dsa │ │ └── iot │ │ └── responder │ │ ├── util │ │ └── FutureCloseHandler.java │ │ ├── Main.java │ │ ├── Echo.java │ │ └── Replicator.java │ └── dslink.json ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── sdk ├── broker │ ├── build.gradle.old │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── dsa │ │ └── iot │ │ └── broker │ │ ├── config │ │ ├── broker │ │ │ ├── package-info.java │ │ │ ├── BrokerConfig.java │ │ │ ├── BrokerMemoryConfig.java │ │ │ └── BrokerFileConfig.java │ │ └── Arguments.java │ │ ├── processor │ │ ├── LinkHandler.java │ │ ├── stream │ │ │ ├── Stream.java │ │ │ ├── GenericStream.java │ │ │ └── SubStream.java │ │ ├── Responder.java │ │ └── MessageProcessor.java │ │ ├── utils │ │ ├── RequestGenerator.java │ │ ├── ParsedPath.java │ │ └── Metrics.java │ │ ├── node │ │ ├── BrokerTree.java │ │ └── Downstream.java │ │ └── server │ │ ├── Server.java │ │ └── client │ │ └── ClientManager.java ├── historian │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── dsa │ │ └── iot │ │ └── historian │ │ ├── utils │ │ ├── TimestampRange.java │ │ ├── TimeParser.java │ │ ├── WatchUpdate.java │ │ └── QueryData.java │ │ ├── stats │ │ └── rollup │ │ │ ├── CountRollup.java │ │ │ ├── SumRollup.java │ │ │ ├── LastRollup.java │ │ │ ├── FirstRollup.java │ │ │ ├── AvgRollup.java │ │ │ ├── MaxRollup.java │ │ │ ├── MinRollup.java │ │ │ ├── OrRollup.java │ │ │ ├── AndRollup.java │ │ │ ├── DeltaRollup.java │ │ │ └── Rollup.java │ │ └── database │ │ └── LoggingType.java ├── commons │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── dsa │ │ │ └── iot │ │ │ └── commons │ │ │ └── Container.java │ │ └── test │ │ └── java │ │ └── org │ │ └── dsa │ │ └── iot │ │ └── commons │ │ ├── GuaranteedReceiverTest.java │ │ └── ParameterizedActionTest.java ├── dslink │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── dsa │ │ │ └── iot │ │ │ └── dslink │ │ │ ├── node │ │ │ ├── package-info.java │ │ │ ├── actions │ │ │ │ ├── package-info.java │ │ │ │ ├── table │ │ │ │ │ ├── Insert.java │ │ │ │ │ ├── Replace.java │ │ │ │ │ ├── Modify.java │ │ │ │ │ ├── Row.java │ │ │ │ │ └── BatchRow.java │ │ │ │ ├── ResultType.java │ │ │ │ └── EditorType.java │ │ │ ├── value │ │ │ │ ├── package-info.java │ │ │ │ ├── SubscriptionValue.java │ │ │ │ └── ValuePair.java │ │ │ ├── MetaData.java │ │ │ ├── exceptions │ │ │ │ └── NoSuchPathException.java │ │ │ ├── NodePair.java │ │ │ ├── Writable.java │ │ │ ├── MessageGenerator.java │ │ │ └── Permission.java │ │ │ ├── util │ │ │ ├── package-info.java │ │ │ ├── handler │ │ │ │ ├── Handler.java │ │ │ │ └── CompleteHandler.java │ │ │ ├── log │ │ │ │ ├── LogLevel.java │ │ │ │ ├── LogBridge.java │ │ │ │ ├── LoggingBridge.java │ │ │ │ └── LogManager.java │ │ │ ├── http │ │ │ │ ├── HttpResp.java │ │ │ │ ├── WsClient.java │ │ │ │ └── HttpClient.java │ │ │ ├── json │ │ │ │ ├── EncodingFormat.java │ │ │ │ ├── encoders │ │ │ │ │ ├── ListEncoder.java │ │ │ │ │ └── MapEncoder.java │ │ │ │ └── decoders │ │ │ │ │ ├── ListDecoder.java │ │ │ │ │ └── MapDecoder.java │ │ │ ├── SubData.java │ │ │ ├── NodeUtils.java │ │ │ ├── Objects.java │ │ │ └── PropertyReference.java │ │ │ ├── link │ │ │ ├── package-info.java │ │ │ └── Linkable.java │ │ │ ├── serializer │ │ │ └── package-info.java │ │ │ ├── handshake │ │ │ ├── package-info.java │ │ │ └── RemoteKey.java │ │ │ ├── provider │ │ │ ├── package-info.java │ │ │ ├── netty │ │ │ │ └── DefaultLoopProvider.java │ │ │ ├── WsProvider.java │ │ │ ├── HttpProvider.java │ │ │ └── LoopProvider.java │ │ │ ├── config │ │ │ └── package-info.java │ │ │ ├── methods │ │ │ ├── package-info.java │ │ │ ├── requests │ │ │ │ ├── CloseRequest.java │ │ │ │ ├── ContinuousInvokeRequest.java │ │ │ │ ├── ListRequest.java │ │ │ │ ├── RemoveRequest.java │ │ │ │ ├── UnsubscribeRequest.java │ │ │ │ ├── SetRequest.java │ │ │ │ ├── InvokeRequest.java │ │ │ │ └── SubscribeRequest.java │ │ │ ├── Request.java │ │ │ ├── responses │ │ │ │ ├── ErrorResponse.java │ │ │ │ ├── CloseResponse.java │ │ │ │ ├── UnsubscribeResponse.java │ │ │ │ ├── RemoveResponse.java │ │ │ │ └── SubscribeResponse.java │ │ │ ├── StreamState.java │ │ │ └── Response.java │ │ │ ├── package-info.java │ │ │ └── connection │ │ │ ├── package-info.java │ │ │ ├── ConnectionType.java │ │ │ ├── NetworkClient.java │ │ │ ├── MessageTracker.java │ │ │ └── NetworkHandlers.java │ │ └── test │ │ └── java │ │ └── org │ │ └── dsa │ │ └── iot │ │ └── dslink │ │ ├── handshake │ │ ├── LocalKeysTest.java │ │ ├── LocalHandshakeTest.java │ │ └── RemoteKeyTest.java │ │ ├── node │ │ ├── value │ │ │ ├── ValueTypeTest.java │ │ │ └── ValueTest.java │ │ ├── NodeManagerTest.java │ │ └── NodeTest.java │ │ └── util │ │ ├── LogManagerTest.java │ │ ├── FileUtilsTest.java │ │ ├── UrlBase64Test.java │ │ ├── ConfigurationTest.java │ │ └── ObjectsTest.java └── README.md ├── .gitattributes ├── runtimes ├── README.md ├── container │ ├── README.md │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── org │ │ │ └── dsa │ │ │ └── iot │ │ │ └── container │ │ │ ├── wrapper │ │ │ ├── package-info.java │ │ │ ├── LocalKeys.java │ │ │ └── log │ │ │ │ └── LogManager.java │ │ │ ├── stdin │ │ │ └── LinkHandler.java │ │ │ ├── utils │ │ │ ├── Json.java │ │ │ └── JarInfo.java │ │ │ ├── security │ │ │ └── SecMan.java │ │ │ ├── Main.java │ │ │ └── Args.java │ └── build.gradle └── broker │ ├── src │ └── main │ │ └── java │ │ └── org │ │ └── dsa │ │ └── iot │ │ └── broker │ │ └── Main.java │ ├── build.gradle.old │ └── README.md ├── .gitignore ├── LICENSE.md ├── settings.gradle ├── README.md └── gradlew.bat /internal/logging/README.md: -------------------------------------------------------------------------------- 1 | # Logging 2 | 3 | A lightweight logging module for SLF4J. 4 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | These projects are example DSLink implementations of various use cases. 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IOT-DSA/sdk-dslink-java/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sdk/broker/build.gradle.old: -------------------------------------------------------------------------------- 1 | //apply from: '../build.gradle' 2 | 3 | dependencies { 4 | implementation project(':dslink') 5 | } 6 | -------------------------------------------------------------------------------- /internal/logging/build.gradle: -------------------------------------------------------------------------------- 1 | description = 'Lightweight SL4J binding' 2 | 3 | dependencies { 4 | api 'org.slf4j:slf4j-api:1.7.32' 5 | } -------------------------------------------------------------------------------- /sdk/historian/build.gradle: -------------------------------------------------------------------------------- 1 | description = 'SDK for creating Historian DSLinks' 2 | 3 | dependencies { 4 | api project(':dslink') 5 | } 6 | -------------------------------------------------------------------------------- /sdk/commons/build.gradle: -------------------------------------------------------------------------------- 1 | description = 'Commons API to assist with developing DSLinks' 2 | 3 | dependencies { 4 | api project(':dslink') 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Default to unix line endings 2 | * text eol=lf 3 | 4 | # Specific line endings 5 | *.bat eol=crlf 6 | 7 | # Binaries 8 | *.jar binary 9 | -------------------------------------------------------------------------------- /sdk/dslink/build.gradle: -------------------------------------------------------------------------------- 1 | description = 'SDK for the IoT DSA protocol' 2 | 3 | dependencies { 4 | api project(':logging') 5 | api project(':runtime_shared') 6 | } 7 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles node data 3 | * 4 | * @author Samuel Grenier 5 | */ 6 | package org.dsa.iot.dslink.node; 7 | -------------------------------------------------------------------------------- /runtimes/README.md: -------------------------------------------------------------------------------- 1 | # Runtime Software 2 | 3 | These projects are ran as standalone software. While they are not APIs for 4 | developing DSLinks, they do provide specific functionality. 5 | -------------------------------------------------------------------------------- /internal/README.md: -------------------------------------------------------------------------------- 1 | # Internal Projects 2 | 3 | These are projects that are depended on by the SDK itself. Developers should 4 | not directly use any of the libraries in here for DSLink development. 5 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Utilities to perform miscellaneous operations. 3 | * 4 | * @author Samuel Grenier 5 | */ 6 | package org.dsa.iot.dslink.util; 7 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/link/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Manages the interfaces between a requester and a responder. 3 | * @author Samuel Grenier 4 | */ 5 | package org.dsa.iot.dslink.link; 6 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/serializer/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles serialization and deserialization of responders. 3 | * 4 | * @author Samuel Grenier 5 | */ 6 | package org.dsa.iot.dslink.serializer; 7 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/handshake/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package handles a handshake between a client and server. 3 | * 4 | * @author Samuel Grenier 5 | */ 6 | package org.dsa.iot.dslink.handshake; 7 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/provider/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Global providers that can replace underlying components of the SDK. 3 | * 4 | * @author Samuel Grenier 5 | */ 6 | package org.dsa.iot.dslink.provider; -------------------------------------------------------------------------------- /runtimes/container/README.md: -------------------------------------------------------------------------------- 1 | # Container 2 | 3 | The container runs multiple DSLinks under a single process. The goal is to use 4 | less resources as it requires only a single running JVM rather than multiple 5 | JVMs running at once. 6 | -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/wrapper/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Manages dslinks at an individual level for fine grained control. 3 | * 4 | * @author Samuel Grenier 5 | */ 6 | package org.dsa.iot.container.wrapper; -------------------------------------------------------------------------------- /internal/runtime_shared/README.md: -------------------------------------------------------------------------------- 1 | # Shared Runtime 2 | 3 | A library designed to go into the global classpath of the runtime container 4 | system. The goal of this library is to share resources across DSLinks when 5 | using the runtime container. 6 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/config/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Manages a configuration based on provided arguments and the 3 | * dslink JSON file. 4 | * 5 | * @author Samuel Grenier 6 | */ 7 | package org.dsa.iot.dslink.config; 8 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/handler/Handler.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.handler; 2 | 3 | /** 4 | * @author Samuel Grenier 5 | */ 6 | public interface Handler { 7 | 8 | void handle(T event); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles the sending and receiving of requests and responses in a common 3 | * data object. 4 | * 5 | * @author Samuel Grenier 6 | */ 7 | package org.dsa.iot.dslink.methods; 8 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/actions/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * API for managing actions within a node. Such as parameters, columns, 3 | * invocations, permissions, etc... 4 | * 5 | * @author Samuel Grenier 6 | */ 7 | package org.dsa.iot.dslink.node.actions; 8 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/config/broker/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains configuration information about the broker. Typically the broker 3 | * configurations are store on disk. 4 | * 5 | * @author Samuel Grenier 6 | */ 7 | package org.dsa.iot.broker.config.broker; -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/stdin/LinkHandler.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.container.stdin; 2 | 3 | /** 4 | * @author Samuel Grenier 5 | */ 6 | public interface LinkHandler { 7 | 8 | String THREAD_NAME = "input-handler"; 9 | 10 | void start(); 11 | } 12 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/value/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package is used for handle values of everything inside of a node. Such 3 | * as configurations, attributes, parameters, results. 4 | * 5 | * @author Samuel Grenier 6 | */ 7 | package org.dsa.iot.dslink.node.value; 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle 2 | .gradle 3 | build 4 | 5 | # Project files 6 | *.iml 7 | .idea 8 | .classpath 9 | .metadata 10 | .project 11 | .settings 12 | eclipseBin 13 | target 14 | out 15 | 16 | # Misc 17 | .DS_Store 18 | 19 | # DSA 20 | .key 21 | nodes.json 22 | nodes.json.bak 23 | /gradle.properties 24 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/MetaData.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node; 2 | 3 | /** 4 | * @author Samuel Grenier 5 | */ 6 | public interface MetaData { 7 | 8 | /** 9 | * @param node Node to set in the meta data class. 10 | */ 11 | void setNode(Node node); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Top level components that glue together the low level APIs. This allows for 3 | * an elegant abstraction while also being flexible for customizing the low level 4 | * components as necessary. 5 | * 6 | * @author Samuel Grenier 7 | */ 8 | package org.dsa.iot.dslink; 9 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/log/LogLevel.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.log; 2 | 3 | /** 4 | * Supported log levels the log bridge must be able to handle. 5 | * 6 | * @author Samuel Grenier 7 | */ 8 | public enum LogLevel { 9 | 10 | OFF, 11 | ERROR, 12 | WARN, 13 | INFO, 14 | DEBUG, 15 | TRACE 16 | } 17 | -------------------------------------------------------------------------------- /runtimes/broker/src/main/java/org/dsa/iot/broker/Main.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker; 2 | 3 | /** 4 | * @author Samuel Grenier 5 | */ 6 | public class Main { 7 | 8 | public static void main(String[] args) { 9 | Broker broker = Broker.create(args); 10 | if (broker != null) { 11 | broker.start(); 12 | } 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/config/broker/BrokerConfig.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.config.broker; 2 | 3 | import org.dsa.iot.dslink.util.json.JsonObject; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public abstract class BrokerConfig { 9 | 10 | public abstract JsonObject get(); 11 | 12 | public abstract void readAndUpdate(); 13 | } 14 | -------------------------------------------------------------------------------- /examples/dual/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'application' 2 | 3 | mainClassName = 'org.dsa.iot.dual.Main' 4 | 5 | dependencies { 6 | api project(':dslink') 7 | } 8 | 9 | run { 10 | args System.getProperty("exec.args", "").split() + "-d" + "../dslink.json" 11 | workingDir project.buildDir 12 | } 13 | 14 | applicationDistribution.from new File(project.projectDir, "/dslink.json") 15 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/utils/TimestampRange.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.utils; 2 | 3 | import java.util.Calendar; 4 | 5 | public class TimestampRange { 6 | public final Calendar from; 7 | public final Calendar to; 8 | 9 | public TimestampRange(Calendar from, Calendar to) { 10 | this.from = from; 11 | this.to = to; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/requester/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'application' 2 | 3 | mainClassName = 'org.dsa.iot.requester.Main' 4 | 5 | dependencies { 6 | api project(':dslink') 7 | } 8 | 9 | run { 10 | args System.getProperty("exec.args", "").split() + "-d" + "../dslink.json" 11 | workingDir project.buildDir 12 | } 13 | 14 | applicationDistribution.from new File(project.projectDir, "/dslink.json") 15 | -------------------------------------------------------------------------------- /examples/responder/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'application' 2 | 3 | mainClassName = 'org.dsa.iot.responder.Main' 4 | 5 | dependencies { 6 | api project(':dslink') 7 | } 8 | 9 | run { 10 | args System.getProperty("exec.args", "").split() + "-d" + "../dslink.json" 11 | workingDir project.buildDir 12 | } 13 | 14 | applicationDistribution.from new File(project.projectDir, "/dslink.json") 15 | -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/utils/Json.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.container.utils; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class Json { 9 | 10 | private static final ObjectMapper MAPPER = new ObjectMapper(); 11 | 12 | public static ObjectMapper getMapper() { 13 | return MAPPER; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/connection/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles connecting to remote endpoints and servers. The handshake package is 3 | * used for handling handshakes and authentication to remote endpoints. A 4 | * successful handshake is necessary before the connection can proceed. Otherwise 5 | * the client will be rejected. 6 | * 7 | * @author Samuel Grenier 8 | */ 9 | package org.dsa.iot.dslink.connection; 10 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/connection/ConnectionType.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.connection; 2 | 3 | /** 4 | * Connection type used in a configuration 5 | * 6 | * @author Samuel Grenier 7 | */ 8 | public enum ConnectionType { 9 | 10 | /** 11 | * Web socket connection type. Used for connecting to data endpoints 12 | * after a handshake to connect to the web socket URI. 13 | */ 14 | WEB_SOCKET 15 | 16 | } 17 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/handler/CompleteHandler.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.handler; 2 | 3 | /** 4 | * Similar to a handler however it provides an easy way to determine 5 | * whether there are no more events. 6 | * 7 | * @author Samuel Grenier 8 | */ 9 | public interface CompleteHandler extends Handler { 10 | 11 | /** 12 | * All events are now completed. 13 | */ 14 | void complete(); 15 | } 16 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/exceptions/NoSuchPathException.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.exceptions; 2 | 3 | /** 4 | * Thrown when no path can be located 5 | * 6 | * @author Samuel Grenier 7 | */ 8 | public class NoSuchPathException extends RuntimeException { 9 | 10 | /** 11 | * @param path Path that doesn't exist 12 | */ 13 | public NoSuchPathException(String path) { 14 | super("No such path: " + path); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /runtimes/broker/build.gradle.old: -------------------------------------------------------------------------------- 1 | apply plugin: 'application' 2 | 3 | mainClassName = 'org.dsa.iot.broker.Main' 4 | applicationDefaultJvmArgs = [ "-XX:+UseG1GC" ] 5 | 6 | dependencies { 7 | implementation 'org.javassist:javassist:3.20.0-GA' 8 | implementation project(':broker') 9 | } 10 | 11 | run { 12 | args System.getProperty("exec.args", "").split() 13 | workingDir project.buildDir 14 | } 15 | 16 | tasks.withType(CreateStartScripts) { 17 | applicationName = "broker" 18 | } 19 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/requests/CloseRequest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.requests; 2 | 3 | import org.dsa.iot.dslink.methods.Request; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class CloseRequest extends Request { 10 | 11 | @Override 12 | public String getName() { 13 | return "close"; 14 | } 15 | 16 | @Override 17 | public void addJsonValues(JsonObject out) { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /runtimes/container/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':runtime_shared') 3 | } 4 | 5 | jar { 6 | from { 7 | (configurations.runtime).collect { 8 | it.isDirectory() ? it : zipTree(it) 9 | } 10 | } 11 | manifest { 12 | attributes("Main-Class": "org.dsa.iot.container.Main") 13 | } 14 | exclude 'META-INF/INDEX.LIST' 15 | exclude 'META-INF/BCKEY.*' 16 | exclude 'META-INF/LICENSE' 17 | exclude 'META-INF/NOTICE' 18 | exclude 'META-INF/maven/**' 19 | } 20 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/utils/TimeParser.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.utils; 2 | 3 | import org.dsa.iot.dslink.util.*; 4 | 5 | /** 6 | * Use TimeUtils instead. 7 | * 8 | * @author Samuel Grenier 9 | * @deprecated Use TimeUtils instead. 10 | */ 11 | public class TimeParser { 12 | 13 | public static long parse(String time) { 14 | return TimeUtils.decode(time); 15 | } 16 | 17 | public static String parse(long time) { 18 | return TimeUtils.encode(time, true).toString(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/utils/JarInfo.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.container.utils; 2 | 3 | import java.net.URL; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class JarInfo { 9 | 10 | private final URL url; 11 | private final boolean isNative; 12 | 13 | public JarInfo(URL url, boolean isNative) { 14 | this.url = url; 15 | this.isNative = isNative; 16 | } 17 | 18 | public URL getUrl() { 19 | return url; 20 | } 21 | 22 | public boolean isNative() { 23 | return isNative; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/actions/table/Insert.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.actions.table; 2 | 3 | public class Insert extends Modify { 4 | 5 | private int insertBefore; 6 | 7 | public Insert(int insertBefore) { 8 | this.insertBefore = insertBefore; 9 | } 10 | 11 | @Override 12 | public boolean isInsert() { 13 | return true; 14 | } 15 | 16 | public int getInsertIndex() { 17 | return insertBefore; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "insert " + Integer.toString(insertBefore); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /runtimes/broker/README.md: -------------------------------------------------------------------------------- 1 | # Broker 2 | 3 | Standalone runtime Java DSA broker. 4 | 5 | # Setting up SSL 6 | 7 | ## Using a certificate you already have 8 | 9 | The X.509 certificate must be in PEM format. The private key must be in pkcs8 10 | format. Encryption is optional in the server configuration. 11 | 12 | ## Creating a self-signed certificate 13 | 14 | - openssl genrsa -out tmp.key 4096 15 | - openssl req -new -key tmp.key -out server.csr 16 | - openssl x509 -req -days 365 -in server.csr -signkey tmp.key -out server.pem -outform PEM 17 | - openssl pkcs8 -topk8 -in tmp.key -out server.key 18 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/CountRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class CountRollup extends Rollup { 9 | 10 | private long count; 11 | 12 | @Override 13 | public void reset() { 14 | count = 0; 15 | } 16 | 17 | @Override 18 | public void update(Value value, long ts) { 19 | count++; 20 | } 21 | 22 | @Override 23 | public Value getValue() { 24 | return new Value(count); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sdk/README.md: -------------------------------------------------------------------------------- 1 | # SDK 2 | 3 | The SDK is intended for DSLink development. 4 | 5 | ## DSLink Library 6 | 7 | This is the primary library used for DSLink development. 8 | 9 | ## Commons Library 10 | 11 | The goal of this library is to make DSLink development faster. The Commons API 12 | has higher level abstractions for common specific use cases in DSLink 13 | development. 14 | 15 | ## Historian Library 16 | 17 | This is a very high level library that enables very quick development of 18 | historian enabled DSLinks. 19 | 20 | ## Broker Library 21 | 22 | This library can be used to embed the DSA broker into any application. 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ``` 2 | Copyright 2014 DGLogik Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ``` 16 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/Request.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods; 2 | 3 | import org.dsa.iot.dslink.util.json.JsonObject; 4 | 5 | /** 6 | * Generic request API 7 | * 8 | * @author Samuel Grenier 9 | */ 10 | public abstract class Request { 11 | 12 | /** 13 | * @return The name of the request 14 | */ 15 | public abstract String getName(); 16 | 17 | /** 18 | * Add the JSON request values of the specified request to the JSON to 19 | * send to the responder. 20 | * 21 | * @param out Values to add to. 22 | */ 23 | public abstract void addJsonValues(JsonObject out); 24 | } 25 | -------------------------------------------------------------------------------- /examples/responder/src/main/java/org/dsa/iot/responder/util/FutureCloseHandler.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.responder.util; 2 | 3 | 4 | import org.dsa.iot.dslink.util.handler.Handler; 5 | 6 | import java.util.concurrent.ScheduledFuture; 7 | 8 | /** 9 | * @author Samuel Grenier 10 | */ 11 | public class FutureCloseHandler implements Handler { 12 | 13 | private ScheduledFuture fut; 14 | 15 | public FutureCloseHandler(ScheduledFuture fut) { 16 | this.fut = fut; 17 | } 18 | 19 | @Override 20 | public void handle(Void event) { 21 | if (fut != null) { 22 | fut.cancel(false); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/actions/table/Replace.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.actions.table; 2 | 3 | public class Replace extends Modify { 4 | 5 | private int from, to; 6 | 7 | public Replace(int from, int to) { 8 | this.from = from; 9 | this.to = to; 10 | } 11 | 12 | @Override 13 | public boolean isReplace() { 14 | return true; 15 | } 16 | 17 | public int getReplaceFrom() { 18 | return from; 19 | } 20 | 21 | public int getReplaceTo() { 22 | return to; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "replace " + Integer.toString(from) + "-" + Integer.toString(to); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/http/HttpResp.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.http; 2 | 3 | import io.netty.handler.codec.http.HttpResponseStatus; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class HttpResp { 9 | 10 | private HttpResponseStatus status; 11 | private String body; 12 | 13 | public HttpResponseStatus getStatus() { 14 | return status; 15 | } 16 | 17 | public void setStatus(HttpResponseStatus status) { 18 | this.status = status; 19 | } 20 | 21 | public String getBody() { 22 | return body; 23 | } 24 | 25 | public void setBody(String body) { 26 | this.body = body; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /internal/runtime_shared/build.gradle: -------------------------------------------------------------------------------- 1 | description = 'Shared Runtime for the SDK' 2 | 3 | dependencies { 4 | api 'org.bouncycastle:bcprov-jdk15on:1.69' 5 | api 'io.netty:netty-all:4.1.66.Final' 6 | //jzlib is an optional dependency of netty, it is possible to trigger usage 7 | implementation 'com.jcraft:jzlib:1.1.3' 8 | api 'com.beust:jcommander:1.81' 9 | api 'org.msgpack:jackson-dataformat-msgpack:0.9.0' 10 | //the following overrides the dependency in jackson-dataformat-msgpack 11 | //api 'com.fasterxml.jackson.core:jackson-databind:2.10.3' 12 | } 13 | 14 | repositories { 15 | maven { 16 | url 'https://repo.maven.apache.org/maven2' 17 | name 'Maven Central' 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/SumRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class SumRollup extends Rollup { 9 | 10 | private double sum; 11 | 12 | @Override 13 | public void reset() { 14 | sum = 0; 15 | } 16 | 17 | @Override 18 | public void update(Value value, long ts) { 19 | Number number = value.getNumber(); 20 | if (number != null) { 21 | sum += number.doubleValue(); 22 | } 23 | } 24 | 25 | @Override 26 | public Value getValue() { 27 | return new Value(sum); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/LastRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class LastRollup extends Rollup { 9 | 10 | private long ts = -1; 11 | private Value value; 12 | 13 | @Override 14 | public void reset() { 15 | value = null; 16 | } 17 | 18 | @Override 19 | public void update(Value value, long ts) { 20 | if (ts > this.ts) { 21 | this.ts = ts; 22 | this.value = value; 23 | } 24 | } 25 | 26 | @Override 27 | public Value getValue() { 28 | return value; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/log/LogBridge.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.log; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public interface LogBridge { 9 | 10 | /** 11 | * Configures the logger. 12 | * 13 | * @param logPath Path where the logger should output to or {@code null} to 14 | * log to standard output steams. 15 | */ 16 | void configure(File logPath); 17 | 18 | /** 19 | * Sets the log level of the root logger. 20 | * 21 | * @param level Level to set. 22 | */ 23 | void setLevel(LogLevel level); 24 | 25 | /** 26 | * 27 | * @return Log level of the root logger. 28 | */ 29 | LogLevel getLevel(); 30 | } 31 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/FirstRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class FirstRollup extends Rollup { 9 | 10 | private Value value; 11 | private long ts; 12 | 13 | @Override 14 | public void reset() { 15 | value = null; 16 | ts = 0; 17 | } 18 | 19 | @Override 20 | public void update(Value value, long ts) { 21 | if (this.value == null || this.ts > ts) { 22 | this.ts = ts; 23 | this.value = value; 24 | } 25 | } 26 | 27 | @Override 28 | public Value getValue() { 29 | return value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/security/SecMan.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.container.security; 2 | 3 | import java.security.Permission; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class SecMan extends SecurityManager { 9 | 10 | @Override 11 | public void checkExit(int status) { 12 | throw new SecurityException("Exit not allowed"); 13 | } 14 | 15 | @Override 16 | public void checkPermission(Permission perm) { 17 | if (perm instanceof RuntimePermission) { 18 | if ("setSecurityManager".equals(perm.getName())) { 19 | String err = "Setting a security manager is not allowed"; 20 | throw new SecurityException(err); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/responses/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.responses; 2 | 3 | /** 4 | * Any method response can have an attached error response. An error may occur 5 | * during any request. 6 | * 7 | * @author Samuel Grenier 8 | */ 9 | public class ErrorResponse { 10 | 11 | private final String msg; 12 | private final String detail; 13 | 14 | public ErrorResponse(String msg, String detail) { 15 | this.msg = msg; 16 | this.detail = detail; 17 | } 18 | 19 | @SuppressWarnings("unused") 20 | public String getMessage() { 21 | return msg; 22 | } 23 | 24 | @SuppressWarnings("unused") 25 | public String getDetail() { 26 | return detail; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/requests/ContinuousInvokeRequest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.requests; 2 | 3 | import org.dsa.iot.dslink.methods.Request; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class ContinuousInvokeRequest extends Request { 10 | 11 | private final JsonObject params; 12 | 13 | public ContinuousInvokeRequest(JsonObject params) { 14 | this.params = params; 15 | } 16 | 17 | @Override 18 | public String getName() { 19 | return null; 20 | } 21 | 22 | @Override 23 | public void addJsonValues(JsonObject out) { 24 | if (params != null) { 25 | out.put("params", params); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/NodePair.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node; 2 | 3 | /** 4 | * Holds a pair between a node reference and a configuration or attribute in 5 | * the path. 6 | * @author Samuel Grenier 7 | */ 8 | public class NodePair { 9 | 10 | /** 11 | * Path of the node 12 | */ 13 | private final Node node; 14 | 15 | /** 16 | * Reference to a configuration or attribute. 17 | */ 18 | private final String reference; 19 | 20 | public NodePair(Node node, String reference) { 21 | this.node = node; 22 | this.reference = reference; 23 | } 24 | 25 | public Node getNode() { 26 | return node; 27 | } 28 | 29 | public String getReference() { 30 | return reference; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/processor/LinkHandler.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.processor; 2 | 3 | import org.dsa.iot.broker.node.DSLinkNode; 4 | import org.dsa.iot.broker.server.client.Client; 5 | import org.dsa.iot.dslink.util.json.JsonObject; 6 | 7 | /** 8 | * @author Samuel Grenier 9 | */ 10 | public abstract class LinkHandler { 11 | 12 | private final DSLinkNode node; 13 | 14 | public LinkHandler(DSLinkNode node) { 15 | if (node == null) { 16 | throw new NullPointerException("node"); 17 | } 18 | this.node = node; 19 | } 20 | 21 | public DSLinkNode node() { 22 | return node; 23 | } 24 | 25 | public Client client() { 26 | return node.client(); 27 | } 28 | 29 | protected abstract void process(JsonObject obj); 30 | } 31 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/handshake/LocalKeysTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.handshake; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Tests key serialization, deserialization, and generation. 8 | * 9 | * @author Samuel Grenier 10 | */ 11 | public class LocalKeysTest { 12 | 13 | /** 14 | * Ensures serialization and deserialization of the keys are consistently 15 | * operated on. Consistency in hashcode is tested as well. 16 | */ 17 | @Test 18 | public void encodeDecodeConsistency() { 19 | LocalKeys keys = LocalKeys.generate(); 20 | LocalKeys newKeys = LocalKeys.deserialize(keys.serialize()); 21 | 22 | Assert.assertEquals(keys, newKeys); 23 | Assert.assertEquals(keys.hashCode(), newKeys.hashCode()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/AvgRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class AvgRollup extends Rollup { 9 | 10 | private double total; 11 | private int count; 12 | 13 | @Override 14 | public void reset() { 15 | total = 0; 16 | count = 0; 17 | } 18 | 19 | @Override 20 | public void update(Value value, long ts) { 21 | count++; 22 | 23 | Number number = value.getNumber(); 24 | if (number != null) { 25 | total += number.doubleValue(); 26 | } 27 | } 28 | 29 | @Override 30 | public Value getValue() { 31 | double avg = total / count; 32 | return new Value(avg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/MaxRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class MaxRollup extends Rollup { 9 | 10 | private Number number; 11 | 12 | @Override 13 | public void reset() { 14 | number = null; 15 | } 16 | 17 | @Override 18 | public void update(Value value, long ts) { 19 | Number num = value.getNumber(); 20 | if (number == null) { 21 | number = num; 22 | } else if (num != null) { 23 | double a = num.doubleValue(); 24 | double b = number.doubleValue(); 25 | number = Math.max(a, b); 26 | } 27 | } 28 | 29 | @Override 30 | public Value getValue() { 31 | return new Value(number); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/MinRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class MinRollup extends Rollup { 9 | 10 | private Number number; 11 | 12 | @Override 13 | public void reset() { 14 | number = null; 15 | } 16 | 17 | @Override 18 | public void update(Value value, long ts) { 19 | Number num = value.getNumber(); 20 | if (number == null) { 21 | number = num; 22 | } else if (num != null) { 23 | double a = num.doubleValue(); 24 | double b = number.doubleValue(); 25 | number = Math.min(a, b); 26 | } 27 | } 28 | 29 | @Override 30 | public Value getValue() { 31 | return new Value(number); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /internal/logging/src/main/java/org/slf4j/impl/Level.java: -------------------------------------------------------------------------------- 1 | package org.slf4j.impl; 2 | 3 | /** 4 | * @author Samuel Grenier 5 | */ 6 | public class Level { 7 | public static final Level OFF = new Level(0, "OFF"); 8 | public static final Level ERROR = new Level(100, "ERROR"); 9 | public static final Level WARN = new Level(200, "WARN"); 10 | public static final Level INFO = new Level(300, "INFO"); 11 | public static final Level DEBUG = new Level(400, "DEBUG"); 12 | public static final Level TRACE = new Level(500, "TRACE"); 13 | 14 | private final int level; 15 | private final String name; 16 | 17 | public Level(int level, String name) { 18 | this.level = level; 19 | this.name = name; 20 | } 21 | 22 | public int getLevel() { 23 | return level; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/requests/ListRequest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.requests; 2 | 3 | import org.dsa.iot.dslink.methods.Request; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class ListRequest extends Request { 10 | private final String path; 11 | 12 | public ListRequest(String path) { 13 | if (path == null) 14 | throw new NullPointerException("path"); 15 | this.path = path; 16 | } 17 | 18 | @Override 19 | public String getName() { 20 | return "list"; 21 | } 22 | 23 | /** 24 | * @return Path used in the request 25 | */ 26 | public String getPath() { 27 | return path; 28 | } 29 | 30 | @Override 31 | public void addJsonValues(JsonObject out) { 32 | out.put("path", path); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/requests/RemoveRequest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.requests; 2 | 3 | import org.dsa.iot.dslink.methods.Request; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * Request used to remove attributes or configurations. 8 | * 9 | * @author Samuel Grenier 10 | */ 11 | public class RemoveRequest extends Request { 12 | 13 | private final String path; 14 | 15 | public RemoveRequest(String path) { 16 | if (path == null) { 17 | throw new NullPointerException("path"); 18 | } 19 | this.path = path; 20 | } 21 | 22 | @Override 23 | public String getName() { 24 | return "remove"; 25 | } 26 | 27 | public String getPath() { 28 | return path; 29 | } 30 | 31 | @Override 32 | public void addJsonValues(JsonObject out) { 33 | out.put("path", path); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/utils/WatchUpdate.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.utils; 2 | 3 | import org.dsa.iot.dslink.node.value.SubscriptionValue; 4 | import org.dsa.iot.historian.database.Watch; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class WatchUpdate { 10 | 11 | private final Watch watch; 12 | private final SubscriptionValue update; 13 | private long intervalTimestamp; 14 | 15 | public WatchUpdate(Watch watch, SubscriptionValue update) { 16 | this.watch = watch; 17 | this.update = update; 18 | } 19 | 20 | public Watch getWatch() { 21 | return watch; 22 | } 23 | 24 | public SubscriptionValue getUpdate() { 25 | return update; 26 | } 27 | 28 | public long getIntervalTimestamp() { 29 | return intervalTimestamp; 30 | } 31 | 32 | public void updateTimestamp(long nowTimestamp) { 33 | intervalTimestamp = nowTimestamp; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/provider/netty/DefaultLoopProvider.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.provider.netty; 2 | 3 | import org.dsa.iot.dslink.provider.LoopProvider; 4 | import org.dsa.iot.dslink.util.Objects; 5 | 6 | import java.util.concurrent.ScheduledFuture; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class DefaultLoopProvider extends LoopProvider { 10 | @Override 11 | public void schedule(Runnable task) { 12 | Objects.getDaemonThreadPool().execute(task); 13 | } 14 | 15 | @Override 16 | public ScheduledFuture schedule(Runnable task, long delay, TimeUnit timeUnit) { 17 | return Objects.getDaemonThreadPool().schedule(task, delay, timeUnit); 18 | } 19 | 20 | @Override 21 | public ScheduledFuture schedulePeriodic(Runnable task, long initialDelay, long delay, TimeUnit timeUnit) { 22 | return Objects.getDaemonThreadPool().scheduleWithFixedDelay(task, initialDelay, delay, timeUnit); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sdk/commons/src/main/java/org/dsa/iot/commons/Container.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.commons; 2 | 3 | /** 4 | * Provides an easy way to set variables from an inner class to an 5 | * outer class. 6 | * 7 | * @author Samuel Grenier 8 | */ 9 | public class Container { 10 | 11 | private T value; 12 | 13 | /** 14 | * Initializes the {@link Container} with no value. 15 | */ 16 | public Container() { 17 | this(null); 18 | } 19 | 20 | /** 21 | * Initializes the {@link Container} with a value. 22 | * 23 | * @param value Value to initialize. 24 | */ 25 | public Container(T value) { 26 | this.value = value; 27 | } 28 | 29 | /** 30 | * @param value Value to set. 31 | */ 32 | public void setValue(T value) { 33 | this.value = value; 34 | } 35 | 36 | /** 37 | * @return Value of the {@link Container}. 38 | */ 39 | public T getValue() { 40 | return value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/requests/UnsubscribeRequest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.requests; 2 | 3 | import org.dsa.iot.dslink.methods.Request; 4 | import org.dsa.iot.dslink.util.json.JsonArray; 5 | import org.dsa.iot.dslink.util.json.JsonObject; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Unsubscribes to the designated subscription IDs. 11 | * 12 | * @author Samuel Grenier 13 | */ 14 | public class UnsubscribeRequest extends Request { 15 | 16 | private final List sids; 17 | 18 | public UnsubscribeRequest(List sids) { 19 | if (sids == null) { 20 | throw new IllegalArgumentException("sids"); 21 | } 22 | this.sids = sids; 23 | } 24 | 25 | @Override 26 | public String getName() { 27 | return "unsubscribe"; 28 | } 29 | 30 | @Override 31 | public void addJsonValues(JsonObject out) { 32 | out.put("sids", new JsonArray(sids)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/utils/QueryData.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.utils; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class QueryData { 9 | 10 | private Value value; 11 | private long ts; 12 | 13 | @SuppressWarnings("unused") 14 | public QueryData() { 15 | this(null, -1); 16 | } 17 | 18 | public QueryData(Value value, long ts) { 19 | this.value = value; 20 | this.ts = ts; 21 | } 22 | 23 | public void setValue(Value value) { 24 | this.value = value; 25 | } 26 | 27 | public Value getValue() { 28 | return value; 29 | } 30 | 31 | @SuppressWarnings("unused") 32 | public void setTimestamp(long ts) { 33 | this.ts = ts; 34 | } 35 | 36 | public long getTimestamp() { 37 | return ts; 38 | } 39 | 40 | public boolean isDefined() { 41 | return ts > -1; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/json/EncodingFormat.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.json; 2 | 3 | /** 4 | * @author Samuel Grenier 5 | */ 6 | public enum EncodingFormat { 7 | 8 | JSON("json"); 9 | 10 | private final String name; 11 | 12 | EncodingFormat(String name) { 13 | this.name = name; 14 | } 15 | 16 | public String toJson() { 17 | return name; 18 | } 19 | 20 | public static JsonArray toJsonArray() { 21 | JsonArray array = new JsonArray(); 22 | for (EncodingFormat format : EncodingFormat.values()) { 23 | array.add(format.toJson()); 24 | } 25 | return array; 26 | } 27 | 28 | public static EncodingFormat toEnum(String format) { 29 | if (format == null) { 30 | return JSON; 31 | } 32 | format = format.toLowerCase(); 33 | if (JSON.name.equals(format)) { 34 | return JSON; 35 | } 36 | return JSON; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /internal/logging/src/main/java/org/slf4j/impl/StaticLoggerBinder.java: -------------------------------------------------------------------------------- 1 | package org.slf4j.impl; 2 | 3 | import org.slf4j.ILoggerFactory; 4 | import org.slf4j.spi.LoggerFactoryBinder; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | @SuppressWarnings("unused") 10 | public class StaticLoggerBinder implements LoggerFactoryBinder { 11 | 12 | private static final StaticLoggerBinder INSTANCE = new StaticLoggerBinder(); 13 | 14 | private final LoggerFactoryImpl factory; 15 | private final String className; 16 | 17 | public static StaticLoggerBinder getSingleton() { 18 | return INSTANCE; 19 | } 20 | 21 | private StaticLoggerBinder() { 22 | factory = new LoggerFactoryImpl(); 23 | className = LoggerFactoryImpl.class.getName(); 24 | } 25 | 26 | @Override 27 | public ILoggerFactory getLoggerFactory() { 28 | return factory; 29 | } 30 | 31 | @Override 32 | public String getLoggerFactoryClassStr() { 33 | return className; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/http/WsClient.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.http; 2 | 3 | import org.dsa.iot.dslink.connection.NetworkClient; 4 | import org.dsa.iot.dslink.provider.WsProvider; 5 | import org.dsa.iot.dslink.util.URLInfo; 6 | 7 | /** 8 | * @author Samuel Grenier 9 | */ 10 | public abstract class WsClient { 11 | 12 | private final URLInfo url; 13 | 14 | public WsClient(URLInfo url) { 15 | if (url == null) { 16 | throw new NullPointerException("url"); 17 | } 18 | this.url = url; 19 | } 20 | 21 | public URLInfo getUrl() { 22 | return url; 23 | } 24 | 25 | public void connect() { 26 | WsProvider.getProvider().connect(this); 27 | } 28 | 29 | public abstract void onData(byte[] data, int offset, int length); 30 | 31 | public abstract void onConnected(NetworkClient writer); 32 | 33 | public abstract void onDisconnected(); 34 | 35 | public abstract void onThrowable(Throwable throwable); 36 | } 37 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/actions/table/Modify.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.actions.table; 2 | 3 | public abstract class Modify { 4 | 5 | public boolean isReplace() { 6 | return false; 7 | }; 8 | 9 | public boolean isInsert() { 10 | return true; 11 | }; 12 | 13 | public static Modify fromString(String s) { 14 | String[] arr = s.split("\\s+"); 15 | if (arr.length != 2) { 16 | return null; 17 | } 18 | if ("insert".equals(arr[0])) { 19 | return insert(Integer.parseInt(arr[1])); 20 | } else if ("replace".equals(arr[0])) { 21 | String[] arr2 = arr[1].split("-"); 22 | if (arr2.length != 2) { 23 | return null; 24 | } 25 | return replace(Integer.parseInt(arr2[0]), Integer.parseInt(arr2[1])); 26 | } 27 | return null; 28 | 29 | } 30 | 31 | public static Modify insert(int insertBefore) { 32 | return new Insert(insertBefore); 33 | } 34 | 35 | public static Modify replace(int from, int to) { 36 | return new Replace(from, to); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/OrRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | import org.dsa.iot.dslink.node.value.ValueType; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class OrRollup extends Rollup { 10 | 11 | private boolean value; 12 | 13 | @Override 14 | public void reset() { 15 | value = false; 16 | } 17 | 18 | @Override 19 | public void update(Value value, long ts) { 20 | if (value.getType().equals(ValueType.NUMBER)) { 21 | Number number = value.getNumber(); 22 | if (number != null) { 23 | this.value |= (number.doubleValue() != 0); 24 | } 25 | } else { 26 | Boolean bool = value.getBool(); 27 | if (bool != null) { 28 | this.value |= bool; 29 | } 30 | } 31 | } 32 | 33 | @Override 34 | public Value getValue() { 35 | return new Value(value); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/StreamState.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods; 2 | 3 | /** 4 | * Possible states that a method can be in. 5 | * 6 | * @author Samuel Grenier 7 | */ 8 | public enum StreamState { 9 | 10 | INITIALIZED("initialize"), 11 | OPEN("open"), 12 | CLOSED("closed"); 13 | 14 | private final String jsonName; 15 | 16 | StreamState(String jsonName) { 17 | this.jsonName = jsonName; 18 | } 19 | 20 | public String getJsonName() { 21 | return jsonName; 22 | } 23 | 24 | public static StreamState toEnum(String stream) { 25 | if (stream == null) { 26 | return null; 27 | } 28 | switch (stream) { 29 | case "initialize": 30 | return INITIALIZED; 31 | case "open": 32 | return OPEN; 33 | case "closed": 34 | return CLOSED; 35 | default: 36 | throw new RuntimeException("Unknown stream type: " + stream); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/AndRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | import org.dsa.iot.dslink.node.value.ValueType; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class AndRollup extends Rollup { 10 | 11 | private boolean value; 12 | 13 | @Override 14 | public void reset() { 15 | value = false; 16 | } 17 | 18 | @Override 19 | public void update(Value value, long ts) { 20 | if (value.getType().equals(ValueType.NUMBER)) { 21 | Number number = value.getNumber(); 22 | if (number != null) { 23 | this.value = this.value && (number.doubleValue() != 0); 24 | } 25 | } else { 26 | Boolean bool = value.getBool(); 27 | if (bool != null) { 28 | this.value = this.value && bool; 29 | } 30 | } 31 | } 32 | 33 | @Override 34 | public Value getValue() { 35 | return new Value(value); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/connection/NetworkClient.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.connection; 2 | 3 | import org.dsa.iot.dslink.util.json.EncodingFormat; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * Handles writing and closing connections. Can be used for clients connected 8 | * to servers and remote endpoint connections. 9 | * 10 | * @author Samuel Grenier 11 | */ 12 | public interface NetworkClient { 13 | 14 | /** 15 | * 16 | * @return Whether the client can write immediately to the network without 17 | * being queued. 18 | */ 19 | boolean writable(); 20 | 21 | /** 22 | * Writes text data to the network. 23 | * 24 | * @param format Format the data should be encoded in. 25 | * @param data Data to write. 26 | */ 27 | void write(EncodingFormat format, JsonObject data); 28 | 29 | /** 30 | * Closes the connection to the client 31 | */ 32 | void close(); 33 | 34 | /** 35 | * @return Whether the client is connected or not. 36 | */ 37 | boolean isConnected(); 38 | } 39 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/connection/MessageTracker.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.connection; 2 | 3 | /** 4 | * Keeps track of outgoing message IDs and incoming acks from and to the 5 | * network. 6 | * 7 | * @author Samuel Grenier 8 | */ 9 | public interface MessageTracker { 10 | 11 | /** 12 | * An ack has been received from the network. This directly affects the 13 | * missing ack count. 14 | * 15 | * @param ack Received ack ID. 16 | */ 17 | void ackReceived(int ack); 18 | 19 | /** 20 | * Increments the message ID. This directly affects the missing ack 21 | * count. 22 | * 23 | * @return Next message ID. 24 | */ 25 | int incrementMessageId(); 26 | 27 | /** 28 | * The last ack received from the network. 29 | */ 30 | int lastAckReceived(); 31 | 32 | /** 33 | * Retrieves the amount of missing acks. This can be used for network 34 | * throttling if their is too many missing acks. 35 | * 36 | * @return Missing acks from the network. 37 | */ 38 | int missingAckCount(); 39 | } 40 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/Writable.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node; 2 | 3 | /** 4 | * @author Samuel Grenier 5 | */ 6 | public enum Writable { 7 | 8 | /** 9 | * Whether anyone can write to the value. 10 | */ 11 | WRITE, 12 | 13 | /** 14 | * Whether configuration permissions are required to write to the value. 15 | */ 16 | CONFIG, 17 | 18 | /** 19 | * Can never write to the value. 20 | */ 21 | NEVER; 22 | 23 | public String toJsonName() { 24 | return this.toString().toLowerCase(); 25 | } 26 | 27 | public static Writable toEnum(String writable) { 28 | if (writable == null) { 29 | return NEVER; 30 | } 31 | switch (writable) { 32 | case "write": 33 | return WRITE; 34 | case "config": 35 | return CONFIG; 36 | case "never": 37 | return NEVER; 38 | default: 39 | throw new RuntimeException("Unhandled writable permission: " + writable); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/provider/WsProvider.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.provider; 2 | 3 | import org.dsa.iot.dslink.provider.netty.DefaultWsProvider; 4 | import org.dsa.iot.dslink.util.http.WsClient; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public abstract class WsProvider { 10 | 11 | private static WsProvider PROVIDER; 12 | private boolean useCompression = true; 13 | 14 | public abstract void connect(WsClient client); 15 | 16 | public static WsProvider getProvider() { 17 | if (PROVIDER == null) { 18 | setProvider(new DefaultWsProvider()); 19 | } 20 | return PROVIDER; 21 | } 22 | 23 | public boolean getUseCompression() { 24 | return useCompression; 25 | } 26 | 27 | public static void setProvider(WsProvider provider) { 28 | if (provider == null) { 29 | throw new NullPointerException("provider"); 30 | } 31 | PROVIDER = provider; 32 | } 33 | 34 | public void setUseCompression(boolean useCompression) { 35 | this.useCompression = useCompression; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/SubData.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | import org.dsa.iot.dslink.node.NodeManager; 4 | 5 | /** 6 | * Contains data for a specific path when performing subscription requests. 7 | * 8 | * @author Samuel Grenier 9 | */ 10 | public class SubData { 11 | 12 | private final String path; 13 | private final Integer qos; 14 | 15 | /** 16 | * Constructs a data container used when making a subscription 17 | * request. 18 | * 19 | * @param path Path of the subscription. 20 | * @param qos QoS of the subscription. 21 | */ 22 | public SubData(String path, Integer qos) { 23 | this.path = NodeManager.normalizePath(path, true); 24 | this.qos = qos; 25 | if (qos != null) { 26 | int q = qos; 27 | if (q > 3 || q < 0) { 28 | throw new IllegalArgumentException("Invalid QoS setting"); 29 | } 30 | } 31 | } 32 | 33 | public String getPath() { 34 | return path; 35 | } 36 | 37 | public Integer getQos() { 38 | return qos; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/MessageGenerator.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node; 2 | 3 | import org.dsa.iot.dslink.util.json.JsonObject; 4 | 5 | /** 6 | * Used by subscriptions to achieve proper QOS, but could be used by any responder message that 7 | * want to delay encoding until actually being written to the output stream. 8 | */ 9 | public interface MessageGenerator { 10 | 11 | /** 12 | * The source should provide the message to send here, or null to send nothing. The returned 13 | * message will not be queued, that is the responsibility of the MessageGenerator. 14 | * 15 | * @param lastAckId The last ack received, can be used for qos. 16 | * @return The message to send, or null to not send anything. 17 | */ 18 | public JsonObject getMessage(int lastAckId); 19 | 20 | /** 21 | * The message couldn't be sent, so it should requeue and try again. 22 | */ 23 | public void retry(); 24 | 25 | /** 26 | * If a message was returned from getMessage, this will be it's message id. 27 | */ 28 | public void setMessageId(int messageId); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /examples/requester/dslink.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "requester", 3 | "version": "0.0.1", 4 | "description": "An example requester dslink to demonstrate data requesting", 5 | "license": "Apache", 6 | "author": { 7 | "name": "Samuel Grenier", 8 | "email": "samrg472@gmail.com" 9 | }, 10 | "main": "bin/requester", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/IOT-DSA/sdk-dslink-java" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/IOT-DSA/sdk-dslink-java/issues" 17 | }, 18 | "configs": { 19 | "name": { 20 | "type": "string", 21 | "default": "requester" 22 | }, 23 | "broker": { 24 | "type": "url" 25 | }, 26 | "token": { 27 | "type": "string" 28 | }, 29 | "nodes": { 30 | "type": "path", 31 | "default": "nodes.json" 32 | }, 33 | "key": { 34 | "type": "path", 35 | "default": ".key" 36 | }, 37 | "log": { 38 | "type": "enum", 39 | "default": "info" 40 | }, 41 | "handler_class": { 42 | "type": "string", 43 | "default": "org.dsa.iot.requester.Main" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/responder/dslink.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "responder", 3 | "version": "0.0.1", 4 | "description": "An example responder dslink to demonstrate data handling", 5 | "license": "Apache", 6 | "author": { 7 | "name": "Samuel Grenier", 8 | "email": "samrg472@gmail.com" 9 | }, 10 | "main": "bin/responder", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/IOT-DSA/sdk-dslink-java" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/IOT-DSA/sdk-dslink-java/issues" 17 | }, 18 | "configs": { 19 | "name": { 20 | "type": "string", 21 | "default": "responder" 22 | }, 23 | "broker": { 24 | "type": "url" 25 | }, 26 | "token": { 27 | "type": "string" 28 | }, 29 | "nodes": { 30 | "type": "path", 31 | "default": "nodes.json" 32 | }, 33 | "key": { 34 | "type": "path", 35 | "default": ".key" 36 | }, 37 | "log": { 38 | "type": "enum", 39 | "default": "info" 40 | }, 41 | "handler_class": { 42 | "type": "string", 43 | "default": "org.dsa.iot.responder.Main" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/requests/SetRequest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.requests; 2 | 3 | import org.dsa.iot.dslink.methods.Request; 4 | import org.dsa.iot.dslink.node.value.Value; 5 | import org.dsa.iot.dslink.util.json.JsonObject; 6 | 7 | /** 8 | * Request used to set a value 9 | * 10 | * @author Samuel Grenier 11 | */ 12 | public class SetRequest extends Request { 13 | 14 | private final String path; 15 | private final Value value; 16 | 17 | public SetRequest(String path, Value value) { 18 | if (path == null) { 19 | throw new NullPointerException("path"); 20 | } else if (value == null) { 21 | throw new NullPointerException("value"); 22 | } 23 | this.path = path; 24 | this.value = value; 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return "set"; 30 | } 31 | 32 | public String getPath() { 33 | return path; 34 | } 35 | 36 | @Override 37 | public void addJsonValues(JsonObject out) { 38 | out.put("path", path); 39 | out.put("value", value); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/wrapper/LocalKeys.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.container.wrapper; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class LocalKeys { 10 | 11 | private Class keysClass; 12 | private Object instance; 13 | 14 | public LocalKeys(ClassLoader loader) { 15 | try { 16 | String name = "org.dsa.iot.dslink.handshake.LocalKeys"; 17 | keysClass = loader.loadClass(name); 18 | } catch (ClassNotFoundException e) { 19 | throw new RuntimeException(e); 20 | } 21 | } 22 | 23 | public Object getInstance() { 24 | return instance; 25 | } 26 | 27 | public Class getKeysClass() { 28 | return keysClass; 29 | } 30 | 31 | public void getFromFileSystem(File file) { 32 | try { 33 | Method generate = keysClass.getMethod("getFromFileSystem", File.class); 34 | instance = generate.invoke(null, file); 35 | } catch (Exception e) { 36 | throw new RuntimeException(e); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/connection/NetworkHandlers.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.connection; 2 | 3 | import org.dsa.iot.dslink.util.handler.Handler; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * Handles network clients on vertx events. 8 | * 9 | * @author Samuel Grenier 10 | */ 11 | class NetworkHandlers { 12 | 13 | private Handler onConnected; 14 | private Handler onDisconnected; 15 | private Handler onData; 16 | 17 | public Handler getOnConnected() { 18 | return onConnected; 19 | } 20 | 21 | public void setOnConnected(Handler onConnected) { 22 | this.onConnected = onConnected; 23 | } 24 | 25 | public Handler getOnDisconnected() { 26 | return onDisconnected; 27 | } 28 | 29 | public void setOnDisconnected(Handler onDisconnected) { 30 | this.onDisconnected = onDisconnected; 31 | } 32 | 33 | public Handler getOnData() { 34 | return onData; 35 | } 36 | 37 | public void setOnData(Handler onData) { 38 | this.onData = onData; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/NodeUtils.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | import org.dsa.iot.dslink.node.Node; 4 | import org.dsa.iot.dslink.node.NodeBuilder; 5 | import org.dsa.iot.dslink.node.value.Value; 6 | 7 | /** 8 | * @author Samuel Grenier 9 | */ 10 | public class NodeUtils { 11 | 12 | /** 13 | * When using a {@link NodeBuilder} there is a possibility that the node 14 | * already exists. This can be as a result of serialization. This will 15 | * retrieve the underlying value from the real node if that node exists. 16 | * If the node doesn't currently exist then configuration set on the 17 | * {@link NodeBuilder} will be returned. 18 | * 19 | * @param b Node that has not yet been built. 20 | * @param name Name of the read-only configuration. 21 | * @return Value of the configuration. 22 | */ 23 | public static Value getRoConfig(NodeBuilder b, String name) { 24 | Node n = b.getParent().getChild(b.getChild().getName(), false); 25 | if (n != null) { 26 | return n.getRoConfig(name); 27 | } 28 | return b.getChild().getRoConfig(name); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/actions/table/Row.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.actions.table; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | import java.util.Collections; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | /** 10 | * @author Samuel Grenier 11 | */ 12 | public class Row { 13 | 14 | private List values = new LinkedList<>(); 15 | 16 | /** 17 | * Adds a value to the row. 18 | * 19 | * @param value Value to add. 20 | */ 21 | public void addValue(Value value) { 22 | values.add(value); 23 | } 24 | 25 | /** 26 | * @return A collection of values in the row. 27 | */ 28 | public List getValues() { 29 | return Collections.unmodifiableList(values); 30 | } 31 | 32 | /** 33 | * Convenience method for quickly creating rows. 34 | * 35 | * @param values Array of values. 36 | * @return A created row. 37 | */ 38 | public static Row make(Value... values) { 39 | Row row = new Row(); 40 | for (Value v : values) { 41 | row.addValue(v); 42 | } 43 | return row; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'sdk-dslink-java' 2 | 3 | // APIs for developers to use 4 | include 'dslink' 5 | project(':dslink').projectDir = file('sdk/dslink') 6 | include 'commons' 7 | project(':commons').projectDir = file('sdk/commons') 8 | //include 'broker' 9 | //project(':broker').projectDir = file('sdk/broker') 10 | include 'historian' 11 | project(':historian').projectDir = file('sdk/historian') 12 | 13 | // Internal APIs the SDK depends on 14 | include 'logging' 15 | project(':logging').projectDir = file('internal/logging') 16 | include 'runtime_shared' 17 | project(':runtime_shared').projectDir = file('internal/runtime_shared') 18 | 19 | // Software that is meant to be ran as a standalone 20 | //include 'broker' 21 | //project(':broker').name = 'broker_runtime' 22 | //project(':broker').projectDir = file('runtimes/broker') 23 | include 'container' 24 | project(':container').projectDir = file('runtimes/container') 25 | 26 | // Examples for developers to reference 27 | include 'requester' 28 | project(':requester').projectDir = file('examples/requester') 29 | include 'responder' 30 | project(':responder').projectDir = file('examples/responder') 31 | include 'dual' 32 | project(':dual').projectDir = file('examples/dual') 33 | -------------------------------------------------------------------------------- /examples/dual/src/main/java/org/dsa/iot/dual/Main.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dual; 2 | 3 | import org.dsa.iot.dslink.DSLink; 4 | import org.dsa.iot.dslink.DSLinkFactory; 5 | import org.dsa.iot.dslink.DSLinkHandler; 6 | import org.dsa.iot.dual.requester.Requester; 7 | import org.dsa.iot.dual.responder.Responder; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * @author Samuel Grenier 13 | */ 14 | public class Main extends DSLinkHandler { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); 17 | 18 | @Override 19 | public boolean isRequester() { 20 | return true; 21 | } 22 | 23 | @Override 24 | public boolean isResponder() { 25 | return true; 26 | } 27 | 28 | @Override 29 | public void onResponderInitialized(DSLink link) { 30 | Responder.init(link); 31 | LOGGER.info("Responder initialized"); 32 | } 33 | 34 | @Override 35 | public void onRequesterConnected(final DSLink link) { 36 | Requester.init(link); 37 | LOGGER.info("Requester initialized"); 38 | } 39 | 40 | public static void main(String[] args) { 41 | DSLinkFactory.start(args, new Main()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/node/value/ValueTypeTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.value; 2 | 3 | import org.dsa.iot.dslink.node.Node; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author Samuel Grenier 9 | */ 10 | public class ValueTypeTest { 11 | 12 | @Test 13 | public void typeComparison() { 14 | ValueType dyn = ValueType.DYNAMIC; 15 | Assert.assertTrue(dyn.compare(ValueType.DYNAMIC)); 16 | 17 | ValueType enums = ValueType.makeEnum("a", "b"); 18 | Assert.assertTrue(enums.compare(ValueType.ENUM)); 19 | } 20 | 21 | @Test 22 | public void bool() { 23 | ValueType type = ValueType.makeBool("enabled", "disabled"); 24 | Assert.assertEquals(type.toJsonString(), "bool[disabled,enabled]"); 25 | } 26 | 27 | @Test 28 | public void validEnum() { 29 | Node node = new Node("root", null, null); 30 | node.setValueType(ValueType.makeEnum("a", "b", "c")); 31 | node.setValue(new Value("c")); 32 | } 33 | 34 | @Test(expected = RuntimeException.class) 35 | public void invalidEnum() { 36 | Node node = new Node("root", null, null); 37 | node.setValueType(ValueType.makeEnum("a", "b", "c")); 38 | node.setValue(new Value("d")); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/dual/dslink.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dual", 3 | "version": "0.0.1", 4 | "description": "An example link to demonstrate a requester and responder", 5 | "license": "Apache", 6 | "author": { 7 | "name": "Samuel Grenier", 8 | "email": "samrg472@gmail.com" 9 | }, 10 | "main": "bin/dual", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/IOT-DSA/sdk-dslink-java" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/IOT-DSA/sdk-dslink-java/issues" 17 | }, 18 | "configs": { 19 | "name": { 20 | "type": "string", 21 | "default": "dual" 22 | }, 23 | "broker": { 24 | "type": "url" 25 | }, 26 | "token": { 27 | "type": "string" 28 | }, 29 | "nodes": { 30 | "type": "path", 31 | "default": "nodes.json" 32 | }, 33 | "key": { 34 | "type": "path", 35 | "default": ".key" 36 | }, 37 | "log": { 38 | "type": "enum", 39 | "default": "info" 40 | }, 41 | "handler_class": { 42 | "type": "string", 43 | "default": "org.dsa.iot.dual.Main" 44 | }, 45 | "valuePersistenceEnabled": { 46 | "type": "bool", 47 | "value": true 48 | }, 49 | "qosPersistenceEnabled": { 50 | "type": "bool", 51 | "value": false 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sdk/commons/src/test/java/org/dsa/iot/commons/GuaranteedReceiverTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.commons; 2 | 3 | import org.dsa.iot.dslink.util.handler.Handler; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.concurrent.CountDownLatch; 8 | 9 | /** 10 | * @author Samuel Grenier 11 | */ 12 | public class GuaranteedReceiverTest { 13 | 14 | @Test(expected = IllegalStateException.class) 15 | public void receiverShutdownThrowsStateException() throws InterruptedException { 16 | GuaranteedReceiver gr = new GuaranteedReceiver(1) { 17 | @Override 18 | protected Object instantiate() throws Exception { 19 | return new Object(); 20 | } 21 | 22 | @Override 23 | protected boolean invalidateInstance(Exception e) { 24 | return false; 25 | } 26 | }; 27 | final CountDownLatch latch = new CountDownLatch(1); 28 | gr.get(new Handler() { 29 | @Override 30 | public void handle(Object event) { 31 | Assert.assertNotNull(event); 32 | latch.countDown(); 33 | } 34 | }, true); 35 | latch.await(); 36 | gr.shutdown(); 37 | gr.get(null, true); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/http/HttpClient.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.http; 2 | 3 | import org.dsa.iot.dslink.provider.HttpProvider; 4 | import org.dsa.iot.dslink.util.URLInfo; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * @author Samuel Grenier 11 | */ 12 | public class HttpClient { 13 | 14 | private final URLInfo url; 15 | 16 | public HttpClient(URLInfo url) { 17 | if (url == null) { 18 | throw new NullPointerException("url"); 19 | } 20 | this.url = url; 21 | } 22 | 23 | public HttpResp post(String uri, byte[] content) { 24 | HttpProvider provider = HttpProvider.getProvider(); 25 | Map headers = new HashMap<>(); 26 | { 27 | headers.put("connection", "close"); 28 | headers.put("accept-encoding", "text/plain"); 29 | headers.put("host", url.host); 30 | } 31 | 32 | URLInfo tmp = createUrlFromUri(url, uri); 33 | return provider.post(tmp, content, headers); 34 | } 35 | 36 | private static URLInfo createUrlFromUri(URLInfo url, String uri) { 37 | if (uri == null || uri.isEmpty()) { 38 | uri = "/"; 39 | } 40 | return new URLInfo(url.protocol, url.host, url.port, uri, url.secure); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/responses/CloseResponse.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.responses; 2 | 3 | import org.dsa.iot.dslink.methods.Response; 4 | import org.dsa.iot.dslink.methods.StreamState; 5 | import org.dsa.iot.dslink.util.json.JsonObject; 6 | 7 | /** 8 | * @author Samuel Grenier 9 | */ 10 | public class CloseResponse extends Response { 11 | 12 | private final int rid; 13 | private final Response response; 14 | 15 | public CloseResponse(int rid, Response response) { 16 | this.rid = rid; 17 | this.response = response; 18 | } 19 | 20 | @Override 21 | public int getRid() { 22 | return rid; 23 | } 24 | 25 | @Override 26 | public void populate(JsonObject in) { 27 | throw new UnsupportedOperationException(); 28 | } 29 | 30 | @Override 31 | public JsonObject getJsonResponse(JsonObject in) { 32 | if (response == null) { 33 | JsonObject obj = new JsonObject(); 34 | obj.put("rid", rid); 35 | obj.put("stream", StreamState.CLOSED.getJsonName()); 36 | return obj; 37 | } else { 38 | return response.getCloseResponse(); 39 | } 40 | } 41 | 42 | @Override 43 | public JsonObject getCloseResponse() { 44 | throw new UnsupportedOperationException(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/database/LoggingType.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.database; 2 | 3 | import java.util.LinkedHashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public enum LoggingType { 10 | ALL_DATA("All data"), 11 | NONE("None"), 12 | INTERVAL("Interval"), 13 | POINT_CHANGE("Point Change"); 14 | 15 | private final String name; 16 | 17 | LoggingType(String name) { 18 | this.name = name; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public static LoggingType toEnum(String s) { 26 | if (NONE.getName().equals(s)) { 27 | return NONE; 28 | } else if (ALL_DATA.getName().equals(s)) { 29 | return ALL_DATA; 30 | } else if (INTERVAL.getName().equals(s)) { 31 | return INTERVAL; 32 | } else if (POINT_CHANGE.getName().equals(s)) { 33 | return POINT_CHANGE; 34 | } else { 35 | throw new IllegalArgumentException("Invalid logging type: " + s); 36 | } 37 | } 38 | 39 | public static Set buildEnums() { 40 | Set enums = new LinkedHashSet<>(); 41 | for (LoggingType t : LoggingType.values()) { 42 | enums.add(t.getName()); 43 | } 44 | return enums; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/processor/stream/Stream.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.processor.stream; 2 | 3 | import org.dsa.iot.broker.processor.Responder; 4 | import org.dsa.iot.broker.server.client.Client; 5 | import org.dsa.iot.broker.utils.ParsedPath; 6 | import org.dsa.iot.dslink.methods.StreamState; 7 | import org.dsa.iot.dslink.util.json.JsonObject; 8 | 9 | /** 10 | * @author Samuel Grenier 11 | */ 12 | public abstract class Stream { 13 | 14 | private final Responder responder; 15 | private final ParsedPath path; 16 | 17 | public Stream(Responder responder, ParsedPath path) { 18 | this.responder = responder; 19 | this.path = path; 20 | } 21 | 22 | public final Responder responder() { 23 | return responder; 24 | } 25 | 26 | public final ParsedPath path() { 27 | return path; 28 | } 29 | 30 | public void close(Client requester, boolean write) { 31 | responder().stream().close(requester, this, write); 32 | } 33 | 34 | public abstract void add(Client requester, int requesterRid); 35 | 36 | public abstract void remove(Client requester); 37 | 38 | public abstract boolean isEmpty(); 39 | 40 | public abstract void dispatch(StreamState state, JsonObject response); 41 | 42 | public abstract void responderConnected(); 43 | 44 | public abstract void responderDisconnected(); 45 | } 46 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/provider/HttpProvider.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.provider; 2 | 3 | import org.dsa.iot.dslink.provider.netty.DefaultHttpProvider; 4 | import org.dsa.iot.dslink.util.URLInfo; 5 | import org.dsa.iot.dslink.util.http.HttpResp; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * @author Samuel Grenier 11 | */ 12 | public abstract class HttpProvider { 13 | 14 | private static HttpProvider PROVIDER; 15 | 16 | public static HttpProvider getProvider() { 17 | if (PROVIDER == null) { 18 | setProvider(new DefaultHttpProvider()); 19 | } 20 | return PROVIDER; 21 | } 22 | 23 | public static void setProvider(HttpProvider provider) { 24 | if (provider == null) { 25 | throw new NullPointerException("provider"); 26 | } 27 | PROVIDER = provider; 28 | } 29 | 30 | /** 31 | * Posts data to the designated address and immediately closes 32 | * the connection. 33 | * 34 | * @param url URL to post to. 35 | * @param content Content of the body or {@code null}. 36 | * @param headers Headers to send or {@code null}. 37 | * @return A response from the HTTP server. 38 | */ 39 | public abstract HttpResp post(URLInfo url, 40 | byte[] content, 41 | Map headers); 42 | } 43 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/actions/table/BatchRow.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.actions.table; 2 | 3 | import java.util.Collections; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * @author Samuel Grenier 9 | */ 10 | public class BatchRow { 11 | 12 | private List rows = new LinkedList<>(); 13 | private Modifier modifier; 14 | 15 | public void addRow(Row row) { 16 | rows.add(row); 17 | } 18 | 19 | public List getRows() { 20 | return Collections.unmodifiableList(rows); 21 | } 22 | 23 | public void setModifier(Modifier modifier) { 24 | this.modifier = modifier; 25 | } 26 | 27 | public Modifier getModifier() { 28 | return modifier; 29 | } 30 | 31 | public static class Modifier { 32 | 33 | private final String modifier; 34 | 35 | public Modifier(String modifier) { 36 | this.modifier = modifier; 37 | } 38 | 39 | public String get() { 40 | return modifier; 41 | } 42 | 43 | public static Modifier makeReplace(int start, int end) { 44 | return new Modifier("replace " + start + "-" + end); 45 | } 46 | 47 | @SuppressWarnings("unused") 48 | public static Modifier makeInsert(int pos) { 49 | return new Modifier("insert " + pos); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/Permission.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node; 2 | 3 | /** 4 | * Handles various permission levels 5 | * @author Samuel Grenier 6 | */ 7 | public enum Permission { 8 | NONE("none"), 9 | READ("read"), 10 | WRITE("write"), 11 | CONFIG("config"), 12 | NEVER("never"); 13 | 14 | private final String jsonName; 15 | 16 | Permission(String jsonName) { 17 | this.jsonName = jsonName; 18 | } 19 | 20 | /** 21 | * @return JSON ready name of the permission 22 | */ 23 | public String getJsonName() { 24 | return jsonName; 25 | } 26 | 27 | /** 28 | * Converts a string permission received from an endpoint back into a 29 | * permission enumeration. 30 | * 31 | * @param perm Permission string to convert. 32 | * @return Converted string into an enumeration. 33 | */ 34 | public static Permission toEnum(String perm) { 35 | switch (perm) { 36 | case "none": 37 | return NONE; 38 | case "read": 39 | return READ; 40 | case "write": 41 | return WRITE; 42 | case "config": 43 | return CONFIG; 44 | case "never": 45 | return NEVER; 46 | default: 47 | throw new RuntimeException("Unhandled type"); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/util/LogManagerTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | import org.dsa.iot.dslink.util.log.LogLevel; 4 | import org.dsa.iot.dslink.util.log.LogManager; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | /** 9 | * @author Samuel Grenier 10 | */ 11 | public class LogManagerTest { 12 | 13 | @Test 14 | public void logLevelSetting() { 15 | LogManager.setLevel("debug"); 16 | Assert.assertEquals(LogLevel.DEBUG, LogManager.getLevel()); 17 | 18 | LogManager.setLevel("info"); 19 | Assert.assertEquals(LogLevel.INFO, LogManager.getLevel()); 20 | 21 | LogManager.setLevel("warn"); 22 | Assert.assertEquals(LogLevel.WARN, LogManager.getLevel()); 23 | 24 | LogManager.setLevel("error"); 25 | Assert.assertEquals(LogLevel.ERROR, LogManager.getLevel()); 26 | 27 | LogManager.setLevel("none"); 28 | Assert.assertEquals(LogLevel.OFF, LogManager.getLevel()); 29 | } 30 | 31 | @Test(expected = RuntimeException.class) 32 | public void unknownLogLevel() { 33 | LogManager.setLevel("unknown"); 34 | } 35 | 36 | @Test(expected = NullPointerException.class) 37 | public void nullStringLogLevel() { 38 | LogManager.setLevel((String) null); 39 | } 40 | 41 | @Test(expected = NullPointerException.class) 42 | public void nullLevelLogLevel() { 43 | LogManager.setLevel((LogLevel) null); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sdk-dslink-java 2 | 3 | [![](https://jitpack.io/v/iot-dsa/sdk-dslink-java.svg)](https://jitpack.io/#iot-dsa/sdk-dslink-java) 4 | 5 | Java binding for the DSA API. 6 | 7 | ## Project Structure 8 | 9 | There are four categories that each Gradle subproject fall under. Those categories are: 10 | 11 | - sdk 12 | - examples 13 | - runtimes 14 | - internal 15 | 16 | Each categorized directory has its own README describing its category in more detail. 17 | 18 | ## Running the examples 19 | 20 | In order to run any examples a broker must be running. All the examples can quickly be ran through 21 | Gradle. 22 | 23 | Running the requester:
24 | `./gradlew :examples/requester:run -Dexec.args="-b http://localhost:8080/conn"` 25 | 26 | Running the responder:
27 | `./gradlew :examples/responder:run -Dexec.args="-b http://localhost:8080/conn"` 28 | 29 | ## Dependency Management 30 | 31 | Maven 32 | 33 | ``` 34 | 35 | 36 | jitpack.io 37 | https://jitpack.io 38 | 39 | 40 | 41 | 42 | com.github.iot-dsa 43 | sdk-dslink-java 44 | Tag 45 | 46 | ``` 47 | 48 | Gradle 49 | 50 | ``` 51 | allprojects { 52 | repositories { 53 | ... 54 | maven { url 'https://jitpack.io' } 55 | } 56 | } 57 | 58 | dependencies { 59 | implementation 'com.github.iot-dsa:sdk-dslink-java:Tag' 60 | } 61 | ``` -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/DeltaRollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class DeltaRollup extends Rollup { 9 | 10 | private Number previousStart; 11 | private Number start; 12 | private Number end; 13 | 14 | @Override 15 | public void reset() { 16 | previousStart = start; 17 | start = null; 18 | end = null; 19 | } 20 | 21 | @Override 22 | public void update(Value value, long ts) { 23 | Number number = value.getNumber(); 24 | if (number == null) { 25 | return; 26 | } 27 | 28 | if (start == null) { 29 | start = number; 30 | } 31 | if (previousStart == null) { 32 | previousStart = start; 33 | } 34 | end = number; 35 | } 36 | 37 | @Override 38 | public Value getValue() { 39 | if (previousStart != null && end != null) { 40 | double delta = end.doubleValue() - previousStart.doubleValue(); 41 | if (end.doubleValue() < previousStart.doubleValue()) { 42 | delta = end.doubleValue(); 43 | } 44 | return new Value(delta); 45 | } else if (previousStart != null) { 46 | return new Value(0); 47 | } else if (end != null) { 48 | return new Value(end); 49 | } 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/actions/ResultType.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.actions; 2 | 3 | /** 4 | * Handles invocation result types on a node. 5 | * 6 | * @author Samuel Grenier 7 | */ 8 | public enum ResultType { 9 | 10 | /** 11 | * Default invocation type. Infers static results of a single row 12 | * populated with the desired values 13 | */ 14 | VALUES("values"), 15 | 16 | /** 17 | * Streams results continuously. Infers highly dynamic results. 18 | */ 19 | STREAM("stream"), 20 | 21 | /** 22 | * Streams results continuously similarly to the {@link #STREAM} type. 23 | * While the results are dynamic, they differ in such that the response 24 | * stream ends when there is no more data to be read. 25 | */ 26 | TABLE("table"); 27 | 28 | private final String jsonName; 29 | 30 | ResultType(String jsonName) { 31 | this.jsonName = jsonName; 32 | } 33 | 34 | /** 35 | * @return The JSON name of the type. 36 | */ 37 | public String getJsonName() { 38 | return jsonName; 39 | } 40 | 41 | public static ResultType toEnum(String type) { 42 | switch (type) { 43 | case "values": 44 | return VALUES; 45 | case "stream": 46 | return STREAM; 47 | case "table": 48 | return TABLE; 49 | default: 50 | throw new RuntimeException("Unsupported type: " + type); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/value/SubscriptionValue.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.value; 2 | 3 | /** 4 | * @author Samuel Grenier 5 | */ 6 | public class SubscriptionValue { 7 | 8 | private final String path; 9 | private final Value value; 10 | private final Number count; 11 | private final Number sum; 12 | private final Number min; 13 | private final Number max; 14 | 15 | public SubscriptionValue(String path, Value value, 16 | Number count, Number sum, 17 | Number min, Number max) { 18 | this.path = path; 19 | this.value = value; 20 | this.count = count; 21 | this.sum = sum; 22 | this.min = min; 23 | this.max = max; 24 | } 25 | 26 | public String getPath() { 27 | return path; 28 | } 29 | 30 | /** 31 | * @return Timestamp of the value 32 | * @see Value#getTimeStamp() 33 | * @see Value#getTime() 34 | */ 35 | @Deprecated 36 | public String getTimestamp() { 37 | if (value == null) { 38 | return null; 39 | } 40 | return value.getTimeStamp(); 41 | } 42 | 43 | public Value getValue() { 44 | return value; 45 | } 46 | 47 | public Number getCount() { 48 | return count; 49 | } 50 | 51 | public Number getSum() { 52 | return sum; 53 | } 54 | 55 | public Number getMin() { 56 | return min; 57 | } 58 | 59 | public Number getMax() { 60 | return max; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/responses/UnsubscribeResponse.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.responses; 2 | 3 | import org.dsa.iot.dslink.DSLink; 4 | import org.dsa.iot.dslink.methods.Response; 5 | import org.dsa.iot.dslink.methods.StreamState; 6 | import org.dsa.iot.dslink.node.SubscriptionManager; 7 | import org.dsa.iot.dslink.util.json.JsonArray; 8 | import org.dsa.iot.dslink.util.json.JsonObject; 9 | 10 | /** 11 | * @author Samuel Grenier 12 | */ 13 | public class UnsubscribeResponse extends Response { 14 | 15 | private final int rid; 16 | private final SubscriptionManager manager; 17 | 18 | public UnsubscribeResponse(int rid, DSLink link) { 19 | this.rid = rid; 20 | this.manager = link.getSubscriptionManager(); 21 | } 22 | 23 | @Override 24 | public int getRid() { 25 | return rid; 26 | } 27 | 28 | @Override 29 | public void populate(JsonObject in) { 30 | } 31 | 32 | @Override 33 | public JsonObject getJsonResponse(JsonObject in) { 34 | JsonArray sids = in.get("sids"); 35 | if (sids != null && sids.size() > 0) { 36 | for (Object obj : sids) { 37 | Integer sid = (Integer) obj; 38 | manager.removeValueSub(sid); 39 | } 40 | } 41 | 42 | JsonObject obj = new JsonObject(); 43 | obj.put("rid", rid); 44 | obj.put("stream", StreamState.CLOSED.getJsonName()); 45 | return obj; 46 | } 47 | 48 | @Override 49 | public JsonObject getCloseResponse() { 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/handshake/LocalHandshakeTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.handshake; 2 | 3 | import org.dsa.iot.dslink.config.Configuration; 4 | import org.dsa.iot.dslink.connection.ConnectionType; 5 | import org.dsa.iot.dslink.util.json.JsonObject; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Tests the local handshake json information 11 | * 12 | * @author Samuel Grenier 13 | */ 14 | public class LocalHandshakeTest { 15 | 16 | /** 17 | * Ensures the proper json is outputted in the handshake that needs to be 18 | * sent to the server. 19 | */ 20 | @Test 21 | public void ensureFieldsExist() { 22 | Configuration config = new Configuration(); 23 | config.setConnectionType(ConnectionType.WEB_SOCKET); 24 | config.setAuthEndpoint("http://localhost"); 25 | config.setKeys(LocalKeys.generate()); 26 | config.setDsId("test"); 27 | config.setResponder(true); 28 | 29 | LocalHandshake handshake = new LocalHandshake(config); 30 | 31 | JsonObject object = handshake.toJson(); 32 | Assert.assertNotNull(object); 33 | 34 | Assert.assertFalse(object.contains("zone")); 35 | config.setZone("testZone"); 36 | handshake = new LocalHandshake(config); 37 | object = handshake.toJson(); 38 | Assert.assertTrue(object.contains("zone")); 39 | 40 | Assert.assertTrue(object.contains("publicKey")); 41 | Assert.assertTrue(object.contains("isRequester")); 42 | Assert.assertTrue(object.contains("isResponder")); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/Main.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.container; 2 | 3 | import org.dsa.iot.container.manager.DSLinkManager; 4 | import org.dsa.iot.container.security.SecMan; 5 | import org.dsa.iot.container.stdin.LinkHandler; 6 | import org.dsa.iot.container.stdin.StdinHandler; 7 | import org.dsa.iot.container.stdin.UserHandler; 8 | import org.dsa.iot.shared.SharedObjects; 9 | 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | 13 | /** 14 | * @author Samuel Grenier 15 | */ 16 | public class Main { 17 | 18 | public static void main(String[] args) { 19 | Args parsed = Args.parse(args); 20 | if (parsed == null) { 21 | return; 22 | } 23 | 24 | // Set the security manager 25 | System.setSecurityManager(new SecMan()); 26 | 27 | // Start up global thread pools on main 28 | SharedObjects.getLoop(); 29 | SharedObjects.getDaemonThreadPool().prestartAllCoreThreads(); 30 | SharedObjects.getThreadPool().prestartAllCoreThreads(); 31 | 32 | DSLinkManager manager = new DSLinkManager(); 33 | String dslinksFolder = parsed.getDslinksFolder(); 34 | String token = parsed.getToken(); 35 | 36 | LinkHandler handler; 37 | if (dslinksFolder != null) { 38 | String brokerUrl = parsed.getBrokerUrl(); 39 | Path folder = Paths.get(dslinksFolder); 40 | handler = new UserHandler(manager, folder, brokerUrl, token); 41 | } else { 42 | handler = new StdinHandler(manager); 43 | } 44 | handler.start(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/wrapper/log/LogManager.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.container.wrapper.log; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class LogManager { 10 | 11 | private final ClassLoader loader; 12 | 13 | public LogManager(ClassLoader loader) { 14 | if (loader == null) { 15 | throw new NullPointerException("loader"); 16 | } 17 | this.loader = loader; 18 | } 19 | 20 | public void configure(String logPath) { 21 | try { 22 | if (logPath != null) { 23 | Class managerClass = getManagerClass(); 24 | Method method = managerClass.getMethod("configure", File.class); 25 | method.invoke(null, new File(logPath)); 26 | } 27 | } catch (Exception e) { 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | 32 | public void setLevel(String level) { 33 | try { 34 | Class managerClass = getManagerClass(); 35 | Method method = managerClass.getMethod("setLevel", String.class); 36 | method.invoke(null, level); 37 | } catch (Exception e) { 38 | throw new RuntimeException(e); 39 | } 40 | } 41 | 42 | private Class getManagerClass() { 43 | String logManagerClass = "org.dsa.iot.dslink.util.log.LogManager"; 44 | try { 45 | return loader.loadClass(logManagerClass); 46 | } catch (ClassNotFoundException e) { 47 | throw new RuntimeException(e); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/requests/InvokeRequest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.requests; 2 | 3 | import org.dsa.iot.dslink.methods.Request; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * Used to invoke an action on a node. 8 | * 9 | * @author Samuel Grenier 10 | */ 11 | public class InvokeRequest extends Request { 12 | 13 | private final String path; 14 | private final JsonObject params; 15 | private boolean waitForStreamClose; 16 | 17 | public InvokeRequest(String path) { 18 | this(path, null); 19 | } 20 | 21 | public InvokeRequest(String path, JsonObject params) { 22 | if (path == null) { 23 | throw new NullPointerException("path"); 24 | } 25 | this.params = params; 26 | this.path = path; 27 | } 28 | 29 | @Override 30 | public String getName() { 31 | return "invoke"; 32 | } 33 | 34 | public String getPath() { 35 | return path; 36 | } 37 | 38 | /** 39 | * The invocation response handler will not be called until the stream 40 | * for the invocation is closed, if {@code wait} is {@code true}. 41 | * 42 | * @param wait Whether to wait or not. 43 | */ 44 | @SuppressWarnings("unused") 45 | public void setWaitForStreamClose(boolean wait) { 46 | this.waitForStreamClose = wait; 47 | } 48 | 49 | public boolean waitForStreamClose() { 50 | return waitForStreamClose; 51 | } 52 | 53 | @Override 54 | public void addJsonValues(JsonObject out) { 55 | out.put("path", path); 56 | if (params != null) { 57 | out.put("params", params); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/utils/RequestGenerator.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.utils; 2 | 3 | import org.dsa.iot.dslink.util.json.JsonArray; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class RequestGenerator { 10 | 11 | public static JsonArray list(ParsedPath path, int rid) { 12 | JsonObject req = new JsonObject(); 13 | req.put("method", "list"); 14 | req.put("rid", rid); 15 | req.put("path", path.base()); 16 | 17 | JsonArray reqs = new JsonArray(); 18 | reqs.add(req); 19 | return reqs; 20 | } 21 | 22 | public static JsonArray subscribe(ParsedPath path, int sid, int rid) { 23 | JsonObject req = new JsonObject(); 24 | req.put("rid", rid); 25 | req.put("method", "subscribe"); 26 | { 27 | JsonArray paths = new JsonArray(); 28 | { 29 | JsonObject obj = new JsonObject(); 30 | obj.put("path", path.base()); 31 | obj.put("sid", sid); 32 | paths.add(obj); 33 | } 34 | req.put("paths", paths); 35 | } 36 | 37 | JsonArray reqs = new JsonArray(); 38 | reqs.add(req); 39 | return reqs; 40 | } 41 | 42 | public static JsonArray unsubscribe(int rid, int sid) { 43 | JsonObject req = new JsonObject(); 44 | req.put("rid", rid); 45 | req.put("method", "unsubscribe"); 46 | JsonArray sids = new JsonArray(); 47 | sids.add(sid); 48 | req.put("sids", sids); 49 | 50 | JsonArray reqs = new JsonArray(); 51 | reqs.add(req); 52 | return reqs; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/config/broker/BrokerMemoryConfig.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.config.broker; 2 | 3 | import org.dsa.iot.dslink.util.json.JsonObject; 4 | 5 | /** 6 | * Broker configuration stored in memory. 7 | * 8 | * @author Samuel Grenier 9 | */ 10 | public class BrokerMemoryConfig extends BrokerConfig { 11 | 12 | private JsonObject opts; 13 | 14 | @Override 15 | public JsonObject get() { 16 | return opts; 17 | } 18 | 19 | @Override 20 | public void readAndUpdate() { 21 | opts = new JsonObject(); 22 | addDefaultOpts(); 23 | } 24 | 25 | protected void addDefaultOpts() { 26 | addServerOpts(); 27 | addBrokerOpts(); 28 | } 29 | 30 | protected void addServerOpts() { 31 | JsonObject server = new JsonObject(); 32 | { 33 | JsonObject http = new JsonObject(); 34 | http.put("enabled", true); 35 | http.put("host", "0.0.0.0"); 36 | http.put("port", 8080); 37 | server.put("http", http); 38 | } 39 | { 40 | JsonObject https = new JsonObject(); 41 | https.put("enabled", false); 42 | https.put("host", "0.0.0.0"); 43 | https.put("port", 8443); 44 | https.put("certChainFile", null); 45 | https.put("certKeyFile", null); 46 | https.put("certKeyPass", null); 47 | server.put("https", https); 48 | } 49 | opts.put("server", server); 50 | } 51 | 52 | protected void addBrokerOpts() { 53 | JsonObject broker = new JsonObject(); 54 | broker.put("downstreamName", "downstream"); 55 | opts.put("broker", broker); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/responses/RemoveResponse.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.responses; 2 | 3 | import org.dsa.iot.dslink.methods.Response; 4 | import org.dsa.iot.dslink.methods.StreamState; 5 | import org.dsa.iot.dslink.node.NodePair; 6 | import org.dsa.iot.dslink.util.json.JsonObject; 7 | 8 | /** 9 | * @author Samuel Grenier 10 | */ 11 | public class RemoveResponse extends Response { 12 | 13 | private final int rid; 14 | private final NodePair pair; 15 | 16 | public RemoveResponse(int rid, NodePair pair) { 17 | if (pair.getReference() == null) { 18 | String err = "path does not reference config or attribute"; 19 | throw new NullPointerException(err); 20 | } 21 | this.rid = rid; 22 | this.pair = pair; 23 | } 24 | 25 | @Override 26 | public int getRid() { 27 | return rid; 28 | } 29 | 30 | @Override 31 | public void populate(JsonObject in) { 32 | } 33 | 34 | @Override 35 | public JsonObject getJsonResponse(JsonObject in) { 36 | removeConfig(); 37 | 38 | JsonObject obj = new JsonObject(); 39 | obj.put("rid", rid); 40 | obj.put("stream", StreamState.CLOSED.getJsonName()); 41 | return obj; 42 | } 43 | 44 | @Override 45 | public JsonObject getCloseResponse() { 46 | return null; 47 | } 48 | 49 | private void removeConfig() { 50 | String ref = pair.getReference(); 51 | if (ref.startsWith("@")) { 52 | ref = ref.substring(1); 53 | pair.getNode().removeAttribute(ref); 54 | } else { 55 | throw new RuntimeException("Unable to set reference: " + ref); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/log/LoggingBridge.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.log; 2 | 3 | import org.slf4j.ILoggerFactory; 4 | import org.slf4j.impl.Level; 5 | import org.slf4j.impl.LoggerFactoryImpl; 6 | import org.slf4j.impl.StaticLoggerBinder; 7 | 8 | import java.io.File; 9 | 10 | /** 11 | * @author Samuel Grenier 12 | */ 13 | public class LoggingBridge implements LogBridge { 14 | 15 | private LogLevel level; 16 | 17 | @Override 18 | public void configure(File path) { 19 | getLoggerFactory().setLogPath(path); 20 | } 21 | 22 | @Override 23 | public void setLevel(LogLevel level) { 24 | LoggerFactoryImpl logger = getLoggerFactory(); 25 | switch (level) { 26 | case OFF: 27 | logger.setLogLevel(Level.OFF); 28 | break; 29 | case ERROR: 30 | logger.setLogLevel(Level.ERROR); 31 | break; 32 | case WARN: 33 | logger.setLogLevel(Level.WARN); 34 | break; 35 | case INFO: 36 | logger.setLogLevel(Level.INFO); 37 | break; 38 | case DEBUG: 39 | logger.setLogLevel(Level.DEBUG); 40 | break; 41 | case TRACE: 42 | logger.setLogLevel(Level.TRACE); 43 | } 44 | this.level = level; 45 | } 46 | 47 | @Override 48 | public LogLevel getLevel() { 49 | return level; 50 | } 51 | 52 | private LoggerFactoryImpl getLoggerFactory() { 53 | StaticLoggerBinder binder = StaticLoggerBinder.getSingleton(); 54 | ILoggerFactory factory = binder.getLoggerFactory(); 55 | return (LoggerFactoryImpl) factory; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/config/broker/BrokerFileConfig.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.config.broker; 2 | 3 | import org.dsa.iot.dslink.util.FileUtils; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | /** 10 | * @author Samuel Grenier 11 | */ 12 | public class BrokerFileConfig extends BrokerConfig { 13 | 14 | private final File path; 15 | private JsonObject opts; 16 | 17 | public BrokerFileConfig() { 18 | this("server.json"); 19 | } 20 | 21 | public BrokerFileConfig(String file) { 22 | if (file == null || file.isEmpty()) { 23 | throw new IllegalArgumentException("file"); 24 | } 25 | this.path = new File(file); 26 | } 27 | 28 | @Override 29 | public JsonObject get() { 30 | return opts; 31 | } 32 | 33 | @Override 34 | public void readAndUpdate() { 35 | opts = getDefaultConfig(); 36 | if (path.exists()) { 37 | try { 38 | byte[] bytes = FileUtils.readAllBytes(path); 39 | opts.mergeIn(new JsonObject(new String(bytes, "UTF-8")), true); 40 | } catch (IOException e) { 41 | throw new RuntimeException(e); 42 | } 43 | } 44 | writeConfig(opts); 45 | } 46 | 47 | protected void writeConfig(JsonObject opts) { 48 | try { 49 | byte[] data = opts.encodePrettily(); 50 | FileUtils.write(path, data); 51 | } catch (IOException e) { 52 | throw new RuntimeException(e); 53 | } 54 | } 55 | 56 | protected JsonObject getDefaultConfig() { 57 | BrokerConfig conf = new BrokerMemoryConfig(); 58 | conf.readAndUpdate(); 59 | return conf.get(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/node/NodeManagerTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node; 2 | 3 | import org.dsa.iot.dslink.node.exceptions.NoSuchPathException; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | /** 8 | * Tests the node manager. 9 | * 10 | * @author Samuel Grenier 11 | */ 12 | public class NodeManagerTest { 13 | 14 | /** 15 | * Tests node additions to the node manager. Also exercises node path 16 | * building when retrieving nodes. 17 | */ 18 | @Test 19 | public void nodeAdditions() { 20 | NodeManager manager = new NodeManager(null, "node"); 21 | manager.createRootNode("A", "node").build(); 22 | 23 | Assert.assertNotNull(manager.getNode("A")); 24 | Assert.assertNotNull(manager.getNode("/A")); 25 | Assert.assertNotNull(manager.getNode("/A/")); 26 | 27 | manager.createRootNode("A").build().createChild("B").build(); 28 | Assert.assertNotNull(manager.getNode("/A/B")); 29 | } 30 | 31 | /** 32 | * Tests that node removals are completely out of the node manager. 33 | */ 34 | @Test(expected = NoSuchPathException.class) 35 | public void nodeRemovals() { 36 | NodeManager manager = new NodeManager(null, "node"); 37 | manager.createRootNode("A", "node"); 38 | manager.getSuperRoot().removeChild("A"); 39 | manager.getNode("/A"); 40 | } 41 | 42 | @Test(expected = NullPointerException.class) 43 | public void nullPath() { 44 | NodeManager manager = new NodeManager(null, "node"); 45 | manager.getNode(null); 46 | } 47 | 48 | @Test(expected = IllegalArgumentException.class) 49 | public void badPath() { 50 | NodeManager manager = new NodeManager(null, "node"); 51 | manager.getNode("/A//"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/actions/EditorType.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.actions; 2 | 3 | import org.dsa.iot.dslink.util.json.JsonObject; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public class EditorType { 9 | 10 | public static final EditorType TEXT_AREA = new EditorType("textarea"); 11 | public static final EditorType PASSWORD = new EditorType("password"); 12 | public static final EditorType DATE_RANGE = new EditorType("daterange"); 13 | public static final EditorType DATE = new EditorType("date"); 14 | public static final EditorType FILE_INPUT = new EditorType("fileinput"); 15 | 16 | private final String type; 17 | private final JsonObject meta; 18 | 19 | private EditorType(String type, JsonObject meta) { 20 | this.type = type; 21 | this.meta = meta; 22 | } 23 | 24 | private EditorType(String type) { 25 | this(type, null); 26 | } 27 | 28 | public String toJsonString() { 29 | return type; 30 | } 31 | 32 | /** 33 | * In case an editor type is not supported, it can easily be created using 34 | * this factory method. 35 | * 36 | * @param type Type of editor. 37 | * @return An editor type. 38 | */ 39 | public static EditorType make(String type) { 40 | return new EditorType(type); 41 | } 42 | 43 | /** 44 | * In case an editor type is not supported, it can easily be created using 45 | * this factory method. 46 | * 47 | * @param type Type of editor. 48 | * @param meta Metadata for the editor. 49 | * @return An editor type. 50 | */ 51 | public static EditorType make(String type, JsonObject meta) { 52 | return new EditorType(type, meta); 53 | } 54 | 55 | public JsonObject getMeta() { 56 | return meta; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/config/Arguments.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.config; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import com.beust.jcommander.Parameter; 5 | import com.beust.jcommander.ParameterException; 6 | import com.beust.jcommander.Parameters; 7 | 8 | /** 9 | * @author Samuel Grenier 10 | */ 11 | @Parameters(separators = "= ") 12 | public class Arguments { 13 | 14 | private JCommander jc; 15 | 16 | @Parameter(names = { "--server", "-s" }, 17 | description = "Starts the broker server") 18 | private boolean server = false; 19 | 20 | @Parameter(names = { "--log", "-l"}, 21 | description = "Sets the log level", 22 | arity = 1) 23 | private String log = "info"; 24 | 25 | @Parameter(names = { "--help", "-h" }, 26 | description = "Displays the help menu", 27 | help = true) 28 | private boolean help = false; 29 | 30 | public boolean runServer() { 31 | return server; 32 | } 33 | 34 | public String getLogLevel() { 35 | return log; 36 | } 37 | 38 | /** 39 | * Parses the arguments. 40 | * 41 | * @param args Arguments to parse. 42 | * @return Whether parsing was successful or not. 43 | */ 44 | public boolean parse(String[] args) { 45 | try { 46 | jc = new JCommander(this, args); 47 | jc.setProgramName(""); 48 | if (help) { 49 | jc.usage(); 50 | return false; 51 | } 52 | return true; 53 | } catch (ParameterException pe) { 54 | System.out.println("Use --help or -h to get usage help"); 55 | System.out.println(pe.getMessage()); 56 | } 57 | return false; 58 | } 59 | 60 | public void displayHelp() { 61 | jc.usage(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/requests/SubscribeRequest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.requests; 2 | 3 | import org.dsa.iot.dslink.methods.Request; 4 | import org.dsa.iot.dslink.util.SubData; 5 | import org.dsa.iot.dslink.util.json.JsonArray; 6 | import org.dsa.iot.dslink.util.json.JsonObject; 7 | 8 | import java.util.Collections; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | /** 13 | * @author Samuel Grenier 14 | */ 15 | public class SubscribeRequest extends Request { 16 | 17 | private Map subSids; 18 | private final Set paths; 19 | 20 | /** 21 | * @param paths Paths to subscribe to. 22 | */ 23 | public SubscribeRequest(Set paths) { 24 | if (paths == null) { 25 | throw new NullPointerException("paths"); 26 | } 27 | this.paths = paths; 28 | } 29 | 30 | public Set getPaths() { 31 | return Collections.unmodifiableSet(paths); 32 | } 33 | 34 | public void setSubSids(Map sids) { 35 | this.subSids = sids; 36 | } 37 | 38 | @Override 39 | public String getName() { 40 | return "subscribe"; 41 | } 42 | 43 | @Override 44 | public void addJsonValues(JsonObject out) { 45 | JsonArray array = new JsonArray(); 46 | for (Map.Entry sub : subSids.entrySet()) { 47 | SubData data = sub.getKey(); 48 | String path = data.getPath(); 49 | Integer qos = data.getQos(); 50 | 51 | JsonObject obj = new JsonObject(); 52 | obj.put("path", path); 53 | obj.put("sid", sub.getValue()); 54 | if (qos != null) { 55 | obj.put("qos", qos); 56 | } 57 | array.add(obj); 58 | } 59 | out.put("paths", array); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/util/FileUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | 10 | /** 11 | * @author Samuel Grenier 12 | */ 13 | public class FileUtilsTest { 14 | 15 | @Before 16 | public void setup() { 17 | // Coverage 18 | new FileUtils(); 19 | } 20 | 21 | @Test 22 | @SuppressWarnings("unused") 23 | public void write() throws IOException { 24 | File file = File.createTempFile("test", "tmp"); 25 | Assert.assertTrue(file.isFile()); 26 | try { 27 | FileUtils.write(file, "Hello world".getBytes("UTF-8")); 28 | 29 | byte[] data = FileUtils.readAllBytes(file); 30 | Assert.assertNotNull(data); 31 | 32 | String s = new String(data, "UTF-8"); 33 | Assert.assertEquals("Hello world", s); 34 | } finally { 35 | boolean ignored = file.delete(); 36 | } 37 | } 38 | 39 | @Test 40 | @SuppressWarnings({"unused", "UnusedAssignment"}) 41 | public void copy() throws IOException { 42 | File a = File.createTempFile("file-1", "tmp"); 43 | File b = File.createTempFile("file-2", "tmp"); 44 | 45 | Assert.assertTrue(a.isFile()); 46 | Assert.assertTrue(b.isFile()); 47 | 48 | try { 49 | FileUtils.write(a, "Hello world".getBytes("UTF-8")); 50 | FileUtils.copy(a, b); 51 | 52 | byte[] data = FileUtils.readAllBytes(b); 53 | Assert.assertNotNull(data); 54 | 55 | String s = new String(data, "UTF-8"); 56 | Assert.assertEquals("Hello world", s); 57 | } finally { 58 | boolean ignored = a.delete(); 59 | ignored = b.delete(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/node/BrokerTree.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.node; 2 | 3 | import org.dsa.iot.broker.server.client.Client; 4 | import org.dsa.iot.broker.utils.ParsedPath; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class BrokerTree { 10 | 11 | private final BrokerNode root = new BrokerNode<>(null, null, "dsa/broker"); 12 | private Downstream downstream; 13 | 14 | public void initialize(String downStreamName) { 15 | initialize(new Downstream(root, downStreamName)); 16 | } 17 | 18 | public void initialize(Downstream downstream) { 19 | this.downstream = downstream; 20 | root.addChild(downstream); 21 | } 22 | 23 | public BrokerNode getRoot() { 24 | return root; 25 | } 26 | 27 | public BrokerNode getNode(ParsedPath path) { 28 | BrokerNode node = getRoot(); 29 | { 30 | String[] split = path.split(); 31 | for (String name : split) { 32 | BrokerNode tmp = node.getChild(name); 33 | if (tmp == null) { 34 | if (!path.isRemote()) { 35 | node = null; 36 | } 37 | break; 38 | } 39 | node = tmp; 40 | } 41 | } 42 | return node != null && node.accessible() ? node : null; 43 | } 44 | 45 | public String initDslink(String name, String dsId) { 46 | return downstream.init(name, dsId); 47 | } 48 | 49 | public void connected(Client client) { 50 | root.connected(client); 51 | root.propagateConnected(client); 52 | client.node(downstream.getChild(client.handshake().name())); 53 | } 54 | 55 | public void disconnected(Client client) { 56 | root.disconnected(client); 57 | root.propagateDisconnected(client); 58 | client.node(null); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /internal/logging/src/main/java/org/slf4j/impl/LoggerFactoryImpl.java: -------------------------------------------------------------------------------- 1 | package org.slf4j.impl; 2 | 3 | import org.slf4j.ILoggerFactory; 4 | import org.slf4j.Logger; 5 | 6 | import java.io.File; 7 | import java.io.FileOutputStream; 8 | import java.io.OutputStream; 9 | import java.io.PrintStream; 10 | 11 | /** 12 | * @author Samuel Grenier 13 | */ 14 | public class LoggerFactoryImpl implements ILoggerFactory { 15 | 16 | private Level logLevel = Level.INFO; 17 | private PrintStream stream = System.out; 18 | private boolean shouldClose = false; 19 | 20 | public LoggerFactoryImpl() { 21 | Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 22 | @Override 23 | public void run() { 24 | if (!shouldClose) { 25 | return; 26 | } 27 | stream.close(); 28 | } 29 | })); 30 | } 31 | 32 | public void setLogPath(File logPath) { 33 | if (logPath == null) { 34 | shouldClose = false; 35 | stream = System.out; 36 | return; 37 | } 38 | 39 | try { 40 | if (shouldClose) { 41 | stream.close(); 42 | } 43 | shouldClose = true; 44 | boolean exists = logPath.exists(); 45 | OutputStream out = new FileOutputStream(logPath, exists); 46 | stream = new PrintStream(out, false, "UTF-8"); 47 | } catch (Exception e) { 48 | throw new RuntimeException(e); 49 | } 50 | } 51 | 52 | public PrintStream getPrintStream() { 53 | return stream; 54 | } 55 | 56 | public void setLogLevel(Level level) { 57 | this.logLevel = level; 58 | } 59 | 60 | public Level getLogLevel() { 61 | return logLevel; 62 | } 63 | 64 | @Override 65 | public Logger getLogger(String name) { 66 | return new LoggerImpl(this, name); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/Response.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods; 2 | 3 | import org.dsa.iot.dslink.methods.responses.ErrorResponse; 4 | import org.dsa.iot.dslink.util.json.JsonObject; 5 | 6 | /** 7 | * Generic response API 8 | * 9 | * @author Samuel Grenier 10 | */ 11 | public abstract class Response { 12 | 13 | private ErrorResponse error; 14 | 15 | /** 16 | * Sets the error that occurred in the originating request. 17 | * 18 | * @param error Error to set. 19 | */ 20 | public void setError(ErrorResponse error) { 21 | this.error = error; 22 | } 23 | 24 | /** 25 | * @return The originating error that was set or {@code null} if there was 26 | * no error. 27 | */ 28 | public ErrorResponse getError() { 29 | return error; 30 | } 31 | 32 | /** 33 | * @return Whether or not an error has occurred. 34 | */ 35 | public boolean hasError() { 36 | return error != null; 37 | } 38 | 39 | /** 40 | * @return Request ID of the response 41 | */ 42 | public abstract int getRid(); 43 | 44 | /** 45 | * Populates the response with the incoming data. 46 | * 47 | * @param in Incoming data from remote endpoint 48 | */ 49 | public abstract void populate(JsonObject in); 50 | 51 | /** 52 | * Retrieves a response object based on the incoming request. 53 | * 54 | * @param in Original incoming data 55 | * @return JSON response 56 | */ 57 | public abstract JsonObject getJsonResponse(JsonObject in); 58 | 59 | /** 60 | * Closes the stream if it is open and writes the closed stream response 61 | * to the client. This response should then go into a closed state. 62 | * 63 | * @return Closed state response information 64 | * @see org.dsa.iot.dslink.methods.responses.CloseResponse 65 | */ 66 | public abstract JsonObject getCloseResponse(); 67 | } 68 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/Objects.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | import org.dsa.iot.shared.SharedObjects; 4 | 5 | import java.util.concurrent.ScheduledThreadPoolExecutor; 6 | 7 | /** 8 | * Miscellaneous global fields. 9 | * 10 | * @author Samuel Grenier 11 | */ 12 | public class Objects { 13 | 14 | private static volatile ScheduledThreadPoolExecutor THREAD_POOL; 15 | private static volatile ScheduledThreadPoolExecutor DAEMON_THREAD_POOL; 16 | 17 | @SuppressWarnings("unused") 18 | public static ScheduledThreadPoolExecutor createThreadPool() { 19 | return createThreadPool(SharedObjects.POOL_SIZE); 20 | } 21 | 22 | public static ScheduledThreadPoolExecutor createThreadPool(int size) { 23 | return SharedObjects.createThreadPool(size); 24 | } 25 | 26 | public static ScheduledThreadPoolExecutor getThreadPool() { 27 | if (THREAD_POOL == null) { 28 | THREAD_POOL = SharedObjects.getThreadPool(); 29 | } 30 | return THREAD_POOL; 31 | } 32 | 33 | @SuppressWarnings("unused") 34 | public static void setThreadPool(ScheduledThreadPoolExecutor stpe) { 35 | THREAD_POOL = stpe; 36 | } 37 | 38 | public static ScheduledThreadPoolExecutor createDaemonThreadPool() { 39 | return createDaemonThreadPool(SharedObjects.POOL_SIZE); 40 | } 41 | 42 | public static ScheduledThreadPoolExecutor createDaemonThreadPool(int size) { 43 | return SharedObjects.createDaemonThreadPool(size); 44 | } 45 | 46 | public static ScheduledThreadPoolExecutor getDaemonThreadPool() { 47 | if (DAEMON_THREAD_POOL == null) { 48 | DAEMON_THREAD_POOL = SharedObjects.getDaemonThreadPool(); 49 | } 50 | return DAEMON_THREAD_POOL; 51 | } 52 | 53 | @SuppressWarnings("unused") 54 | public static void setDaemonThreadPool(ScheduledThreadPoolExecutor stpe) { 55 | DAEMON_THREAD_POOL = stpe; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sdk/commons/src/test/java/org/dsa/iot/commons/ParameterizedActionTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.commons; 2 | 3 | import org.dsa.iot.dslink.node.NodeManager; 4 | import org.dsa.iot.dslink.node.Permission; 5 | import org.dsa.iot.dslink.node.actions.ActionResult; 6 | import org.dsa.iot.dslink.node.value.Value; 7 | import org.dsa.iot.dslink.node.value.ValueType; 8 | import org.dsa.iot.dslink.util.handler.Handler; 9 | import org.dsa.iot.dslink.util.json.JsonObject; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | 13 | import java.util.Map; 14 | 15 | import static org.dsa.iot.commons.ParameterizedAction.ParameterInfo; 16 | 17 | /** 18 | * @author Samuel Grenier 19 | */ 20 | public class ParameterizedActionTest { 21 | 22 | @Test 23 | public void validateTest() { 24 | NodeManager manager = new NodeManager(null, null); 25 | ParameterizedAction a = new ParameterizedAction(Permission.READ) { 26 | @Override 27 | public void handle(ActionResult actRes, 28 | Map params) { 29 | Value value = params.get("test"); 30 | Assert.assertTrue(value.getBool()); 31 | } 32 | }; 33 | { 34 | ParameterInfo info = new ParameterInfo("test", ValueType.BOOL); 35 | info.setDefaultValue(new Value(false)); 36 | info.setOptional(true); 37 | info.setPersistent(true); 38 | info.setValidator(new Handler() { 39 | @Override 40 | public void handle(Value event) { 41 | event.set(true); 42 | } 43 | }); 44 | a.addParameter(info); 45 | } 46 | 47 | JsonObject params = new JsonObject(); 48 | params.put("test", false); 49 | 50 | JsonObject in = new JsonObject(); 51 | in.put("params", params); 52 | 53 | a.invoke(new ActionResult(manager.getSuperRoot(), in)); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /runtimes/container/src/main/java/org/dsa/iot/container/Args.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.container; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import com.beust.jcommander.Parameter; 5 | import com.beust.jcommander.ParameterException; 6 | import com.beust.jcommander.Parameters; 7 | 8 | /** 9 | * @author Samuel Grenier 10 | */ 11 | @Parameters(separators = "= ") 12 | public class Args { 13 | 14 | @Parameter(names = { "--dslinks", "-d" }, 15 | description = "DSLinks folder to run as a standalone", 16 | arity = 1) 17 | private String dslinksFolder; 18 | 19 | @Parameter(names = { "--broker", "-b"}, 20 | description = "Broker URL to connect to, only works in standalone mode", 21 | arity = 1) 22 | private String brokerUrl; 23 | 24 | @Parameter(names = { "--token", "-t" }, 25 | description = "Sets the token used when connecting to the broker", 26 | arity = 1) 27 | private String token; 28 | 29 | @Parameter(names = { "--help", "-h" }, 30 | description = "Displays the help menu", 31 | help = true) 32 | private boolean help = false; 33 | 34 | public String getDslinksFolder() { 35 | return dslinksFolder; 36 | } 37 | 38 | public String getBrokerUrl() { 39 | return brokerUrl; 40 | } 41 | 42 | public String getToken() { 43 | return token; 44 | } 45 | 46 | public static Args parse(String[] args) { 47 | try { 48 | Args parsed = new Args(); 49 | JCommander jc = new JCommander(parsed, args); 50 | jc.setProgramName(""); 51 | if (parsed.help) { 52 | jc.usage(); 53 | return null; 54 | } 55 | return parsed; 56 | } catch (ParameterException pe) { 57 | System.out.println("Use --help or -h to get usage help"); 58 | System.out.println(pe.getMessage()); 59 | return null; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/responder/src/main/java/org/dsa/iot/responder/Main.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.responder; 2 | 3 | import org.dsa.iot.dslink.DSLink; 4 | import org.dsa.iot.dslink.DSLinkFactory; 5 | import org.dsa.iot.dslink.DSLinkHandler; 6 | import org.dsa.iot.dslink.node.Node; 7 | import org.dsa.iot.dslink.node.NodeManager; 8 | import org.dsa.iot.dslink.util.json.JsonObject; 9 | import org.dsa.iot.responder.rng.RNG; 10 | import org.dsa.iot.responder.rng.TableActions; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | /** 15 | * @author Samuel Grenier 16 | */ 17 | public class Main extends DSLinkHandler { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); 20 | 21 | @Override 22 | public JsonObject getLinkData() { 23 | JsonObject obj = new JsonObject(); 24 | obj.put("exampleResponder", true); 25 | return obj; 26 | } 27 | 28 | @Override 29 | public boolean isResponder() { 30 | return true; 31 | } 32 | 33 | @Override 34 | public void preInit() { 35 | String path = getWorkingDir().getAbsolutePath(); 36 | LOGGER.info("Current working directory: {}", path); 37 | } 38 | 39 | @Override 40 | public void onResponderInitialized(DSLink link) { 41 | NodeManager manager = link.getNodeManager(); 42 | Node superRoot = manager.getSuperRoot(); 43 | 44 | Replicator.start(superRoot); 45 | RNG.init(superRoot); 46 | Values.init(superRoot); 47 | Echo.init(superRoot); 48 | TableActions.init(superRoot); 49 | LOGGER.info("Responder initialized"); 50 | } 51 | 52 | @Override 53 | public void onResponderConnected(DSLink link) { 54 | LOGGER.info("Responder connected"); 55 | } 56 | 57 | @Override 58 | public void onResponderDisconnected(DSLink link) { 59 | LOGGER.info("Oh no! The connected to the broker was lost"); 60 | } 61 | 62 | public static void main(String[] args) { 63 | DSLinkFactory.start(args, new Main()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/node/NodeTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Tests the node API. 8 | * 9 | * @author Samuel Grenier 10 | */ 11 | public class NodeTest { 12 | 13 | /** 14 | * Ensures the names are accurate 15 | */ 16 | @Test 17 | public void nameTest() { 18 | Node node = new Node("Test", null, null); 19 | Assert.assertEquals("Test", node.getName()); 20 | Assert.assertNotEquals("test", node.getName()); 21 | Assert.assertNull(node.getDisplayName()); 22 | } 23 | 24 | @Test 25 | public void profileTest() { 26 | Node node = new Node("Test", null, null); 27 | node = node.createChild("test", "test").build(); 28 | Assert.assertEquals("test", node.getProfile()); 29 | 30 | node = new Node("Test", null, null); 31 | node.setProfile("test"); 32 | Assert.assertEquals("test", node.getProfile()); 33 | } 34 | 35 | /** 36 | * Ensures that the node can build its node properly 37 | */ 38 | @Test 39 | public void pathBuilding() { 40 | Node node = new Node("A", null, null); 41 | Assert.assertEquals("/A", node.getPath()); 42 | 43 | node = node.createChild("A_B").build().createChild("B_A").build(); 44 | Assert.assertEquals("/A/A_B/B_A", node.getPath()); 45 | } 46 | 47 | /** 48 | * Ensures that configurations are null if none were set. 49 | */ 50 | @Test 51 | public void noConfigs() { 52 | Node node = new Node("Test", null, null); 53 | Assert.assertNull(node.getConfigurations()); 54 | Assert.assertNull(node.getConfig("nothing")); 55 | } 56 | 57 | /** 58 | * Ensures that attributes are null if none were set. 59 | */ 60 | @Test 61 | public void noAttributes() { 62 | Node node = new Node("Test", null, null); 63 | Assert.assertNull(node.getAttributes()); 64 | Assert.assertNull(node.getAttribute("nothing")); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/provider/LoopProvider.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.provider; 2 | 3 | import org.dsa.iot.dslink.provider.netty.DefaultLoopProvider; 4 | 5 | import java.util.concurrent.ScheduledFuture; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * Allows customization of the event loop that the SDK will use. 10 | */ 11 | public abstract class LoopProvider { 12 | private static LoopProvider PROVIDER; 13 | 14 | /** 15 | * Gets the current event loop provider. 16 | * @return current event loop provider 17 | */ 18 | public static LoopProvider getProvider() { 19 | if (PROVIDER == null) { 20 | setProvider(new DefaultLoopProvider()); 21 | } 22 | return PROVIDER; 23 | } 24 | 25 | /** 26 | * Sets the current event loop provider. 27 | * @param provider event loop provider 28 | */ 29 | public static void setProvider(LoopProvider provider) { 30 | if (provider == null) { 31 | throw new NullPointerException("provider"); 32 | } 33 | PROVIDER = provider; 34 | } 35 | 36 | /** 37 | * Schedule a task on the event loop immediately. 38 | * @param task a task to schedule 39 | */ 40 | public abstract void schedule(Runnable task); 41 | 42 | /** 43 | * Schedule a task on the event loop to run after a delay. 44 | * @param task a task to schedule 45 | * @param delay delay time 46 | * @param timeUnit delay time unit 47 | * @return a future for this task 48 | */ 49 | public abstract ScheduledFuture schedule(Runnable task, long delay, TimeUnit timeUnit); 50 | 51 | /** 52 | * Schedule a task on the event loop periodically. 53 | * @param task a task to schedule 54 | * @param initialDelay initial delay time 55 | * @param delay periodic delay 56 | * @param timeUnit delay time unit 57 | * @return a future for this task 58 | */ 59 | public abstract ScheduledFuture schedulePeriodic(Runnable task, long initialDelay, long delay, TimeUnit timeUnit); 60 | } 61 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/node/Downstream.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.node; 2 | 3 | import org.dsa.iot.broker.server.client.Client; 4 | 5 | import java.util.Random; 6 | 7 | /** 8 | * @author Samuel Grenier 9 | */ 10 | public class Downstream extends BrokerNode { 11 | 12 | private static final Random RANDOM = new Random(); 13 | private static final String ALPHABET; 14 | 15 | public Downstream(BrokerNode parent, String name) { 16 | super(parent, name, "node"); 17 | } 18 | 19 | public String init(String name, String dsId) { 20 | synchronized (this) { 21 | DSLinkNode node = getChild(name); 22 | if (node != null && node.dsId().equals(dsId)) { 23 | if (node.client() != null) { 24 | return null; 25 | } 26 | return name; 27 | } 28 | if (node != null && hasChild(name)) { 29 | StringBuilder tmp = new StringBuilder(name); 30 | tmp.append("-"); 31 | tmp.append(randomChar()); 32 | 33 | while (hasChild(tmp.toString())) { 34 | tmp.append(randomChar()); 35 | } 36 | name = tmp.toString(); 37 | } 38 | 39 | node = new DSLinkNode(this, name); 40 | node.accessible(false); 41 | addChild(node); 42 | } 43 | return name; 44 | } 45 | 46 | @Override 47 | public void connected(Client client) { 48 | super.connected(client); 49 | DSLinkNode node = getChild(client.handshake().name()); 50 | node.connected(client); 51 | } 52 | 53 | @Override 54 | public void propagateConnected(Client client) { 55 | } 56 | 57 | private static char randomChar() { 58 | return ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())); 59 | } 60 | 61 | static { 62 | String tmp = ""; 63 | tmp += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 64 | tmp += "abcdefghijklmnopqrstuvwxyz"; 65 | tmp += "0123456789"; 66 | ALPHABET = tmp; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/processor/Responder.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.processor; 2 | 3 | import org.dsa.iot.broker.node.DSLinkNode; 4 | import org.dsa.iot.broker.processor.stream.Stream; 5 | import org.dsa.iot.broker.processor.stream.manager.StreamManager; 6 | import org.dsa.iot.dslink.methods.StreamState; 7 | import org.dsa.iot.dslink.util.json.JsonArray; 8 | import org.dsa.iot.dslink.util.json.JsonObject; 9 | 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | /** 13 | * @author Samuel Grenier 14 | */ 15 | public class Responder extends LinkHandler { 16 | 17 | private final StreamManager streamManager = new StreamManager(this); 18 | private final AtomicInteger rid = new AtomicInteger(); 19 | private final AtomicInteger sid = new AtomicInteger(); 20 | 21 | public Responder(DSLinkNode node) { 22 | super(node); 23 | } 24 | 25 | public StreamManager stream() { 26 | return streamManager; 27 | } 28 | 29 | public int nextRid() { 30 | return rid.incrementAndGet(); 31 | } 32 | 33 | public int nextSid() { 34 | return sid.incrementAndGet(); 35 | } 36 | 37 | public void responderConnected() { 38 | streamManager.responderConnected(); 39 | } 40 | 41 | public void responderDisconnected() { 42 | streamManager.responderDisconnected(); 43 | } 44 | 45 | @Override 46 | protected void process(JsonObject response) { 47 | Integer rid = response.get("rid"); 48 | if (rid == null) { 49 | return; 50 | } else if (rid == 0) { 51 | JsonArray updates = response.get("updates"); 52 | stream().sub().dispatch(updates); 53 | return; 54 | } 55 | 56 | StreamState state = StreamState.toEnum((String) response.get("stream")); 57 | Stream stream; 58 | if (state == StreamState.CLOSED) { 59 | stream = streamManager.remove(rid); 60 | } else { 61 | stream = streamManager.get(rid); 62 | } 63 | if (stream != null) { 64 | stream.dispatch(state, response); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /sdk/historian/src/main/java/org/dsa/iot/historian/stats/rollup/Rollup.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.historian.stats.rollup; 2 | 3 | import org.dsa.iot.dslink.node.value.Value; 4 | 5 | /** 6 | * @author Samuel Grenier 7 | */ 8 | public abstract class Rollup { 9 | 10 | /** 11 | * Resets the rollup for consumption again 12 | */ 13 | public abstract void reset(); 14 | 15 | /** 16 | * Updates the rollup data. 17 | * 18 | * @param value Value to update. 19 | * @param ts Timestamp of the value. 20 | */ 21 | public abstract void update(Value value, long ts); 22 | 23 | /** 24 | * @return The statistical value of the rollup. 25 | */ 26 | public abstract Value getValue(); 27 | 28 | public enum Type { 29 | AND("and"), 30 | OR("or"), 31 | AVERAGE("avg"), 32 | COUNT("count"), 33 | FIRST("first"), 34 | LAST("last"), 35 | MAX("max"), 36 | MIN("min"), 37 | SUM("sum"), 38 | DELTA("delta"), 39 | NONE("none"); 40 | 41 | private final String name; 42 | 43 | Type(String name) { 44 | this.name = name; 45 | } 46 | 47 | public static Type toEnum(String rollup) { 48 | if (AND.name.equals(rollup)) { 49 | return AND; 50 | } else if (OR.name.equals(rollup)) { 51 | return OR; 52 | } else if (AVERAGE.name.equals(rollup)) { 53 | return AVERAGE; 54 | } else if (COUNT.name.equals(rollup)) { 55 | return COUNT; 56 | } else if (FIRST.name.equals(rollup)) { 57 | return FIRST; 58 | } else if (LAST.name.equals(rollup)) { 59 | return LAST; 60 | } else if (MAX.name.equals(rollup)) { 61 | return MAX; 62 | } else if (MIN.name.equals(rollup)) { 63 | return MIN; 64 | } else if (SUM.name.equals(rollup)) { 65 | return SUM; 66 | } else if (DELTA.name.equals(rollup)) { 67 | return DELTA; 68 | } 69 | return NONE; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/PropertyReference.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | /** 4 | * Global string references for the system properties in use. 5 | * 6 | * @author Samuel Grenier 7 | * @see System#getProperty(String) 8 | */ 9 | public class PropertyReference { 10 | 11 | public static final String NAMESPACE = "dslink"; 12 | 13 | /** 14 | * A string property of CSV values for the supported encoding formats 15 | * that can be used over the network for communication. If the property 16 | * is not specified then the SDK will allow using any supported format. 17 | */ 18 | public static final String FORMATS = NAMESPACE + ".formats"; 19 | 20 | /** 21 | * An integer property that determines the dispatch delay when a client 22 | * is unable to write to the network. During this delay, messages will be 23 | * merged until they can be sent over the network. 24 | * 25 | * Default value is 75. 26 | */ 27 | public static final String DISPATCH_DELAY = NAMESPACE + ".dispatchDelay"; 28 | 29 | /** 30 | * An integer property that determines the QOS queue size. A value of 0 or less means an 31 | * unlimited queue. 32 | * 33 | * Default value is 0. 34 | */ 35 | public static final String QOS_QUEUE_SIZE = NAMESPACE + ".qosQueueSize"; 36 | 37 | /** 38 | * A boolean property that determines the sdk should perform any 39 | * validations. Currently only the dslink.json is validated. 40 | * 41 | * Default value is true. 42 | */ 43 | public static final String VALIDATE = NAMESPACE + ".validate"; 44 | 45 | /** 46 | * A boolean property that determines whether to validate the 47 | * dslink.json configuration fields. 48 | * 49 | * Default value is true. 50 | */ 51 | public static final String VALIDATE_JSON = VALIDATE + ".json"; 52 | 53 | /** 54 | * A boolean property that determines whether to validate the handler 55 | * class or not. The field must still exist at a minimum. 56 | * 57 | * Default value is true. 58 | */ 59 | public static final String VALIDATE_HANDLER = VALIDATE + ".handler_class"; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/util/UrlBase64Test.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * URL base64 encoding tests. 8 | * 9 | * @author Samuel Grenier 10 | */ 11 | public class UrlBase64Test { 12 | 13 | /** 14 | * Ensures that there is no padding during the encoding process. 15 | */ 16 | @Test 17 | public void noPadding() { 18 | String encoded = UrlBase64.stripPadding("MTIzNA.."); 19 | Assert.assertFalse(encoded.endsWith(".")); 20 | 21 | encoded = UrlBase64.encode("1234"); 22 | Assert.assertFalse(encoded.endsWith(".")); 23 | } 24 | 25 | /** 26 | * Ensures that the padding is added. This is essential when performing a 27 | * decode. 28 | */ 29 | @Test 30 | public void padding() { 31 | String encoded = UrlBase64.addPadding("MTIzNA"); 32 | Assert.assertTrue(encoded.endsWith("..")); 33 | 34 | String decoded = UrlBase64.decodeToString("MTIzNA"); 35 | Assert.assertEquals("1234", decoded); 36 | } 37 | 38 | @Test 39 | public void badEncoding() { 40 | boolean exception = false; 41 | try { 42 | UrlBase64.encode("", "UNKNOWN"); 43 | } catch (RuntimeException e) { 44 | exception = true; 45 | } 46 | Assert.assertTrue(exception); 47 | 48 | exception = false; 49 | try { 50 | UrlBase64.encode(new byte[0], "UNKNOWN"); 51 | } catch (RuntimeException e) { 52 | exception = true; 53 | } 54 | Assert.assertTrue(exception); 55 | } 56 | 57 | @Test 58 | public void badDecoding() { 59 | boolean exception = false; 60 | try { 61 | UrlBase64.decodeToString("", "UNKNOWN"); 62 | } catch (RuntimeException e) { 63 | exception = true; 64 | } 65 | Assert.assertTrue(exception); 66 | 67 | exception = false; 68 | try { 69 | UrlBase64.decode("", "UNKNOWN"); 70 | } catch (RuntimeException e) { 71 | exception = true; 72 | } 73 | Assert.assertTrue(exception); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/node/value/ValuePair.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.value; 2 | 3 | 4 | import org.dsa.iot.dslink.node.NodeListener; 5 | 6 | /** 7 | * Used for handling updates that involve a previous value. 8 | * 9 | * @author Samuel Grenier 10 | * @see NodeListener#setValueHandler 11 | */ 12 | public class ValuePair { 13 | 14 | private final boolean externalSource; 15 | private final Value previous; 16 | private Value current; 17 | private boolean reject; 18 | 19 | /** 20 | * @param previous Previous value. 21 | * @param current The new current value. 22 | * @param externalSource Whether the new value is being set from an 23 | * external source or not. 24 | */ 25 | public ValuePair(Value previous, Value current, boolean externalSource) { 26 | this.previous = previous; 27 | this.current = current; 28 | this.externalSource = externalSource; 29 | } 30 | 31 | /** 32 | * @return Whether the new value came from an external source. 33 | */ 34 | public boolean isFromExternalSource() { 35 | return externalSource; 36 | } 37 | 38 | /** 39 | * @return The previous, or old, value. 40 | */ 41 | public Value getPrevious() { 42 | return previous; 43 | } 44 | 45 | /** 46 | * @return The current, or new, value. 47 | */ 48 | public Value getCurrent() { 49 | return current; 50 | } 51 | 52 | /** 53 | * This allows for dynamic value control when being set from an external 54 | * source but the value is still valid and does not need to be rejected. 55 | * 56 | * @param value New value to set. 57 | */ 58 | @SuppressWarnings("unused") 59 | public void setCurrent(Value value) { 60 | this.current = value; 61 | } 62 | 63 | /** 64 | * @param reject Whether to reject the new value or not. 65 | */ 66 | @SuppressWarnings("unused") 67 | public void setReject(boolean reject) { 68 | this.reject = reject; 69 | } 70 | 71 | /** 72 | * @return Whether to reject the new value or not. 73 | */ 74 | public boolean isRejected() { 75 | return reject; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/processor/stream/GenericStream.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.processor.stream; 2 | 3 | import org.dsa.iot.broker.processor.Responder; 4 | import org.dsa.iot.broker.server.client.Client; 5 | import org.dsa.iot.broker.utils.ParsedPath; 6 | import org.dsa.iot.dslink.methods.StreamState; 7 | import org.dsa.iot.dslink.util.json.JsonArray; 8 | import org.dsa.iot.dslink.util.json.JsonObject; 9 | 10 | /** 11 | * @author Samuel Grenier 12 | */ 13 | public class GenericStream extends Stream { 14 | 15 | private Client requester; 16 | private int rid; 17 | 18 | public GenericStream(Responder responder, ParsedPath path) { 19 | super(responder, path); 20 | } 21 | 22 | @Override 23 | public void add(Client requester, int requesterRid) { 24 | if (this.requester != null) { 25 | throw new IllegalStateException("requester already set on stream"); 26 | } 27 | this.requester = requester; 28 | this.rid = requesterRid; 29 | } 30 | 31 | @Override 32 | public void remove(Client requester) { 33 | this.requester = null; 34 | } 35 | 36 | @Override 37 | public boolean isEmpty() { 38 | return requester == null; 39 | } 40 | 41 | @Override 42 | public void dispatch(StreamState state, JsonObject response) { 43 | if (isEmpty()) { 44 | return; 45 | } 46 | 47 | response.put("rid", rid); 48 | JsonArray resps = new JsonArray(); 49 | resps.add(response); 50 | requester.writeResponse(resps); 51 | 52 | if (state == StreamState.CLOSED) { 53 | close(); 54 | } 55 | } 56 | 57 | @Override 58 | public void responderConnected() { 59 | } 60 | 61 | @Override 62 | public void responderDisconnected() { 63 | JsonObject resp = new JsonObject(); 64 | resp.put("rid", rid); 65 | resp.put("stream", StreamState.CLOSED); 66 | JsonArray resps = new JsonArray(); 67 | resps.add(resp); 68 | requester.writeResponse(resps); 69 | close(); 70 | } 71 | 72 | private void close() { 73 | requester.processor().requester().removeStream(rid); 74 | close(requester, false); 75 | requester = null; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/link/Linkable.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.link; 2 | 3 | import org.dsa.iot.dslink.DSLink; 4 | import org.dsa.iot.dslink.DSLinkHandler; 5 | import org.dsa.iot.dslink.node.Node; 6 | import org.dsa.iot.dslink.node.SubscriptionManager; 7 | import org.dsa.iot.dslink.node.value.Value; 8 | import org.dsa.iot.dslink.serializer.SerializationManager; 9 | 10 | import java.lang.ref.WeakReference; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author Samuel Grenier 15 | */ 16 | public abstract class Linkable { 17 | 18 | private final DSLinkHandler handler; 19 | private WeakReference link; 20 | 21 | /** 22 | * @param handler Link handler 23 | */ 24 | public Linkable(DSLinkHandler handler) { 25 | this.handler = handler; 26 | } 27 | 28 | /** 29 | * Optimally batch set a massive collection of nodes and values. 30 | * 31 | * @param updates Updates to batch set. 32 | */ 33 | public abstract void batchSet(Map updates); 34 | 35 | /** 36 | * @return The subscription manager of the link. 37 | */ 38 | public SubscriptionManager getSubscriptionManager() { 39 | DSLink dsLink = getDSLink(); 40 | if (dsLink != null) { 41 | return dsLink.getSubscriptionManager(); 42 | } 43 | return null; 44 | } 45 | 46 | /** 47 | * 48 | * @return The serialization manager of the link. 49 | */ 50 | public SerializationManager getSerialManager() { 51 | DSLink dslink = getDSLink(); 52 | if (dslink != null) { 53 | return dslink.getSerialManager(); 54 | } 55 | return null; 56 | } 57 | 58 | /** 59 | * @return Handler of the link 60 | */ 61 | public DSLinkHandler getHandler() { 62 | return handler; 63 | } 64 | 65 | /** 66 | * The DSLink object is used for the client and node manager. 67 | * @param link The link to set. 68 | */ 69 | public void setDSLink(DSLink link) { 70 | if (link == null) 71 | throw new NullPointerException("link"); 72 | this.link = new WeakReference<>(link); 73 | } 74 | 75 | /** 76 | * @return A reference to the dslink, can be null 77 | */ 78 | public DSLink getDSLink() { 79 | return link.get(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /internal/logging/src/main/java/org/slf4j/impl/StaticMDCBinder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2004-2011 QOS.ch 3 | * All rights reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | package org.slf4j.impl; 26 | 27 | import org.slf4j.helpers.BasicMDCAdapter; 28 | import org.slf4j.spi.MDCAdapter; 29 | 30 | /** 31 | * This implementation is bound to {@link BasicMDCAdapter}. 32 | * 33 | * @author Ceki Gülcü 34 | */ 35 | @SuppressWarnings("unused") 36 | public class StaticMDCBinder { 37 | 38 | /** 39 | * The unique instance of this class. 40 | */ 41 | public static final StaticMDCBinder SINGLETON = new StaticMDCBinder(); 42 | 43 | private StaticMDCBinder() { 44 | } 45 | 46 | /** 47 | * Currently this method always returns an instance of 48 | * {@link BasicMDCAdapter}. 49 | * 50 | * @return MDCA Adapter 51 | */ 52 | public MDCAdapter getMDCA() { 53 | // note that this method is invoked only from within the static initializer of 54 | // the org.slf4j.MDC class. 55 | return new BasicMDCAdapter(); 56 | } 57 | 58 | public String getMDCAdapterClassStr() { 59 | return BasicMDCAdapter.class.getName(); 60 | } 61 | } -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/utils/ParsedPath.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.utils; 2 | 3 | import org.dsa.iot.dslink.node.NodeManager; 4 | import org.dsa.iot.dslink.util.StringUtils; 5 | 6 | /** 7 | * @author Samuel Grenier 8 | */ 9 | public class ParsedPath { 10 | 11 | private final boolean isRemote; 12 | private final String[] splitPath; 13 | private final String fullPath; 14 | 15 | private ParsedPath(boolean isRemote, 16 | String[] split, 17 | String fullPath) { 18 | this.isRemote = isRemote; 19 | this.splitPath = split; 20 | this.fullPath = fullPath; 21 | } 22 | 23 | /** 24 | * @return Whether the path references a remote endpoint or not. 25 | */ 26 | public boolean isRemote() { 27 | return isRemote; 28 | } 29 | 30 | /** 31 | * @return The split path during parsing. 32 | */ 33 | public String[] split() { 34 | return splitPath.clone(); 35 | } 36 | 37 | /** 38 | * @return The full path 39 | */ 40 | public String full() { 41 | return fullPath; 42 | } 43 | 44 | /** 45 | * @return The base path of the remote path. 46 | */ 47 | public String base() { 48 | if (isRemote()) { 49 | String[] tmp = new String[splitPath.length - 2]; 50 | System.arraycopy(splitPath, 2, tmp, 0, tmp.length); 51 | return "/" + StringUtils.join(tmp, "/"); 52 | } else { 53 | return fullPath; 54 | } 55 | } 56 | 57 | /** 58 | * @param downstream Downstream name. 59 | * @param path Path to parse. 60 | * @return The parsed path. 61 | */ 62 | public static ParsedPath parse(String downstream, 63 | String path) { 64 | path = NodeManager.normalizePath(path, true); 65 | String[] split = NodeManager.splitPath(path); 66 | boolean ir = split.length > 1 && split[0].equals(downstream); 67 | return new ParsedPath(ir, split, path); 68 | } 69 | 70 | @Override 71 | public boolean equals(Object o) { 72 | if (this == o) { 73 | return true; 74 | } else if (!(o instanceof ParsedPath)) { 75 | return false; 76 | } 77 | 78 | String other = ((ParsedPath) o).full(); 79 | return other == null ? full() == null : other.equals(full()); 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | return fullPath != null ? fullPath.hashCode() : 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/handshake/RemoteKeyTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.handshake; 2 | 3 | import org.bouncycastle.math.ec.ECPoint; 4 | import org.bouncycastle.util.Arrays; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.math.BigInteger; 9 | 10 | /** 11 | * @author Samuel Grenier 12 | */ 13 | public class RemoteKeyTest { 14 | 15 | /** 16 | * Tests shared secret validation. 17 | */ 18 | @Test 19 | public void sharedSecretTest() { 20 | // Client gives its key to the server 21 | LocalKeys localKeys = LocalKeys.generate(); 22 | 23 | // Server generates a temporary local key 24 | ECPoint point = localKeys.getPublicKey().getQ(); 25 | LocalKeys tempLocalKey = LocalKeys.generate(); 26 | point = point.multiply(tempLocalKey.getPrivateKey().getD()); 27 | BigInteger bi = point.normalize().getXCoord().toBigInteger(); 28 | byte[] sharedSecret = RemoteKey.normalize(bi.toByteArray()); 29 | 30 | // Client receives temp key 31 | String tempKey = tempLocalKey.encodedPublicKey(); 32 | RemoteKey remoteKey = RemoteKey.generate(localKeys, tempKey); 33 | Assert.assertArrayEquals(sharedSecret, remoteKey.getSharedSecret()); 34 | } 35 | 36 | /** 37 | * Tests the normalization of the shared secret. This ensures a consistency 38 | * throughout the system. 39 | */ 40 | @Test 41 | public void normalizationTest() { 42 | byte[] b = new byte[3]; 43 | b = RemoteKey.normalize(b); 44 | Assert.assertEquals(32, b.length); 45 | 46 | b = new byte[40]; 47 | b = RemoteKey.normalize(b); 48 | Assert.assertEquals(32, b.length); 49 | 50 | b = new byte[]{1, 2, 3}; 51 | b = RemoteKey.normalize(b); 52 | Assert.assertEquals(1, b[29]); 53 | Assert.assertEquals(2, b[30]); 54 | Assert.assertEquals(3, b[31]); 55 | 56 | b = new byte[100]; 57 | b[50] = 4; 58 | b[99] = 3; 59 | b[98] = 2; 60 | b[97] = 1; 61 | b = RemoteKey.normalize(b); 62 | Assert.assertEquals(1, b[29]); 63 | Assert.assertEquals(2, b[30]); 64 | Assert.assertEquals(3, b[31]); 65 | Assert.assertFalse(Arrays.contains(toIntArray(b), 4)); 66 | } 67 | 68 | private int[] toIntArray(byte[] bytes) { 69 | int[] array = new int[bytes.length]; 70 | for (int i = 0; i < bytes.length; i++) { 71 | array[i] = bytes[i]; 72 | } 73 | return array; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /internal/logging/src/main/java/org/slf4j/impl/StaticMarkerBinder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2004-2011 QOS.ch 3 | * All rights reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | package org.slf4j.impl; 26 | 27 | import org.slf4j.IMarkerFactory; 28 | import org.slf4j.MarkerFactory; 29 | import org.slf4j.helpers.BasicMarkerFactory; 30 | import org.slf4j.spi.MarkerFactoryBinder; 31 | 32 | /** 33 | * 34 | * The binding of {@link MarkerFactory} class with an actual instance of 35 | * {@link IMarkerFactory} is performed using information returned by this class. 36 | * 37 | * @author Ceki Gülcü 38 | */ 39 | @SuppressWarnings("unused") 40 | public class StaticMarkerBinder implements MarkerFactoryBinder { 41 | 42 | /** 43 | * The unique instance of this class. 44 | */ 45 | public static final StaticMarkerBinder SINGLETON = new StaticMarkerBinder(); 46 | 47 | final IMarkerFactory markerFactory = new BasicMarkerFactory(); 48 | 49 | private StaticMarkerBinder() { 50 | } 51 | 52 | /** 53 | * Currently this method always returns an instance of 54 | * {@link BasicMarkerFactory}. 55 | */ 56 | public IMarkerFactory getMarkerFactory() { 57 | return markerFactory; 58 | } 59 | 60 | /** 61 | * Currently, this method returns the class name of 62 | * {@link BasicMarkerFactory}. 63 | */ 64 | public String getMarkerFactoryClassStr() { 65 | return BasicMarkerFactory.class.getName(); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/processor/MessageProcessor.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.processor; 2 | 3 | import org.dsa.iot.broker.node.DSLinkNode; 4 | import org.dsa.iot.broker.server.DsaHandshake; 5 | import org.dsa.iot.broker.server.client.Client; 6 | import org.dsa.iot.dslink.util.json.JsonArray; 7 | import org.dsa.iot.dslink.util.json.JsonObject; 8 | 9 | /** 10 | * @author Samuel Grenier 11 | */ 12 | public class MessageProcessor { 13 | 14 | private Responder responder; 15 | private Requester requester; 16 | 17 | public void initialize(DSLinkNode node) { 18 | DsaHandshake handshake = node.client().handshake(); 19 | if (handshake.isResponder()) { 20 | if (responder == null) { 21 | responder = new Responder(node); 22 | } 23 | } else { 24 | responder = null; 25 | } 26 | if (handshake.isRequester()) { 27 | if (requester == null) { 28 | requester = new Requester(node); 29 | } 30 | } else { 31 | requester = null; 32 | } 33 | 34 | if (responder != null) { 35 | responder.responderConnected(); 36 | } 37 | } 38 | 39 | public void processData(JsonObject data) { 40 | Requester requester = this.requester; 41 | if (requester != null) { 42 | processRequests((JsonArray) data.get("requests")); 43 | } 44 | 45 | Responder responder = this.responder; 46 | if (responder != null) { 47 | processResponses((JsonArray) data.get("responses")); 48 | } 49 | } 50 | 51 | public void disconnected(Client client) { 52 | Requester requester = this.requester; 53 | if (requester != null) { 54 | requester.requesterDisconnected(client); 55 | } 56 | Responder responder = this.responder; 57 | if (responder != null) { 58 | responder.responderDisconnected(); 59 | } 60 | } 61 | 62 | public Responder responder() { 63 | return responder; 64 | } 65 | 66 | public Requester requester() { 67 | return requester; 68 | } 69 | 70 | protected void processRequests(JsonArray requests) { 71 | if (requests != null) { 72 | for (Object obj : requests) { 73 | requester.process((JsonObject) obj); 74 | } 75 | } 76 | } 77 | 78 | protected void processResponses(JsonArray responses) { 79 | if (responses != null) { 80 | for (Object obj : responses) { 81 | responder.process((JsonObject) obj); 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/util/ConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | import org.dsa.iot.dslink.config.Configuration; 4 | import org.dsa.iot.dslink.connection.ConnectionType; 5 | import org.dsa.iot.dslink.handshake.LocalKeys; 6 | import org.junit.Test; 7 | 8 | /** 9 | * Tests the configuration and its validation schemes. 10 | * 11 | * @author Samuel Grenier 12 | */ 13 | public class ConfigurationTest { 14 | 15 | /** 16 | * Ensures the validation fails on invalid setup data. 17 | */ 18 | @Test(expected = RuntimeException.class) 19 | public void validationNoID() { 20 | Configuration config = new Configuration(); 21 | config.validate(); 22 | } 23 | 24 | /** 25 | * Ensures that empty IDs can't be set. 26 | */ 27 | @Test(expected = IllegalArgumentException.class) 28 | public void validationEmptyID() { 29 | Configuration config = new Configuration(); 30 | config.setDsId(""); 31 | } 32 | 33 | /** 34 | * Ensures that null IDs can't be set. 35 | */ 36 | @Test(expected = NullPointerException.class) 37 | public void validationNullID() { 38 | Configuration config = new Configuration(); 39 | config.setDsId(null); 40 | } 41 | 42 | /** 43 | * Ensures that null connection types can't be set. 44 | */ 45 | @Test(expected = NullPointerException.class) 46 | public void validationNullConnType() { 47 | Configuration config = new Configuration(); 48 | config.setConnectionType(null); 49 | } 50 | 51 | /** 52 | * Ensures that null keys can't be set. 53 | */ 54 | @Test(expected = NullPointerException.class) 55 | public void validationNullStringKeys() { 56 | Configuration config = new Configuration(); 57 | config.setKeys((String) null); 58 | } 59 | 60 | /** 61 | * Ensures that null keys can't be set. 62 | */ 63 | @Test(expected = NullPointerException.class) 64 | public void validationNullLocalKeys() { 65 | Configuration config = new Configuration(); 66 | config.setKeys((LocalKeys) null); 67 | } 68 | 69 | /** 70 | * Ensures that the validation passes on proper data. 71 | */ 72 | @Test 73 | public void validationPass() { 74 | Configuration config = new Configuration(); 75 | config.setDsId("test"); 76 | config.setConnectionType(ConnectionType.WEB_SOCKET); 77 | config.setAuthEndpoint("http://localhost"); 78 | config.setKeys(LocalKeys.generate()); 79 | config.setRequester(true); 80 | config.setResponder(false); 81 | config.validate(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/handshake/RemoteKey.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.handshake; 2 | 3 | import org.bouncycastle.jce.spec.ECParameterSpec; 4 | import org.bouncycastle.jce.spec.ECPublicKeySpec; 5 | import org.bouncycastle.math.ec.ECPoint; 6 | import org.dsa.iot.dslink.util.UrlBase64; 7 | 8 | import java.math.BigInteger; 9 | 10 | /** 11 | * Handles remote keys. The shared secret will be decrypted here. 12 | * 13 | * @author Samuel Grenier 14 | */ 15 | public class RemoteKey { 16 | 17 | private final byte[] sharedSecret; 18 | 19 | /** 20 | * Populates the remote key with encryption data from the server. 21 | * 22 | * @param sharedSecret Shared secret retrieved from the server 23 | */ 24 | private RemoteKey(byte[] sharedSecret) { 25 | this.sharedSecret = sharedSecret.clone(); 26 | } 27 | 28 | /** 29 | * Retrieves the decrypted shared secret. 30 | * 31 | * @return shared secret 32 | */ 33 | public byte[] getSharedSecret() { 34 | return sharedSecret.clone(); 35 | } 36 | 37 | /** 38 | * @param keys Local keys necessary for the decryption 39 | * @param tempKey Temporary key received from the server 40 | * @return A remote key. 41 | */ 42 | public static RemoteKey generate(LocalKeys keys, String tempKey) { 43 | byte[] decoded = UrlBase64.decode(tempKey); 44 | ECParameterSpec params = keys.getPrivateKey().getParameters(); 45 | ECPoint point = params.getCurve().decodePoint(decoded); 46 | ECPublicKeySpec spec = new ECPublicKeySpec(point, params); 47 | point = spec.getQ().multiply(keys.getPrivateKey().getD()); 48 | BigInteger bi = point.normalize().getXCoord().toBigInteger(); 49 | byte[] sharedSecret = bi.toByteArray(); 50 | sharedSecret = normalize(sharedSecret); 51 | return new RemoteKey(sharedSecret); 52 | } 53 | 54 | /** 55 | * Ensures the shared secret is always 32 bytes in length. 56 | * 57 | * @param sharedSecret Shared secret to normalize 58 | * @return Normalized shared secret 59 | */ 60 | public static byte[] normalize(byte[] sharedSecret) { 61 | if (sharedSecret.length < 32) { 62 | byte[] fixed = new byte[32]; 63 | int len = sharedSecret.length; 64 | System.arraycopy(sharedSecret, 0, fixed, 32 - len, len); 65 | sharedSecret = fixed; 66 | } else if (sharedSecret.length > 32) { 67 | byte[] fixed = new byte[32]; 68 | System.arraycopy(sharedSecret, sharedSecret.length - 32, fixed, 0, fixed.length); 69 | sharedSecret = fixed; 70 | } 71 | return sharedSecret; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/responder/src/main/java/org/dsa/iot/responder/Echo.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.responder; 2 | 3 | import org.dsa.iot.dslink.node.Node; 4 | import org.dsa.iot.dslink.node.NodeBuilder; 5 | import org.dsa.iot.dslink.node.Permission; 6 | import org.dsa.iot.dslink.node.actions.Action; 7 | import org.dsa.iot.dslink.node.actions.ActionResult; 8 | import org.dsa.iot.dslink.node.actions.Parameter; 9 | import org.dsa.iot.dslink.node.actions.table.Row; 10 | import org.dsa.iot.dslink.node.actions.table.Table; 11 | import org.dsa.iot.dslink.node.value.Value; 12 | import org.dsa.iot.dslink.node.value.ValueType; 13 | import org.dsa.iot.dslink.util.handler.Handler; 14 | 15 | import java.util.LinkedHashSet; 16 | import java.util.Set; 17 | 18 | /** 19 | * @author Samuel Grenier 20 | */ 21 | public class Echo { 22 | 23 | public static void init(Node superRoot) { 24 | { 25 | NodeBuilder builder = superRoot.createChild("echoEnum"); 26 | builder.setAction(getEchoEnumAction()); 27 | builder.build(); 28 | } 29 | 30 | { 31 | NodeBuilder builder = superRoot.createChild("echoText"); 32 | builder.setAction(getEchoTextAction()); 33 | builder.build(); 34 | } 35 | } 36 | 37 | private static Action getEchoTextAction() { 38 | Action a = new Action(Permission.READ, new Handler() { 39 | @Override 40 | public void handle(ActionResult event) { 41 | Value v = event.getParameter("text", new Value("")); 42 | Table t = event.getTable(); 43 | t.addRow(Row.make(v)); 44 | } 45 | }); 46 | a.addParameter(new Parameter("text", ValueType.STRING) 47 | .setDescription("Text to echo") 48 | .setPlaceHolder("Hello world!")); 49 | a.addResult(new Parameter("echo", ValueType.STRING)); 50 | return a; 51 | } 52 | 53 | private static Action getEchoEnumAction() { 54 | Action a = new Action(Permission.READ, new Handler() { 55 | @Override 56 | public void handle(ActionResult event) { 57 | Value v = event.getParameter("type", new Value("")); 58 | Table t = event.getTable(); 59 | t.addRow(Row.make(v)); 60 | } 61 | }); 62 | Set enums = new LinkedHashSet<>(); 63 | enums.add("A: 1,2"); 64 | enums.add("B: 3,4"); 65 | enums.add("C: 5,6"); 66 | a.addParameter(new Parameter("type", ValueType.makeEnum(enums)) 67 | .setDescription("Enumeration string to echo")); 68 | a.addResult(new Parameter("echo", ValueType.STRING)); 69 | return a; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/json/encoders/ListEncoder.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.json.encoders; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import org.dsa.iot.dslink.util.json.JsonArray; 5 | import org.dsa.iot.dslink.util.json.JsonObject; 6 | 7 | import java.io.IOException; 8 | import java.math.BigDecimal; 9 | import java.math.BigInteger; 10 | 11 | /** 12 | * @author Samuel Grenier 13 | */ 14 | public class ListEncoder { 15 | 16 | public static void write(JsonGenerator gen, JsonArray json) 17 | throws IOException { 18 | gen.writeStartArray(); 19 | performWrite(gen, json); 20 | } 21 | 22 | static void performWrite(JsonGenerator gen, JsonArray json) 23 | throws IOException { 24 | for (Object instance : json) { 25 | if (instance instanceof Byte) { 26 | gen.writeNumber(((Number) instance).byteValue()); 27 | } else if (instance instanceof Short) { 28 | gen.writeNumber(((Number) instance).shortValue()); 29 | } else if (instance instanceof Integer) { 30 | gen.writeNumber(((Number) instance).intValue()); 31 | } else if (instance instanceof Long) { 32 | gen.writeNumber(((Number) instance).longValue()); 33 | } else if (instance instanceof Float) { 34 | gen.writeNumber(((Number) instance).floatValue()); 35 | } else if (instance instanceof Double) { 36 | gen.writeNumber(((Number) instance).doubleValue()); 37 | } else if (instance instanceof BigDecimal) { 38 | gen.writeNumber((BigDecimal) instance); 39 | } else if (instance instanceof BigInteger) { 40 | gen.writeNumber((BigInteger) instance); 41 | } else if (instance instanceof Boolean) { 42 | gen.writeBoolean((Boolean) instance); 43 | } else if (instance instanceof String) { 44 | gen.writeString((String) instance); 45 | } else if (instance instanceof JsonObject) { 46 | gen.writeStartObject(); 47 | MapEncoder.performWrite(gen, (JsonObject) instance); 48 | } else if (instance instanceof JsonArray) { 49 | gen.writeStartArray(); 50 | performWrite(gen, (JsonArray) instance); 51 | } else if (instance instanceof byte[]) { 52 | gen.writeBinary((byte[]) instance); 53 | } else if (instance == null) { 54 | gen.writeNull(); 55 | } else { 56 | String err = "Unsupported class: " + instance.getClass().getName(); 57 | throw new RuntimeException(err); 58 | } 59 | } 60 | gen.writeEndArray(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/responder/src/main/java/org/dsa/iot/responder/Replicator.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.responder; 2 | 3 | import org.dsa.iot.dslink.node.Node; 4 | import org.dsa.iot.dslink.node.NodeBuilder; 5 | import org.dsa.iot.dslink.node.Permission; 6 | import org.dsa.iot.dslink.node.actions.Action; 7 | import org.dsa.iot.dslink.node.actions.ActionResult; 8 | import org.dsa.iot.dslink.util.handler.Handler; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * Creates a bunch of nodes to test children updates. 14 | * @author Samuel Grenier 15 | */ 16 | public class Replicator { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(Replicator.class); 19 | 20 | private Node node; 21 | private Thread thread; 22 | 23 | private Replicator(Node node) { 24 | this.node = node; 25 | } 26 | 27 | public static void start(Node parent) { 28 | NodeBuilder builder = parent.createChild("replicator"); 29 | builder.getListener().setOnListHandler(new Handler() { 30 | @Override 31 | public void handle(Node event) { 32 | LOGGER.info("Replicator node has been listed"); 33 | } 34 | }); 35 | 36 | final Node node = builder.build(); 37 | final Permission perm = Permission.READ; 38 | final Replicator rep = new Replicator(node); 39 | final Action act = new Action(perm, new ResetHandler(rep)); 40 | node.createChild("reset").build().setAction(act); 41 | rep.startThread(); 42 | } 43 | 44 | private synchronized void startThread() { 45 | thread = new Thread(new Runnable() { 46 | @Override 47 | public void run() { 48 | try { 49 | for (int i = 0; i < 5; i++) { 50 | Thread.sleep(1000); 51 | NodeBuilder b = node.createChild(String.valueOf(i)); 52 | b.setSerializable(false); 53 | b.build(); 54 | } 55 | } catch (InterruptedException ignored) { 56 | } 57 | } 58 | }); 59 | thread.setDaemon(true); 60 | thread.start(); 61 | } 62 | 63 | private synchronized void reset() { 64 | if (thread != null) { 65 | thread.interrupt(); 66 | } 67 | thread = null; 68 | for (int i = 0; i < 100; i++) { 69 | node.removeChild(String.valueOf(i)); 70 | } 71 | } 72 | 73 | private static class ResetHandler implements Handler { 74 | 75 | private final Replicator rep; 76 | 77 | public ResetHandler(Replicator rep) { 78 | this.rep = rep; 79 | } 80 | 81 | @Override 82 | public void handle(ActionResult event) { 83 | rep.reset(); 84 | rep.startThread(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/processor/stream/SubStream.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.processor.stream; 2 | 3 | import org.dsa.iot.broker.node.BrokerNode; 4 | import org.dsa.iot.broker.server.client.Client; 5 | import org.dsa.iot.broker.utils.ParsedPath; 6 | import org.dsa.iot.dslink.util.json.JsonArray; 7 | import org.dsa.iot.dslink.util.json.JsonObject; 8 | 9 | import java.util.Map; 10 | import java.util.Objects; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | 13 | /** 14 | * @author Samuel Grenier 15 | */ 16 | public class SubStream { 17 | 18 | private final ParsedPath path; 19 | private final BrokerNode node; 20 | 21 | private Map clientMap = new ConcurrentHashMap<>(); 22 | private JsonArray lastValueUpdate; 23 | 24 | public SubStream(ParsedPath path, BrokerNode node) { 25 | this.path = Objects.requireNonNull(path, "path"); 26 | this.node = Objects.requireNonNull(node, "node"); 27 | } 28 | 29 | public ParsedPath path() { 30 | return path; 31 | } 32 | 33 | public BrokerNode node() { 34 | return node; 35 | } 36 | 37 | public void add(Client requester, int sid) { 38 | Integer prev = clientMap.put(requester, sid); 39 | if (prev != null) { 40 | return; 41 | } 42 | JsonArray lastValueUpdate = this.lastValueUpdate; 43 | if (lastValueUpdate != null) { 44 | JsonObject resp = new JsonObject(); 45 | resp.put("rid", 0); 46 | 47 | JsonArray update = new JsonArray(); 48 | update.add(sid); 49 | update.add(lastValueUpdate.get(1)); 50 | update.add(lastValueUpdate.get(2)); 51 | 52 | JsonArray updates = new JsonArray(); 53 | updates.add(update); 54 | resp.put("updates", updates); 55 | 56 | JsonArray resps = new JsonArray(); 57 | resps.add(resp); 58 | requester.writeResponse(resps); 59 | } 60 | } 61 | 62 | public void remove(Client requester) { 63 | clientMap.remove(requester); 64 | } 65 | 66 | public boolean isEmpty() { 67 | return clientMap.isEmpty(); 68 | } 69 | 70 | public void dispatch(JsonArray update) { 71 | lastValueUpdate = update; 72 | 73 | JsonObject resp = new JsonObject(); 74 | resp.put("rid", 0); 75 | 76 | JsonArray updates = new JsonArray(); 77 | updates.add(update); 78 | resp.put("updates", updates); 79 | 80 | JsonArray resps = new JsonArray(); 81 | resps.add(resp); 82 | 83 | for (Map.Entry entry : clientMap.entrySet()) { 84 | Integer sid = entry.getValue(); 85 | update.set(0, sid); 86 | 87 | Client requester = entry.getKey(); 88 | if (!requester.writeResponse(resps)) { 89 | node().unsubscribe(this, requester); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/json/decoders/ListDecoder.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.json.decoders; 2 | 3 | import com.fasterxml.jackson.core.JsonFactory; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonToken; 6 | import org.dsa.iot.dslink.util.json.JsonArray; 7 | import org.dsa.iot.dslink.util.json.JsonObject; 8 | 9 | import java.io.IOException; 10 | import java.util.LinkedHashMap; 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * @author Samuel Grenier 17 | */ 18 | public class ListDecoder { 19 | 20 | public static List decode(JsonFactory factory, 21 | byte[] content, 22 | int offset, 23 | int length) { 24 | List list = new LinkedList<>(); 25 | JsonParser parser = null; 26 | try { 27 | parser = factory.createParser(content, offset, length); 28 | parser.nextToken(); 29 | performDecodeList(list, parser); 30 | } catch (IOException e) { 31 | throw new RuntimeException(e); 32 | } finally { 33 | if (parser != null) { 34 | try { 35 | parser.close(); 36 | } catch (IOException ignored) { 37 | } 38 | } 39 | } 40 | return list; 41 | } 42 | 43 | static void performDecodeList(List in, 44 | JsonParser parser) 45 | throws IOException { 46 | JsonToken token; 47 | while (!((token = parser.nextToken()) == JsonToken.END_ARRAY 48 | || token == null)) { 49 | if (token == JsonToken.VALUE_NULL) { 50 | in.add(null); 51 | } else if (token == JsonToken.VALUE_STRING) { 52 | in.add(parser.getText()); 53 | } else if (token == JsonToken.VALUE_FALSE) { 54 | in.add(false); 55 | } else if (token == JsonToken.VALUE_TRUE) { 56 | in.add(true); 57 | } else if (token == JsonToken.VALUE_NUMBER_INT 58 | || token == JsonToken.VALUE_NUMBER_FLOAT) { 59 | in.add(parser.getNumberValue()); 60 | } else if (token == JsonToken.VALUE_EMBEDDED_OBJECT) { 61 | in.add(parser.getBinaryValue()); 62 | } else if (token == JsonToken.START_ARRAY) { 63 | List list = new LinkedList<>(); 64 | performDecodeList(list, parser); 65 | in.add(new JsonArray(list)); 66 | } else if (token == JsonToken.START_OBJECT) { 67 | Map map = new LinkedHashMap<>(); 68 | MapDecoder.performDecodeMap(map, parser); 69 | in.add(new JsonObject(map)); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/server/Server.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.server; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.*; 5 | import io.netty.channel.socket.SocketChannel; 6 | import io.netty.channel.socket.nio.NioServerSocketChannel; 7 | import io.netty.handler.codec.http.HttpObjectAggregator; 8 | import io.netty.handler.codec.http.HttpServerCodec; 9 | import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; 10 | import io.netty.handler.ssl.SslContext; 11 | import org.dsa.iot.broker.Broker; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * @author Samuel Grenier 17 | */ 18 | public class Server { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(Server.class); 21 | 22 | private final Broker broker; 23 | private final String host; 24 | private final int port; 25 | private final SslContext ssl; 26 | private Channel channel; 27 | 28 | public Server(String host, int port, 29 | SslContext ssl, Broker broker) { 30 | if (host == null) { 31 | throw new NullPointerException("host"); 32 | } else if (broker == null) { 33 | throw new NullPointerException("broker"); 34 | } else if (port < 0) { 35 | throw new IllegalArgumentException("port"); 36 | } 37 | this.broker = broker; 38 | this.host = host; 39 | this.port = port; 40 | this.ssl = ssl; 41 | } 42 | 43 | public void start(EventLoopGroup bossLoop, 44 | EventLoopGroup workerLoop) { 45 | ServerBootstrap strap = new ServerBootstrap(); 46 | strap.channel(NioServerSocketChannel.class); 47 | strap.childHandler(new WsServerInitializer()); 48 | strap.group(bossLoop, workerLoop); 49 | 50 | ChannelFuture fut = strap.bind(host, port); 51 | fut.syncUninterruptibly(); 52 | { 53 | String pretty = ssl != null ? "HTTPS" : "HTTP"; 54 | LOGGER.info("{} server bound to {} on port {}", pretty, host, port); 55 | } 56 | channel = fut.channel(); 57 | channel.closeFuture().syncUninterruptibly(); 58 | } 59 | 60 | public void stop() { 61 | if (channel != null) { 62 | channel.close(); 63 | channel = null; 64 | } 65 | } 66 | 67 | private class WsServerInitializer extends ChannelInitializer { 68 | 69 | @Override 70 | public void initChannel(SocketChannel ch) throws Exception { 71 | ChannelPipeline pipeline = ch.pipeline(); 72 | if (ssl != null) { 73 | pipeline.addLast(ssl.newHandler(ch.alloc())); 74 | } 75 | pipeline.addLast(new HttpServerCodec()); 76 | pipeline.addLast(new HttpObjectAggregator(65536)); 77 | pipeline.addLast(new WebSocketServerCompressionHandler()); 78 | pipeline.addLast(new WsServerHandler(broker, ssl != null)); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/node/value/ValueTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.node.value; 2 | 3 | import org.dsa.iot.dslink.util.json.JsonArray; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | /** 8 | * Tests the value API. 9 | * 10 | * @author Samuel Grenier 11 | */ 12 | public class ValueTest { 13 | 14 | @Test 15 | public void initialValueSetter() { 16 | Value val = new Value(1); 17 | Assert.assertEquals(ValueType.NUMBER, val.getType()); 18 | 19 | Assert.assertNotNull(val.getNumber()); 20 | Assert.assertEquals(1, val.getNumber().intValue()); 21 | 22 | Assert.assertNull(val.getBool()); 23 | Assert.assertNull(val.getString()); 24 | } 25 | 26 | @Test 27 | public void modifyingValues() { 28 | Value val = new Value(1); 29 | val.set(true); 30 | 31 | Assert.assertEquals(ValueType.BOOL, val.getType()); 32 | Assert.assertNotNull(val.getBool()); 33 | Assert.assertEquals(true, val.getBool()); 34 | 35 | Assert.assertNull(val.getNumber()); 36 | Assert.assertNull(val.getString()); 37 | } 38 | 39 | /** 40 | * Ensures the following values are equal 41 | */ 42 | @Test 43 | public void equal() { 44 | Value a = new Value(1); 45 | Value b = new Value(1); 46 | Assert.assertEquals(a, b); 47 | 48 | b.setImmutable(); 49 | Assert.assertEquals(a, b); 50 | 51 | a = new Value(true); 52 | b = new Value(false); 53 | b.set(true); 54 | Assert.assertEquals(a, b); 55 | 56 | a = new Value("test"); 57 | b = new Value("test"); 58 | Assert.assertEquals(a, b); 59 | 60 | JsonArray array = new JsonArray(); 61 | a.set(array); 62 | b.set(array); 63 | Assert.assertEquals(a, b); 64 | } 65 | 66 | /** 67 | * Ensures the following values are not equal 68 | */ 69 | @Test 70 | public void notEqual() { 71 | Value a = new Value(1); 72 | Value b = new Value(true); 73 | Value c = new Value(false); 74 | Value d = new Value("string"); 75 | Value e = new Value((String) null); 76 | d.setImmutable(); 77 | 78 | Assert.assertFalse(a.equals(b)); 79 | Assert.assertFalse(a.equals(c)); 80 | Assert.assertFalse(a.equals(d)); 81 | Assert.assertFalse(a.equals(e)); 82 | 83 | Assert.assertFalse(b.equals(a)); 84 | Assert.assertFalse(b.equals(c)); 85 | Assert.assertFalse(b.equals(d)); 86 | Assert.assertFalse(b.equals(e)); 87 | 88 | Assert.assertFalse(c.equals(a)); 89 | Assert.assertFalse(c.equals(b)); 90 | Assert.assertFalse(c.equals(d)); 91 | Assert.assertFalse(c.equals(e)); 92 | 93 | Assert.assertFalse(d.equals(a)); 94 | Assert.assertFalse(d.equals(b)); 95 | Assert.assertFalse(d.equals(c)); 96 | Assert.assertFalse(d.equals(e)); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/utils/Metrics.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.utils; 2 | 3 | import org.dsa.iot.broker.Broker; 4 | import org.dsa.iot.broker.node.BrokerNode; 5 | import org.dsa.iot.dslink.node.value.Value; 6 | import org.dsa.iot.dslink.node.value.ValueType; 7 | import org.dsa.iot.dslink.util.Objects; 8 | 9 | import java.util.concurrent.ScheduledFuture; 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.concurrent.atomic.AtomicInteger; 12 | 13 | /** 14 | * @author Samuel Grenier 15 | */ 16 | public class Metrics { 17 | 18 | private final BrokerNode messagesInNode; 19 | private final BrokerNode messagesOutNode; 20 | 21 | private AtomicInteger messagesIn; 22 | private AtomicInteger messagesOut; 23 | private ScheduledFuture future; 24 | 25 | public Metrics(BrokerNode msgIn, BrokerNode msgOut) { 26 | this.messagesInNode = msgIn; 27 | this.messagesOutNode = msgOut; 28 | } 29 | 30 | public void incrementIn() { 31 | AtomicInteger in = this.messagesIn; 32 | if (in != null) { 33 | in.incrementAndGet(); 34 | } 35 | } 36 | 37 | public void incrementOut() { 38 | AtomicInteger out = this.messagesOut; 39 | if (out != null) { 40 | out.incrementAndGet(); 41 | } 42 | } 43 | 44 | public synchronized void start() { 45 | stop(); 46 | messagesIn = new AtomicInteger(); 47 | messagesOut = new AtomicInteger(); 48 | future = Objects.getDaemonThreadPool().scheduleWithFixedDelay(new Runnable() { 49 | @Override 50 | public void run() { 51 | AtomicInteger in = messagesIn; 52 | AtomicInteger out = messagesOut; 53 | if (in != null && out != null) { 54 | Value i = new Value(in.getAndSet(0)); 55 | Value o = new Value(out.getAndSet(0)); 56 | messagesInNode.setValue(i); 57 | messagesOutNode.setValue(o); 58 | } 59 | } 60 | }, 0, 1, TimeUnit.SECONDS); 61 | } 62 | 63 | public synchronized void stop() { 64 | if (future != null) { 65 | future.cancel(false); 66 | future = null; 67 | messagesIn = null; 68 | messagesOut = null; 69 | } 70 | } 71 | 72 | @SuppressWarnings("unchecked") 73 | public static Metrics create(Broker broker) { 74 | BrokerNode node = broker.tree().getRoot(); 75 | BrokerNode sys = new BrokerNode(node, "sys"); 76 | node.addChild(sys); 77 | 78 | BrokerNode msgIn = new BrokerNode(sys, "messagesInPerSecond"); 79 | msgIn.setValueType(ValueType.NUMBER); 80 | msgIn.setValue(new Value(0)); 81 | sys.addChild(msgIn); 82 | 83 | BrokerNode msgOut = new BrokerNode(sys, "messagesOutPerSecond"); 84 | msgOut.setValueType(ValueType.NUMBER); 85 | msgOut.setValue(new Value(0)); 86 | sys.addChild(msgOut); 87 | 88 | return new Metrics(msgIn, msgOut); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/methods/responses/SubscribeResponse.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.methods.responses; 2 | 3 | import org.dsa.iot.dslink.DSLink; 4 | import org.dsa.iot.dslink.DSLinkHandler; 5 | import org.dsa.iot.dslink.methods.Response; 6 | import org.dsa.iot.dslink.methods.StreamState; 7 | import org.dsa.iot.dslink.node.Node; 8 | import org.dsa.iot.dslink.node.NodeManager; 9 | import org.dsa.iot.dslink.node.NodePair; 10 | import org.dsa.iot.dslink.node.SubscriptionManager; 11 | import org.dsa.iot.dslink.util.json.JsonArray; 12 | import org.dsa.iot.dslink.util.json.JsonObject; 13 | 14 | import java.io.PrintWriter; 15 | import java.io.StringWriter; 16 | 17 | /** 18 | * @author Samuel Grenier 19 | */ 20 | public class SubscribeResponse extends Response { 21 | 22 | private final int rid; 23 | private final DSLink link; 24 | private final SubscriptionManager manager; 25 | 26 | public SubscribeResponse(int rid, DSLink link) { 27 | this.rid = rid; 28 | this.link = link; 29 | this.manager = link.getSubscriptionManager(); 30 | } 31 | 32 | @Override 33 | public int getRid() { 34 | return rid; 35 | } 36 | 37 | @Override 38 | public void populate(JsonObject in) { 39 | } 40 | 41 | @Override 42 | public JsonObject getJsonResponse(JsonObject in) { 43 | JsonArray paths = in.get("paths"); 44 | if (paths != null && paths.size() > 0) { 45 | StringBuilder builder = null; 46 | for (Object obj : paths) { 47 | try { 48 | JsonObject subData = (JsonObject) obj; 49 | String path = subData.get("path"); 50 | int sid = subData.get("sid"); 51 | int qos = subData.get("qos", 0); 52 | 53 | NodeManager nm = link.getNodeManager(); 54 | NodePair pair = nm.getNode(path, false, false); 55 | Node node = pair.getNode(); 56 | if (node == null) { 57 | DSLinkHandler h = link.getLinkHandler(); 58 | h.onSubscriptionFail(path); 59 | } 60 | manager.addValueSub(path, sid, qos); 61 | } catch (Exception e) { 62 | if (builder == null) { 63 | builder = new StringBuilder(); 64 | } 65 | 66 | StringWriter writer = new StringWriter(); 67 | e.printStackTrace(new PrintWriter(writer)); 68 | builder.append(writer.toString()); 69 | builder.append("\n"); 70 | } 71 | } 72 | if (builder != null) { 73 | throw new RuntimeException(builder.toString()); 74 | } 75 | } 76 | 77 | JsonObject obj = new JsonObject(); 78 | obj.put("rid", rid); 79 | obj.put("stream", StreamState.CLOSED.getJsonName()); 80 | return obj; 81 | } 82 | 83 | @Override 84 | public JsonObject getCloseResponse() { 85 | return null; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/json/decoders/MapDecoder.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.json.decoders; 2 | 3 | import com.fasterxml.jackson.core.JsonFactory; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonToken; 6 | import org.dsa.iot.dslink.util.json.JsonArray; 7 | import org.dsa.iot.dslink.util.json.JsonObject; 8 | 9 | import java.io.IOException; 10 | import java.util.LinkedHashMap; 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * @author Samuel Grenier 17 | */ 18 | public class MapDecoder { 19 | 20 | public static Map decode(JsonFactory factory, 21 | byte[] content, 22 | int offset, 23 | int length) { 24 | final Map map = new LinkedHashMap<>(); 25 | JsonParser parser = null; 26 | try { 27 | parser = factory.createParser(content, offset, length); 28 | parser.nextToken(); 29 | performDecodeMap(map, parser); 30 | } catch (IOException e) { 31 | throw new RuntimeException(e); 32 | } finally { 33 | if (parser != null) { 34 | try { 35 | parser.close(); 36 | } catch (IOException ignored) { 37 | } 38 | } 39 | } 40 | return map; 41 | } 42 | 43 | static void performDecodeMap(Map in, 44 | JsonParser parser) 45 | throws IOException { 46 | while (parser.nextToken() == JsonToken.FIELD_NAME) { 47 | String name = parser.getText(); 48 | JsonToken token = parser.nextToken(); 49 | if (token == null) { 50 | break; 51 | } else if (token == JsonToken.VALUE_NULL) { 52 | in.put(name, null); 53 | } else if (token == JsonToken.VALUE_STRING) { 54 | in.put(name, parser.getText()); 55 | } else if (token == JsonToken.VALUE_FALSE) { 56 | in.put(name, false); 57 | } else if (token == JsonToken.VALUE_TRUE) { 58 | in.put(name, true); 59 | } else if (token == JsonToken.VALUE_NUMBER_INT 60 | || token == JsonToken.VALUE_NUMBER_FLOAT) { 61 | in.put(name, parser.getNumberValue()); 62 | } else if (token == JsonToken.VALUE_EMBEDDED_OBJECT) { 63 | in.put(name, parser.getBinaryValue()); 64 | } else if (token == JsonToken.START_ARRAY) { 65 | List list = new LinkedList<>(); 66 | ListDecoder.performDecodeList(list, parser); 67 | in.put(name, new JsonArray(list)); 68 | } else if (token == JsonToken.START_OBJECT) { 69 | Map map = new LinkedHashMap<>(); 70 | performDecodeMap(map, parser); 71 | in.put(name, new JsonObject(map)); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/json/encoders/MapEncoder.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.json.encoders; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import org.dsa.iot.dslink.util.json.JsonArray; 5 | import org.dsa.iot.dslink.util.json.JsonObject; 6 | 7 | import java.io.IOException; 8 | import java.math.BigDecimal; 9 | import java.math.BigInteger; 10 | import java.util.Map; 11 | 12 | /** 13 | * @author Samuel Grenier 14 | */ 15 | public class MapEncoder { 16 | 17 | public static void write(JsonGenerator gen, JsonObject json) 18 | throws IOException { 19 | gen.writeStartObject(); 20 | performWrite(gen, json); 21 | } 22 | 23 | static void performWrite(JsonGenerator gen, JsonObject json) 24 | throws IOException { 25 | for (Map.Entry entry : json) { 26 | String name = entry.getKey(); 27 | Object instance = entry.getValue(); 28 | if (instance instanceof Byte) { 29 | gen.writeNumberField(name, ((Number) instance).byteValue()); 30 | } else if (instance instanceof Short) { 31 | gen.writeNumberField(name, ((Number) instance).shortValue()); 32 | } else if (instance instanceof Integer) { 33 | gen.writeNumberField(name, ((Number) instance).intValue()); 34 | } else if (instance instanceof Long) { 35 | gen.writeNumberField(name, ((Number) instance).longValue()); 36 | } else if (instance instanceof Float) { 37 | gen.writeNumberField(name, ((Number) instance).floatValue()); 38 | } else if (instance instanceof Double) { 39 | gen.writeNumberField(name, ((Number) instance).doubleValue()); 40 | } else if (instance instanceof BigDecimal) { 41 | gen.writeNumberField(name, (BigDecimal) instance); 42 | } else if (instance instanceof BigInteger) { 43 | gen.writeFieldName(name); 44 | gen.writeNumber((BigInteger) instance); 45 | } else if (instance instanceof Boolean) { 46 | gen.writeBooleanField(name, (Boolean) instance); 47 | } else if (instance instanceof String) { 48 | gen.writeStringField(name, (String) instance); 49 | } else if (instance instanceof JsonObject) { 50 | gen.writeObjectFieldStart(name); 51 | performWrite(gen, (JsonObject) instance); 52 | } else if (instance instanceof JsonArray) { 53 | gen.writeArrayFieldStart(name); 54 | ListEncoder.performWrite(gen, (JsonArray) instance); 55 | } else if (instance instanceof byte[]) { 56 | gen.writeBinaryField(name, (byte[]) instance); 57 | } else if (instance == null) { 58 | gen.writeNullField(name); 59 | } else { 60 | String err = "Unsupported class: " + instance.getClass().getName(); 61 | throw new RuntimeException(err); 62 | } 63 | } 64 | gen.writeEndObject(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sdk/dslink/src/main/java/org/dsa/iot/dslink/util/log/LogManager.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util.log; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Utility for setting the default log level for all loggers. 7 | * 8 | * @author Samuel Grenier 9 | */ 10 | public class LogManager { 11 | 12 | private static volatile LogBridge instance; 13 | 14 | /** 15 | * Replaces the default log manager with a custom log manager. This 16 | * allows for use with custom slf4j back ends. 17 | * 18 | * @param logBridge Custom log manager to set. 19 | */ 20 | public static void setBridge(LogBridge logBridge) { 21 | if (logBridge == null) { 22 | throw new NullPointerException("logBridge"); 23 | } 24 | 25 | instance = logBridge; 26 | } 27 | 28 | private static LogBridge getBridge() { 29 | if (instance == null) { 30 | setBridge(new LoggingBridge()); 31 | } 32 | return instance; 33 | } 34 | 35 | public static void setLevel(String level) { 36 | if (level == null) { 37 | throw new NullPointerException("level"); 38 | } 39 | level = level.toLowerCase(); 40 | switch (level) { 41 | case "none": case "off": 42 | setLevel(LogLevel.OFF); 43 | break; 44 | case "error": 45 | setLevel(LogLevel.ERROR); 46 | break; 47 | case "warn": 48 | setLevel(LogLevel.WARN); 49 | break; 50 | case "info": 51 | setLevel(LogLevel.INFO); 52 | break; 53 | case "debug": 54 | setLevel(LogLevel.DEBUG); 55 | break; 56 | case "trace": 57 | setLevel(LogLevel.TRACE); 58 | break; 59 | default: 60 | throw new RuntimeException("Unknown log level: " + level); 61 | } 62 | } 63 | 64 | /** 65 | * Sets the global logging level 66 | * 67 | * @param level Level to set 68 | */ 69 | public static void setLevel(LogLevel level) { 70 | if (level == null) { 71 | throw new NullPointerException("level"); 72 | } 73 | getBridge().setLevel(level); 74 | } 75 | 76 | /** 77 | * Configures the root logger with a different layout. 78 | * 79 | * @param logPath Path where the logger should output to or {@code null} to 80 | * log to standard output steams. 81 | */ 82 | public static void configure(File logPath) { 83 | getBridge().configure(logPath); 84 | } 85 | 86 | /** 87 | * Retrieves the root logging level, which may also be the global 88 | * root level. 89 | * 90 | * @return Root logger level 91 | */ 92 | public static LogLevel getLevel() { 93 | return getBridge().getLevel(); 94 | } 95 | 96 | /** 97 | * Protected to allow sub-classes to call the constructor but don't 98 | * permit outsiders to construct LogManager instances. 99 | */ 100 | protected LogManager() { 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /sdk/dslink/src/test/java/org/dsa/iot/dslink/util/ObjectsTest.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.dslink.util; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.lang.Thread.UncaughtExceptionHandler; 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.ScheduledFuture; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * @author Samuel Grenier 13 | */ 14 | public class ObjectsTest { 15 | 16 | @Test 17 | public void nonNullObjects() { 18 | Objects.setDaemonThreadPool(null); 19 | Objects.setThreadPool(null); 20 | 21 | Assert.assertNotNull(Objects.getDaemonThreadPool()); 22 | Assert.assertNotNull(Objects.getThreadPool()); 23 | } 24 | 25 | @Test 26 | public void execution() { 27 | final CountDownLatch latch = new CountDownLatch(1); 28 | Objects.getThreadPool().execute(new Runnable() { 29 | @Override 30 | public void run() { 31 | latch.countDown(); 32 | } 33 | }); 34 | 35 | try { 36 | if (!latch.await(1, TimeUnit.SECONDS)) { 37 | Assert.fail("Execution of the Runnable failed"); 38 | } 39 | } catch (InterruptedException e) { 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | 44 | @Test 45 | public void afterExecuteException() { 46 | final CountDownLatch latch = new CountDownLatch(2); 47 | 48 | Objects.getDaemonThreadPool().execute(new Runnable() { 49 | @Override 50 | public void run() { 51 | Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { 52 | @Override 53 | public void uncaughtException(Thread t, Throwable e) { 54 | latch.countDown(); 55 | } 56 | }); 57 | 58 | Assert.assertTrue(Thread.currentThread().isDaemon()); 59 | throw new RuntimeException(); 60 | } 61 | }); 62 | 63 | Objects.getThreadPool().execute(new Runnable() { 64 | @Override 65 | public void run() { 66 | Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { 67 | @Override 68 | public void uncaughtException(Thread t, Throwable e) { 69 | latch.countDown(); 70 | } 71 | }); 72 | 73 | Assert.assertFalse(Thread.currentThread().isDaemon()); 74 | throw new RuntimeException(); 75 | } 76 | }); 77 | 78 | try { 79 | if (!latch.await(1, TimeUnit.SECONDS)) { 80 | Assert.fail("Exception(s) failed to propagate"); 81 | } 82 | } catch (InterruptedException e) { 83 | throw new RuntimeException(e); 84 | } 85 | } 86 | 87 | @Test 88 | public void ignoredCancellation() { 89 | ScheduledFuture fut = Objects.getThreadPool().schedule(new Runnable() { 90 | @Override 91 | public void run() { 92 | } 93 | }, 10, TimeUnit.SECONDS); 94 | fut.cancel(true); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /sdk/broker/src/main/java/org/dsa/iot/broker/server/client/ClientManager.java: -------------------------------------------------------------------------------- 1 | package org.dsa.iot.broker.server.client; 2 | 3 | import org.dsa.iot.dslink.util.Objects; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.concurrent.ScheduledFuture; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * @author Samuel Grenier 12 | */ 13 | public class ClientManager { 14 | 15 | private final Object lock = new Object(); 16 | private Map pendingClients = new HashMap<>(); 17 | private Map connectedClients = new HashMap<>(); 18 | 19 | public void clientConnecting(Client client) { 20 | if (client == null) { 21 | throw new NullPointerException("client"); 22 | } 23 | synchronized (lock) { 24 | String key = client.handshake().dsId(); 25 | TimedClient value = new TimedClient(key, client); 26 | value.start(30, TimeUnit.SECONDS); 27 | pendingClients.put(key, value); 28 | } 29 | } 30 | 31 | public void clientConnected(Client client) { 32 | synchronized (lock) { 33 | String dsId = client.handshake().dsId(); 34 | TimedClient c = pendingClients.remove(dsId); 35 | if (c != null) { 36 | c.expireEarly(); 37 | } 38 | 39 | { 40 | Client old = connectedClients.put(dsId, client); 41 | if (old != null) { 42 | old.close(); 43 | } 44 | } 45 | } 46 | client.broker().tree().connected(client); 47 | } 48 | 49 | public void clientDisconnected(Client client) { 50 | synchronized (lock) { 51 | String dsId = client.handshake().dsId(); 52 | Client old = connectedClients.remove(dsId); 53 | if (old != null) { 54 | old.close(); 55 | } 56 | } 57 | client.broker().tree().disconnected(client); 58 | } 59 | 60 | public Client getPendingClient(String dsId) { 61 | synchronized (lock) { 62 | TimedClient c = pendingClients.get(dsId); 63 | return c != null ? c.value : null; 64 | } 65 | } 66 | 67 | private class TimedClient { 68 | 69 | private String key; 70 | private Client value; 71 | 72 | private ScheduledFuture fut; 73 | 74 | public TimedClient(String key, Client value) { 75 | this.key = key; 76 | this.value = value; 77 | 78 | } 79 | 80 | public void start(long time, TimeUnit unit) { 81 | fut = Objects.getDaemonThreadPool().schedule(new Runnable() { 82 | @Override 83 | public void run() { 84 | synchronized (lock) { 85 | pendingClients.remove(key); 86 | } 87 | expireEarly(); 88 | } 89 | }, time, unit); 90 | } 91 | 92 | public void expireEarly() { 93 | if (fut != null) { 94 | fut.cancel(true); 95 | } 96 | fut = null; 97 | key = null; 98 | value = null; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | --------------------------------------------------------------------------------