From 06acc9507a1d31fa619f556985bf9b3e61000c7a Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 27 Jan 2018 17:46:04 -0800 Subject: [PATCH 1/3] Misc Refactoring before the big changes. --- .../org/iot/dsa/dslink/DSMainNode.html | 360 ++++++++++++++ .../javadoc/org/iot/dsa/node/DSIStorable.html | 247 ++++++++++ .../javadoc/org/iot/dsa/node/DSValueNode.html | 462 ++++++++++++++++++ .../org/iot/dsa/node/event/DSIEvent.html | 175 +++++++ .../org/iot/dsa/node/event/DSISubscriber.html | 283 +++++++++++ .../iot/dsa/node/event/DSInfoTopic.Event.html | 366 ++++++++++++++ .../org/iot/dsa/node/event/DSInfoTopic.html | 285 +++++++++++ .../org/iot/dsa/node/event/DSTopic.html | 344 +++++++++++++ .../dsa/node/event/DSValueTopic.Event.html | 351 +++++++++++++ .../org/iot/dsa/node/event/DSValueTopic.html | 287 +++++++++++ .../org/iot/dsa/node/event/package-frame.html | 32 ++ .../iot/dsa/node/event/package-summary.html | 200 ++++++++ .../org/iot/dsa/node/event/package-tree.html | 158 ++++++ .../iot/dsa/dslink/DSResponderSession.java | 18 - .../com/acuity/iot/dsa/dslink/DSSession.java | 322 +++--------- .../DS1Stream.java => DSStream.java} | 4 +- .../protocol_v1/DS1LinkConnection.java | 110 +++-- .../protocol/protocol_v1/DS1Session.java | 191 ++++++-- .../responder/DS1InboundInvoke.java | 4 +- .../protocol_v1/responder/DS1InboundList.java | 4 +- .../responder/DS1InboundSubscription.java | 3 +- .../protocol_v1/responder/DS1Responder.java | 20 +- .../protocol_v2/DS2LinkConnection.java | 179 ++++--- .../protocol/protocol_v2/DS2Session.java | 166 +++++++ .../protocol_v2/MessageConstants.java | 26 +- .../protocol/protocol_v2/MessageReader.java | 21 +- .../protocol_v2/responder/DS2Responder.java | 216 ++++++++ .../org/iot/dsa/dslink/DSLinkConnection.java | 79 +-- .../org/iot/dsa/dslink/DSLinkSession.java | 22 - .../iot/dsa/dslink/DSPermissionException.java | 36 -- .../iot/dsa/dslink/DSRequestException.java | 28 -- .../org/iot/dsa/dslink/InvokeWrapper.java | 5 - .../org/iot/dsa/dslink/ListSubscriber.java | 10 +- .../java/org/iot/dsa/dslink/ListWrapper.java | 5 - .../java/org/iot/dsa/dslink/SetWrapper.java | 5 - .../org/iot/dsa/dslink/SubscribeWrapper.java | 5 - .../org/iot/dsa/dslink/ValueSubscriber.java | 14 +- .../dsa/dslink/responder/InboundRequest.java | 8 - .../org/iot/dsa/logging/AsyncLogHandler.java | 9 +- .../main/java/org/iot/dsa/node/DSInfo.java | 27 +- .../java/org/iot/dsa/node/DSInfoProxy.java | 7 +- .../main/java/org/iot/dsa/node/DSNode.java | 96 ++-- .../java/org/iot/dsa/node/DSValueNode.java | 2 +- .../java/org/iot/dsa/node/event/DSIEvent.java | 2 +- .../org/iot/dsa/node/event/DSISubscriber.java | 12 +- .../java/org/iot/dsa/node/event/DSTopic.java | 2 +- .../main/java/org/iot/dsa/util/DSUtil.java | 2 +- .../java/org/iot/dsa/dslink/LoggingTest.java | 2 +- .../java/org/iot/dsa/dslink/PerfTest.java | 124 +++++ .../dsa/dslink/RequesterSubscribeTest.java | 1 + dslink-java-v2-test/dslink.json | 45 -- .../org/iot/dsa/dslink/test/MainNode.java | 2 +- 52 files changed, 4630 insertions(+), 754 deletions(-) create mode 100644 docs/javadoc/org/iot/dsa/dslink/DSMainNode.html create mode 100644 docs/javadoc/org/iot/dsa/node/DSIStorable.html create mode 100644 docs/javadoc/org/iot/dsa/node/DSValueNode.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/DSIEvent.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/DSISubscriber.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/DSInfoTopic.Event.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/DSInfoTopic.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/DSTopic.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/DSValueTopic.Event.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/DSValueTopic.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/package-frame.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/package-summary.html create mode 100644 docs/javadoc/org/iot/dsa/node/event/package-tree.html delete mode 100644 dslink-core/src/main/java/com/acuity/iot/dsa/dslink/DSResponderSession.java rename dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/{protocol_v1/DS1Stream.java => DSStream.java} (79%) create mode 100644 dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/DS2Session.java create mode 100644 dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/responder/DS2Responder.java delete mode 100644 dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkSession.java create mode 100644 dslink-core/src/test/java/org/iot/dsa/dslink/PerfTest.java diff --git a/docs/javadoc/org/iot/dsa/dslink/DSMainNode.html b/docs/javadoc/org/iot/dsa/dslink/DSMainNode.html new file mode 100644 index 00000000..029f5644 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/dslink/DSMainNode.html @@ -0,0 +1,360 @@ + + + + + +DSMainNode (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.dslink
+

Class DSMainNode

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    java.lang.Iterable<DSInfo>, DSIObject
    +
    +
    +
    +
    public class DSMainNode
    +extends DSNode
    +
    The root DSNode that triggers a link's custom functionality . Most links will subclass this and + override declareDefaults() to bind their application logic.
    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        DSMainNode

        +
        public DSMainNode()
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        getLogger

        +
        protected java.util.logging.Logger getLogger(java.lang.String name)
        +
        Creates a child logger of the link.
        +
      • +
      + + + +
        +
      • +

        getLink

        +
        public DSLink getLink()
        +
        The parent link or null.
        +
      • +
      + + + +
        +
      • +

        validateParent

        +
        public void validateParent(DSNode node)
        +
        The parent must be a DSLink instance.
        +
        +
        Overrides:
        +
        validateParent in class DSNode
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/DSIStorable.html b/docs/javadoc/org/iot/dsa/node/DSIStorable.html new file mode 100644 index 00000000..376741dd --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/DSIStorable.html @@ -0,0 +1,247 @@ + + + + + +DSIStorable (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node
+

Interface DSIStorable

+
+
+
+ +
+
+
    +
  • + + +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        restore

        +
        DSIValue restore(DSElement element)
        +
        Deserialize a value from the configuration database, these will be values returned from the + store() method.
        +
      • +
      + + + +
        +
      • +

        store

        +
        DSElement store()
        +
        Serialize the value for the configuration database. Can be a different element type than + toElement().
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/DSValueNode.html b/docs/javadoc/org/iot/dsa/node/DSValueNode.html new file mode 100644 index 00000000..c10ea265 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/DSValueNode.html @@ -0,0 +1,462 @@ + + + + + +DSValueNode (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node
+

Class DSValueNode

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    java.lang.Iterable<DSInfo>, DSIObject, DSIValue
    +
    +
    +
    +
    public abstract class DSValueNode
    +extends DSNode
    +implements DSIValue
    +
    A convenience implementation of a node that is also a value. The value of the node must be + stored as a child. Subclasses only need to override is getValueChild()
    +
    +
    See Also:
    +
    getValueChild()
    +
    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        DSValueNode

        +
        public DSValueNode()
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        onChildChanged

        +
        public void onChildChanged(DSInfo child)
        +
        This fires the NODE_CHANGED topic when the value child changes. Overrides should call + super.onChildChanged.
        +
        +
        Overrides:
        +
        onChildChanged in class DSNode
        +
        +
      • +
      + + + + + + + +
        +
      • +

        getValueChild

        +
        public abstract DSInfo getValueChild()
        +
        Subclasses must store the node value in a child value and provide the info for that child + here. This method will be called often, it would be best to cache the info instance + rather then doing a name lookup each time.
        +
      • +
      + + + +
        +
      • +

        toElement

        +
        public DSElement toElement()
        +
        Description copied from interface: DSIValue
        +
        The current value should convert itself to an element for DSA interop such as subscription + updates, and setting requests. This is not for configuration database serialization.
        +
        +
        Specified by:
        +
        toElement in interface DSIValue
        +
        +
      • +
      + + + +
        +
      • +

        onSet

        +
        public void onSet(DSIValue value)
        +
        Description copied from class: DSNode
        +
        Override point, called only when a DSNode subclass implements DSIValue is being set. This + will throw and IllegalStateException if not overridden. You should store the value of the + node as a child and call put in override implementation. Throw an exception to + report an error to the requester.
        +
        +
        Overrides:
        +
        onSet in class DSNode
        +
        Parameters:
        +
        value - The new value.
        +
        See Also:
        +
        DSIResponder.onSet(InboundSetRequest)
        +
        +
      • +
      + + + +
        +
      • +

        valueOf

        +
        public DSIValue valueOf(DSElement element)
        +
        Description copied from interface: DSIValue
        +
        This should convert an element transmitted over DSA, such as subscription updates or set + requests. This is not for configuration database deserialization.
        +
        +
        Specified by:
        +
        valueOf in interface DSIValue
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/DSIEvent.html b/docs/javadoc/org/iot/dsa/node/event/DSIEvent.html new file mode 100644 index 00000000..a991ca36 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/DSIEvent.html @@ -0,0 +1,175 @@ + + + + + +DSIEvent (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node.event
+

Interface DSIEvent

+
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/DSISubscriber.html b/docs/javadoc/org/iot/dsa/node/event/DSISubscriber.html new file mode 100644 index 00000000..131aacd6 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/DSISubscriber.html @@ -0,0 +1,283 @@ + + + + + +DSISubscriber (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node.event
+

Interface DSISubscriber

+
+
+
+
    +
  • +
    +
    +
    public interface DSISubscriber
    +
    DSISubscribers subscribe to DSTopics on DSNodes. +

    + A single topic can emanate multiple types of events. The event object is an empty interface, + each topic will define it's own events. +

    + Events can also supply parameters. The parameters specific to each kind of event. +

    + See the class documentation of a specific topic to understand the possible events and their + parameters. +

    + The two most important topics are built into every node, they are: DSValueTopic and DSInfoTopic.

    +
    +
    See Also:
    +
    DSIEvent, +DSTopic, +DSNode.subscribe(DSInfo, DSISubscriber, DSTopic)
    +
    +
  • +
+
+
+ +
+
+
    +
  • + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        onEvent

        +
        void onEvent(DSNode node,
        +             DSInfo child,
        +             DSTopic topic,
        +             DSIEvent event,
        +             java.lang.Object... params)
        +
        Subscription callback.
        +
        +
        Parameters:
        +
        node - Required, node subscribed to.
        +
        child - Optional, if the event concerns a child.
        +
        topic - Required, the topic emanating the event.
        +
        event - Required, the actual event.
        +
        params - Can be null, only used if the event defines it.
        +
        +
      • +
      + + + +
        +
      • +

        onUnsubscribed

        +
        void onUnsubscribed(DSNode node,
        +                    DSInfo child,
        +                    DSTopic topic)
        +
        Called no matter how the unsubscribe happens, whether explicitly or if the node + unsubscribes itself.
        +
        +
        Parameters:
        +
        node - Node that was passed to DSNode.subscribe, never null.
        +
        child - The child that was passed to DSNode.subscribe, may be null.
        +
        topic - The topic that was passed to DSNode.subscribe.
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/DSInfoTopic.Event.html b/docs/javadoc/org/iot/dsa/node/event/DSInfoTopic.Event.html new file mode 100644 index 00000000..7e3c5dfd --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/DSInfoTopic.Event.html @@ -0,0 +1,366 @@ + + + + + +DSInfoTopic.Event (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node.event
+

Enum DSInfoTopic.Event

+
+
+ +
+ +
+
+
    +
  • + +
      +
    • + + +

      Enum Constant Summary

      + + + + + + + + + + + + + + +
      Enum Constants 
      Enum Constant and Description
      CHILD_ADDED +
      Events will have a single parameter, the info of the new child.
      +
      CHILD_REMOVED +
      Events will have a single parameter, the unparented info of the child.
      +
      METADATA_CHANGED +
      TBD.
      +
      +
    • +
    + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + + + + + +
      All Methods Static Methods Concrete Methods 
      Modifier and TypeMethod and Description
      static DSInfoTopic.EventvalueOf(java.lang.String name) +
      Returns the enum constant of this type with the specified name.
      +
      static DSInfoTopic.Event[]values() +
      Returns an array containing the constants of this enum type, in +the order they are declared.
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Enum

        +clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
      • +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +getClass, notify, notifyAll, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Enum Constant Detail

      + + + +
        +
      • +

        CHILD_ADDED

        +
        public static final DSInfoTopic.Event CHILD_ADDED
        +
        Events will have a single parameter, the info of the new child.
        +
      • +
      + + + +
        +
      • +

        CHILD_REMOVED

        +
        public static final DSInfoTopic.Event CHILD_REMOVED
        +
        Events will have a single parameter, the unparented info of the child.
        +
      • +
      + + + +
        +
      • +

        METADATA_CHANGED

        +
        public static final DSInfoTopic.Event METADATA_CHANGED
        +
        TBD. Node? Child? Both?
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        values

        +
        public static DSInfoTopic.Event[] values()
        +
        Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
        +for (DSInfoTopic.Event c : DSInfoTopic.Event.values())
        +    System.out.println(c);
        +
        +
        +
        Returns:
        +
        an array containing the constants of this enum type, in the order they are declared
        +
        +
      • +
      + + + +
        +
      • +

        valueOf

        +
        public static DSInfoTopic.Event valueOf(java.lang.String name)
        +
        Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.)
        +
        +
        Parameters:
        +
        name - the name of the enum constant to be returned.
        +
        Returns:
        +
        the enum constant with the specified name
        +
        Throws:
        +
        java.lang.IllegalArgumentException - if this enum type has no constant with the specified name
        +
        java.lang.NullPointerException - if the argument is null
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/DSInfoTopic.html b/docs/javadoc/org/iot/dsa/node/event/DSInfoTopic.html new file mode 100644 index 00000000..a1b04649 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/DSInfoTopic.html @@ -0,0 +1,285 @@ + + + + + +DSInfoTopic (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node.event
+

Class DSInfoTopic

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    DSIObject
    +
    +
    +
    +
    public class DSInfoTopic
    +extends DSTopic
    +
    This topic is for info related events on DSNodes. +

    + Events will be on of the enum defined in the Event inner class. The parameters for each event + are defined in the documentation for each event.

    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Nested Class Summary

      + + + + + + + + + + +
      Nested Classes 
      Modifier and TypeClass and Description
      static class DSInfoTopic.Event +
      The possible events for this topic.
      +
      +
    • +
    + +
      +
    • + + +

      Field Summary

      + + + + + + + + + + +
      Fields 
      Modifier and TypeField and Description
      static DSInfoTopicINSTANCE +
      The only instance of this topic.
      +
      +
    • +
    + +
      +
    • + + +

      Method Summary

      + +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/DSTopic.html b/docs/javadoc/org/iot/dsa/node/event/DSTopic.html new file mode 100644 index 00000000..98353929 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/DSTopic.html @@ -0,0 +1,344 @@ + + + + + +DSTopic (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node.event
+

Class DSTopic

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    DSIObject
    +
    +
    +
    Direct Known Subclasses:
    +
    DSInfoTopic, DSValueTopic
    +
    +
    +
    +
    public class DSTopic
    +extends java.lang.Object
    +implements DSIObject
    +
    DSISubscribers subscribe to DSTopics on DSNodes. +

    + A single topic can emanate multiple types of events. The event object is an empty interface, + each topic will define it's own events. +

    + Events can also supply parameters. The parameters specific to each kind of event. +

    + See the class documentation of a specific topic to understand the possible events and their + parameters. +

    + The two most important topics are built into every node, they are: DSValueTopic and DSInfoTopic.

    +
    +
    See Also:
    +
    DSIEvent, +DSISubscriber, +DSInfoTopic, +DSValueTopic, +DSNode.subscribe(DSInfo, DSISubscriber, DSTopic)
    +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Constructor Summary

      + + + + + + + + +
      Constructors 
      Constructor and Description
      DSTopic() 
      +
    • +
    + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + + + + + + + + + +
      All Methods Instance Methods Concrete Methods 
      Modifier and TypeMethod and Description
      DSTopiccopy() +
      Returns this.
      +
      booleanisEqual(java.lang.Object obj) +
      Only test instance equality.
      +
      booleanisNull() +
      False
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Constructor Detail

      + + + +
        +
      • +

        DSTopic

        +
        public DSTopic()
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        copy

        +
        public DSTopic copy()
        +
        Returns this.
        +
        +
        Specified by:
        +
        copy in interface DSIObject
        +
        +
      • +
      + + + +
        +
      • +

        isEqual

        +
        public boolean isEqual(java.lang.Object obj)
        +
        Only test instance equality.
        +
        +
        Specified by:
        +
        isEqual in interface DSIObject
        +
        +
      • +
      + + + +
        +
      • +

        isNull

        +
        public boolean isNull()
        +
        False
        +
        +
        Specified by:
        +
        isNull in interface DSIObject
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/DSValueTopic.Event.html b/docs/javadoc/org/iot/dsa/node/event/DSValueTopic.Event.html new file mode 100644 index 00000000..c55282ea --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/DSValueTopic.Event.html @@ -0,0 +1,351 @@ + + + + + +DSValueTopic.Event (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node.event
+

Enum DSValueTopic.Event

+
+
+ +
+ +
+
+
    +
  • + +
      +
    • + + +

      Enum Constant Summary

      + + + + + + + + + + + +
      Enum Constants 
      Enum Constant and Description
      CHILD_CHANGED +
      For node value children (who are not also nodes).
      +
      NODE_CHANGED +
      For DSNodes that implement DSIValue.
      +
      +
    • +
    + +
      +
    • + + +

      Method Summary

      + + + + + + + + + + + + + + +
      All Methods Static Methods Concrete Methods 
      Modifier and TypeMethod and Description
      static DSValueTopic.EventvalueOf(java.lang.String name) +
      Returns the enum constant of this type with the specified name.
      +
      static DSValueTopic.Event[]values() +
      Returns an array containing the constants of this enum type, in +the order they are declared.
      +
      +
        +
      • + + +

        Methods inherited from class java.lang.Enum

        +clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
      • +
      +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +getClass, notify, notifyAll, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Enum Constant Detail

      + + + +
        +
      • +

        NODE_CHANGED

        +
        public static final DSValueTopic.Event NODE_CHANGED
        +
        For DSNodes that implement DSIValue. The DSInfo arg to onEvent must be null.
        +
      • +
      + + + +
        +
      • +

        CHILD_CHANGED

        +
        public static final DSValueTopic.Event CHILD_CHANGED
        +
        For node value children (who are not also nodes). The DSInfo arg must not be null.
        +
      • +
      +
    • +
    + +
      +
    • + + +

      Method Detail

      + + + +
        +
      • +

        values

        +
        public static DSValueTopic.Event[] values()
        +
        Returns an array containing the constants of this enum type, in +the order they are declared. This method may be used to iterate +over the constants as follows: +
        +for (DSValueTopic.Event c : DSValueTopic.Event.values())
        +    System.out.println(c);
        +
        +
        +
        Returns:
        +
        an array containing the constants of this enum type, in the order they are declared
        +
        +
      • +
      + + + +
        +
      • +

        valueOf

        +
        public static DSValueTopic.Event valueOf(java.lang.String name)
        +
        Returns the enum constant of this type with the specified name. +The string must match exactly an identifier used to declare an +enum constant in this type. (Extraneous whitespace characters are +not permitted.)
        +
        +
        Parameters:
        +
        name - the name of the enum constant to be returned.
        +
        Returns:
        +
        the enum constant with the specified name
        +
        Throws:
        +
        java.lang.IllegalArgumentException - if this enum type has no constant with the specified name
        +
        java.lang.NullPointerException - if the argument is null
        +
        +
      • +
      +
    • +
    +
  • +
+
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/DSValueTopic.html b/docs/javadoc/org/iot/dsa/node/event/DSValueTopic.html new file mode 100644 index 00000000..40de301c --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/DSValueTopic.html @@ -0,0 +1,287 @@ + + + + + +DSValueTopic (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + + +
+
org.iot.dsa.node.event
+

Class DSValueTopic

+
+
+ +
+
    +
  • +
    +
    All Implemented Interfaces:
    +
    DSIObject, DSIEvent
    +
    +
    +
    +
    public class DSValueTopic
    +extends DSTopic
    +implements DSIEvent
    +
    This topic is for change of value events on DSNodes. There are two types of events, + NODE_CHANGED for nodes that implement DSIValue and CHILD_CHANGED for child values of the + subscribed node. +

    + Events will be one of the enums defined in the Event inner class.

    +
  • +
+
+
+
    +
  • + +
      +
    • + + +

      Nested Class Summary

      + + + + + + + + + + +
      Nested Classes 
      Modifier and TypeClass and Description
      static class DSValueTopic.Event +
      The possible events from this topic.
      +
      +
    • +
    + +
      +
    • + + +

      Field Summary

      + + + + + + + + + + +
      Fields 
      Modifier and TypeField and Description
      static DSValueTopicINSTANCE +
      The only instance of this topic.
      +
      +
    • +
    + +
      +
    • + + +

      Method Summary

      + +
        +
      • + + +

        Methods inherited from class java.lang.Object

        +clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • +
      +
    • +
    +
  • +
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/package-frame.html b/docs/javadoc/org/iot/dsa/node/event/package-frame.html new file mode 100644 index 00000000..b56ac514 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/package-frame.html @@ -0,0 +1,32 @@ + + + + + +org.iot.dsa.node.event (dslink-core 0.14.0 API) + + + + + +

org.iot.dsa.node.event

+
+

Interfaces

+ +

Classes

+ +

Enums

+ +
+ + diff --git a/docs/javadoc/org/iot/dsa/node/event/package-summary.html b/docs/javadoc/org/iot/dsa/node/event/package-summary.html new file mode 100644 index 00000000..d1a6e524 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/package-summary.html @@ -0,0 +1,200 @@ + + + + + +org.iot.dsa.node.event (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Package org.iot.dsa.node.event

+
+
+ +
+ +
+ + + + + + + +
+ + + + diff --git a/docs/javadoc/org/iot/dsa/node/event/package-tree.html b/docs/javadoc/org/iot/dsa/node/event/package-tree.html new file mode 100644 index 00000000..949f2053 --- /dev/null +++ b/docs/javadoc/org/iot/dsa/node/event/package-tree.html @@ -0,0 +1,158 @@ + + + + + +org.iot.dsa.node.event Class Hierarchy (dslink-core 0.14.0 API) + + + + + + + + +
+ + + + + + + +
+ + +
+

Hierarchy For Package org.iot.dsa.node.event

+Package Hierarchies: + +
+
+

Class Hierarchy

+ +

Interface Hierarchy

+ +

Enum Hierarchy

+ +
+ +
+ + + + + + + +
+ + + + diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/DSResponderSession.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/DSResponderSession.java deleted file mode 100644 index 6dc0f3c7..00000000 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/DSResponderSession.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.acuity.iot.dsa.dslink; - -import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; -import org.iot.dsa.dslink.DSLinkSession; - -/** - * The session responders will use to submit responses. - * - * @author Aaron Hansen - */ -public interface DSResponderSession extends DSLinkSession { - - /** - * Append the message to the outgoing queue. - */ - public void sendResponse(OutboundMessage res); - -} diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/DSSession.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/DSSession.java index 215c8109..df6d7990 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/DSSession.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/DSSession.java @@ -1,16 +1,12 @@ package com.acuity.iot.dsa.dslink; import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; -import com.acuity.iot.dsa.dslink.protocol.protocol_v1.DS1LinkConnection; import com.acuity.iot.dsa.dslink.transport.DSTransport; -import java.io.IOException; import java.util.LinkedList; import java.util.List; -import java.util.logging.Level; import java.util.logging.Logger; import org.iot.dsa.dslink.DSIRequester; -import org.iot.dsa.io.DSIReader; -import org.iot.dsa.io.DSIWriter; +import org.iot.dsa.dslink.DSLinkConnection; import org.iot.dsa.node.DSNode; /** @@ -21,158 +17,53 @@ */ public abstract class DSSession extends DSNode { - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// - - static final int END_MSG_THRESHOLD = 32768; - static final int MAX_MSG_TIME = 3000; - /////////////////////////////////////////////////////////////////////////// // Fields /////////////////////////////////////////////////////////////////////////// - private boolean active = false; - private DS1LinkConnection connection; - private long lastMessageSent; + private boolean connected = false; + private DSLinkConnection connection; private Logger logger; private Object outgoingMutex = new Object(); private List outgoingRequests = new LinkedList(); private List outgoingResponses = new LinkedList(); protected boolean requesterAllowed = false; - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////////// - /** - * Prepare a new message. After this is called, beginRequests and/beginResponses maybe called. - * When the message is complete, endMessage will be called. - * - * @see #beginRequests() - * @see #beginResponses() - * @see #endMessage() - */ - protected abstract void beginMessage(); - - /** - * Prepare for the response portion of the message. This will be followed by one or more calls - * to writeResponse(). When there are no more responses, endResponses() will be called. - * - * @see #writeResponse(OutboundMessage) - * @see #endResponses() - */ - protected abstract void beginResponses(); - - /** - * Prepare for the request portion of the message. This will be followed by one or more calls - * to writeRequest(). When there are no more responses, endRequests() will be called. - * - * @see #writeResponse(OutboundMessage) - * @see #endResponses() - */ - protected abstract void beginRequests(); - /** * Can be called by the subclass to force exit the run method. */ - public void close() { - try { - active = false; - synchronized (outgoingMutex) { - outgoingRequests.clear(); - outgoingResponses.clear(); - outgoingMutex.notify(); - } - } catch (Exception x) { - fine(connection.getConnectionId(), x); + public void disconnect() { + if (!connected) { + return; } - } - - /** - * The protocol implementation should read messages and do something with them. The - * implementation should call isOpen() to determine when to exit this method. - * - * @see #isOpen() - */ - protected abstract void doRead() throws IOException; - - /** - * The run method thread spawns a thread which then executes this method for writing outgoing - * requests and responses. - */ - private void doWrite() { - long endTime; - boolean requestsFirst = false; - DSTransport transport = getTransport(); - lastMessageSent = System.currentTimeMillis(); - DSIWriter writer = getWriter(); - try { - while (active) { - synchronized (outgoingMutex) { - if (!hasSomethingToSend()) { - try { - outgoingMutex.wait(1000); - } catch (InterruptedException ignore) { - } - continue; - } - } - endTime = System.currentTimeMillis() + MAX_MSG_TIME; - //alternate the send requests or responses first each time - writer.reset(); - requestsFirst = !requestsFirst; - transport.beginMessage(); - beginMessage(); - if (hasMessagesToSend()) { - send(requestsFirst, endTime); - if (active && - (System.currentTimeMillis() < endTime) && !shouldEndMessage()) { - send(!requestsFirst, endTime); - } - } - endMessage(); - transport.endMessage(); - lastMessageSent = System.currentTimeMillis(); - } - } catch (Exception x) { - if (active) { - getLogger().log(Level.FINE, getConnection().getConnectionId(), x); - active = false; - transport.close(); - } + connected = false; + getTransport().close(); + synchronized (outgoingMutex) { + notifyAll(); } + info(getPath() + " locally closed"); } /** - * Complete the message. + * The subclass should read and process a single message. Throw an exception to indicate + * an error. */ - protected abstract void endMessage(); + protected abstract void doRecvMessage() throws Exception; /** - * Complete the outgoing responses part of the message. - * - * @see #beginResponses() - * @see #writeResponse(OutboundMessage) + * The subclass should send a single message. Throw an exception to indicate + * an error. */ - protected abstract void endResponses(); - - /** - * Complete the outgoing requests part of the message. - * - * @see #beginRequests() - * @see #writeRequest(OutboundMessage) - */ - protected abstract void endRequests(); + protected abstract void doSendMessage() throws Exception; /** * Can return null. */ - private OutboundMessage dequeueOutgoingResponse() { + protected OutboundMessage dequeueOutgoingResponse() { synchronized (outgoingMutex) { if (!outgoingResponses.isEmpty()) { return outgoingResponses.remove(0); @@ -184,7 +75,7 @@ private OutboundMessage dequeueOutgoingResponse() { /** * Can return null. */ - private OutboundMessage dequeueOutgoingRequest() { + protected OutboundMessage dequeueOutgoingRequest() { synchronized (outgoingMutex) { if (!outgoingRequests.isEmpty()) { return outgoingRequests.remove(0); @@ -197,7 +88,7 @@ private OutboundMessage dequeueOutgoingRequest() { * Add a message to the outgoing request queue. */ public void enqueueOutgoingRequest(OutboundMessage arg) { - if (active) { + if (connected) { if (!requesterAllowed) { throw new IllegalStateException("Requests forbidden"); } @@ -212,7 +103,7 @@ public void enqueueOutgoingRequest(OutboundMessage arg) { * Add a message to the outgoing response queue. */ public void enqueueOutgoingResponse(OutboundMessage arg) { - if (active) { + if (connected) { synchronized (outgoingMutex) { outgoingResponses.add(arg); outgoingMutex.notify(); @@ -220,17 +111,10 @@ public void enqueueOutgoingResponse(OutboundMessage arg) { } } - public DS1LinkConnection getConnection() { + public DSLinkConnection getConnection() { return connection; } - /** - * The time the lastRun message was completed. - */ - protected long getLastMessageSent() { - return lastMessageSent; - } - @Override public Logger getLogger() { if (logger == null) { @@ -239,16 +123,14 @@ public Logger getLogger() { return logger; } - protected abstract DSIReader getReader(); - - protected abstract DSIRequester getRequester(); - - protected abstract DSTransport getTransport(); + public abstract DSIRequester getRequester(); - protected abstract DSIWriter getWriter(); + public DSTransport getTransport() { + return getConnection().getTransport(); + } /** - * True if there are outbound messages on the queue. + * True if there are any outbound requests or responses queued up. */ protected final boolean hasMessagesToSend() { if (!outgoingResponses.isEmpty()) { @@ -260,6 +142,14 @@ protected final boolean hasMessagesToSend() { return false; } + protected boolean hasOutgoingRequests() { + return !outgoingRequests.isEmpty(); + } + + protected boolean hasOutgoingResponses() { + return !outgoingResponses.isEmpty(); + } + /** * Override point, this returns the result of hasMessagesToSend. */ @@ -267,13 +157,8 @@ protected boolean hasSomethingToSend() { return hasMessagesToSend(); } - /** - * The subclass check this to determine when to exit the doRun method. - * - * @see #doRead() - */ - protected boolean isOpen() { - return active && getTransport().isOpen(); + protected boolean isConnected() { + return connected; } /** @@ -290,20 +175,24 @@ protected void notifyOutgoing() { * have already been set. */ public void onConnect() { - active = true; + connected = true; } /** * Override point, when a connection attempt failed. */ public void onConnectFail() { + connected = false; } /** - * Override point, the connection was closed. + * Override point, called after the connection is closed. */ public void onDisconnect() { - active = false; + synchronized (outgoingMutex) { + outgoingRequests.clear(); + outgoingResponses.clear(); + } } /** @@ -316,105 +205,30 @@ public void setRequesterAllowed() { /** * Called by the connection, this manages the running state and calls doRun for the specific * implementation. A separate thread is spun off to manage writing. - * - * @see #doRead() */ public void run() { new WriteThread(getConnection().getLink().getLinkName() + " Writer").start(); - try { - doRead(); - } catch (Exception x) { - if (active) { - fine(getConnection().getConnectionId(), x); - } - } - } - - /** - * Send messages from one of the queues. - * - * @param requests Determines which queue to use; True for outgoing requests, false for - * responses. - * @param endTime Stop after this time. - */ - private void send(boolean requests, long endTime) { - if (requests) { - if (outgoingRequests.isEmpty()) { - return; - } - beginRequests(); - } else { - if (outgoingResponses.isEmpty()) { - return; - } - beginResponses(); - } - OutboundMessage msg = requests ? dequeueOutgoingRequest() : dequeueOutgoingResponse(); - while ((msg != null) && (System.currentTimeMillis() < endTime)) { - if (requests) { - writeRequest(msg); - } else { - writeResponse(msg); - } - if (!shouldEndMessage()) { - msg = requests ? dequeueOutgoingRequest() : dequeueOutgoingResponse(); - } else { - msg = null; + while (connected) { + try { + doRecvMessage(); + } catch (Exception x) { + getTransport().close(); + if (connected) { + connected = false; + severe(getPath(), x); + } } } - if (requests) { - endRequests(); - } else { - endResponses(); - } - } - - /** - * Called when there are no outbound messages in the queue. Can be used for pinging and acks. - * - * @return True to send a message anyway. - */ - protected boolean sendEmptyMessage() { - return false; } /** * For use by the connection object. */ - public DSSession setConnection(DS1LinkConnection connection) { + public DSSession setConnection(DSLinkConnection connection) { this.connection = connection; return this; } - /** - * Returns true if the current message size has crossed a message size threshold. - */ - public boolean shouldEndMessage() { - return (getWriter().length() + getTransport().messageSize()) > END_MSG_THRESHOLD; - } - - /** - * Write a request in the current message. Can be called multiple times after beginRequests() is - * called. endRequests() will be called once the request part of the message is complete. - * - * @see #beginRequests() - * @see #endRequests() - */ - public void writeRequest(OutboundMessage message) { - message.write(getWriter()); - } - - /** - * Write a response in the current message. Can be called multiple times after beginResponses() - * is called. endResponses() will be called once the response part of the message is complete. - * - * @see #beginResponses() - * @see #endResponses() - */ - public void writeResponse(OutboundMessage message) { - message.write(getWriter()); - } - /////////////////////////////////////////////////////////////////////////// // Inner Classes /////////////////////////////////////////////////////////////////////////// @@ -430,12 +244,28 @@ private class WriteThread extends Thread { } public void run() { - doWrite(); + try { + while (connected) { + synchronized (outgoingMutex) { + if (!hasSomethingToSend()) { + try { + outgoingMutex.wait(5000); + } catch (InterruptedException x) { + fine(getPath(), x); + } + continue; + } + } + doSendMessage(); + } + } catch (Exception x) { + if (connected) { + connected = false; + getTransport().close(); + severe(getPath(), x); + } + } } } - /////////////////////////////////////////////////////////////////////////// - // Initialization - /////////////////////////////////////////////////////////////////////////// - -} //class +} diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1Stream.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/DSStream.java similarity index 79% rename from dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1Stream.java rename to dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/DSStream.java index 124b8ad1..1fb48661 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1Stream.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/DSStream.java @@ -1,11 +1,11 @@ -package com.acuity.iot.dsa.dslink.protocol.protocol_v1; +package com.acuity.iot.dsa.dslink.protocol; /** * Can be closed locally and remotely. * * @author Aaron Hansen */ -public interface DS1Stream { +public interface DSStream { /** * Use to close locally. diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1LinkConnection.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1LinkConnection.java index e0198645..b557c1e7 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1LinkConnection.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1LinkConnection.java @@ -1,15 +1,23 @@ package com.acuity.iot.dsa.dslink.protocol.protocol_v1; +import com.acuity.iot.dsa.dslink.transport.DSBinaryTransport; +import com.acuity.iot.dsa.dslink.transport.DSTextTransport; import com.acuity.iot.dsa.dslink.transport.DSTransport; import org.iot.dsa.dslink.DSIRequester; import org.iot.dsa.dslink.DSLink; import org.iot.dsa.dslink.DSLinkConfig; import org.iot.dsa.dslink.DSLinkConnection; +import org.iot.dsa.io.DSIReader; +import org.iot.dsa.io.DSIWriter; +import org.iot.dsa.io.json.JsonReader; +import org.iot.dsa.io.json.JsonWriter; +import org.iot.dsa.io.msgpack.MsgpackReader; +import org.iot.dsa.io.msgpack.MsgpackWriter; import org.iot.dsa.util.DSException; /** - * The default connection implementation. Performs connection initialization with the broker, then - * creates a transport and a protocol based on the broker response. + * The DSA V1 connection implementation. Performs connection initialization with the broker, then + * creates a transport based on the broker response. * * @author Aaron Hansen */ @@ -29,19 +37,27 @@ public class DS1LinkConnection extends DSLinkConnection { private DS1ConnectionInit connectionInit; private DSLink link; + private DSIReader reader; + private DSTransport transport; private DS1Session session; - - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// + private DSIWriter writer; /////////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////////// - @Override - public DSLink getLink() { - return link; + /** + * Forcefully closes an open connection. Does not prevent reconnection, intended for + * problem resolution. + */ + public void disconnect() { + if (session != null) { + session.disconnect(); + } + } + + public DSIReader getReader() { + return reader; } @Override @@ -49,6 +65,15 @@ public DSIRequester getRequester() { return session.getRequester(); } + @Override + public DSTransport getTransport() { + return transport; + } + + public DSIWriter getWriter() { + return writer; + } + protected DS1ConnectionInit initializeConnection() { DS1ConnectionInit init = new DS1ConnectionInit(); put(CONNECTION_INIT, init).setTransient(true); @@ -60,17 +85,6 @@ protected DS1ConnectionInit initializeConnection() { return init; } - /** - * Looks at the connection initialization response to determine the protocol implementation. - */ - protected DS1Session makeSession(DS1ConnectionInit init) { - String version = init.getResponse().get("version", ""); - if (!version.startsWith("1.1.2")) { - throw new IllegalStateException("Unsupported version: " + version); - } - return new DS1Session(); - } - /** * Looks at the connection initialization response to determine the type of transport then * instantiates the correct type fom the config. @@ -111,13 +125,13 @@ protected DSTransport makeTransport(DS1ConnectionInit init) { @Override protected void onConnect() { - //If there is a failure, then we want connection init to happen again. - DS1ConnectionInit init = connectionInit; - connectionInit = null; try { + DS1ConnectionInit init = connectionInit; + //Don't reuse the connection init if there is connection problem. + connectionInit = null; getTransport().open(); - connectionInit = init; session.onConnect(); + connectionInit = init; } catch (Exception x) { session.onConnectFail(); DSException.throwRuntime(x); @@ -126,20 +140,19 @@ protected void onConnect() { @Override protected void onDisconnect() { - if (session != null) { - session.close(); - } if (session != null) { session.onDisconnect(); } + reader = null; + writer = null; + transport = null; remove(TRANSPORT); } @Override protected void onInitialize() { - //We need to reinitialize if there are any connection failures, so - //only hold the init reference after successfully connected. DS1ConnectionInit init = connectionInit; + //Don't reuse the connection init if there is connection problem. connectionInit = null; if (init == null) { init = initializeConnection(); @@ -147,10 +160,9 @@ protected void onInitialize() { makeTransport(init); put(TRANSPORT, getTransport()).setTransient(true); if (session == null) { - session = makeSession(init); - config(config() ? "Session type: " + session.getClass().getName() : null); + session = new DS1Session(); put(SESSION, session).setTransient(true); - session.setConnection(DS1LinkConnection.this); + session.setConnection(this); } connectionInit = init; } @@ -170,6 +182,40 @@ public void setRequesterAllowed() { session.setRequesterAllowed(); } + /** + * Sets the transport and creates the appropriate reader/writer. + */ + protected void setTransport(DSTransport transport) { + if (transport instanceof DSBinaryTransport) { + final DSBinaryTransport trans = (DSBinaryTransport) transport; + reader = new MsgpackReader(trans.getInput()); + writer = new MsgpackWriter() { + @Override + public void onComplete() { + /* How to debug + try { + MsgpackReader reader = new MsgpackReader( + new ByteArrayInputStream(byteBuffer.array()); + System.out.println(reader.getMap()); + reader.close(); + } catch (Exception x) { + x.printStackTrace(); + } + */ + trans.write(byteBuffer, true); + } + }; + } else if (transport instanceof DSTextTransport) { + DSTextTransport trans = (DSTextTransport) transport; + reader = new JsonReader(trans.getReader()); + writer = new JsonWriter(trans.getWriter()); + } else { + throw new IllegalStateException( + "Unexpected transport type: " + transport.getClass().getName()); + } + this.transport = transport; + } + public void updateSalt(String salt) { connectionInit.updateSalt(salt); } diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1Session.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1Session.java index d32f88ca..422be3ed 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1Session.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/DS1Session.java @@ -8,6 +8,7 @@ import com.acuity.iot.dsa.dslink.DSProtocolException; import com.acuity.iot.dsa.dslink.DSSession; +import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; import com.acuity.iot.dsa.dslink.protocol.protocol_v1.requester.DS1Requester; import com.acuity.iot.dsa.dslink.protocol.protocol_v1.responder.DS1Responder; import com.acuity.iot.dsa.dslink.transport.DSTransport; @@ -31,6 +32,7 @@ public class DS1Session extends DSSession { // Constants /////////////////////////////////////////////////////////////////////////// + static final int END_MSG_THRESHOLD = 48000; static final String LAST_ACK_RECV = "Last Ack Recv"; static final String LAST_ACK_SENT = "Last Ack Sent"; @@ -43,23 +45,25 @@ public class DS1Session extends DSSession { private DSInfo lastAckRecv = getInfo(LAST_ACK_RECV); private DSInfo lastAckSent = getInfo(LAST_ACK_SENT); + private long lastMessageSent; private int nextAck = -1; private int nextMsg = 1; - private DSIReader reader; + private boolean requestsNext = false; private DS1Requester requester = new DS1Requester(this); private DS1Responder responder = new DS1Responder(this); - private DSTransport transport; - private DSIWriter writer; - - ///////////////////////////////////////////////////////////////// - // Constructors - ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// // Methods ///////////////////////////////////////////////////////////////// - @Override + /** + * Prepare a new message. After this is called, beginRequests and/beginResponses maybe called. + * When the message is complete, endMessage will be called. + * + * @see #beginRequests() + * @see #beginResponses() + * @see #endMessage() + */ protected void beginMessage() { DSIWriter out = getWriter(); out.beginMap(); @@ -76,12 +80,24 @@ protected void beginMessage() { } } - @Override + /** + * Prepare for the response portion of the message. This will be followed by one or more calls + * to writeResponse(). When there are no more responses, endResponses() will be called. + * + * @see #writeResponse(OutboundMessage) + * @see #endResponses() + */ protected void beginResponses() { getWriter().key("responses").beginList(); } - @Override + /** + * Prepare for the request portion of the message. This will be followed by one or more calls + * to writeRequest(). When there are no more responses, endRequests() will be called. + * + * @see #writeResponse(OutboundMessage) + * @see #endResponses() + */ protected void beginRequests() { getWriter().key("requests").beginList(); } @@ -93,43 +109,77 @@ protected void declareDefaults() { } @Override - protected void doRead() throws IOException { + protected void doRecvMessage() throws IOException { DSIReader reader = getReader(); - while (isOpen()) { - switch (reader.next()) { - case BEGIN_MAP: - processEnvelope(reader); - break; - case END_MAP: - case END_LIST: - case ROOT: - break; - case END_INPUT: - return; - default: - throw new IllegalStateException("Unexpected input: " + reader.last()); - } + switch (reader.next()) { + case BEGIN_MAP: + processEnvelope(reader); + break; + case END_MAP: + case END_LIST: + case ROOT: + break; + case END_INPUT: + return; + default: + throw new IOException("Unexpected input: " + reader.last()); } } @Override + protected void doSendMessage() { + DSTransport transport = getTransport(); + long endTime = System.currentTimeMillis() + 2000; + DSIWriter writer = getWriter(); + writer.reset(); + requestsNext = !requestsNext; + transport.beginMessage(); + beginMessage(); + if (hasMessagesToSend()) { + send(requestsNext, endTime); + if ((System.currentTimeMillis() < endTime) && !shouldEndMessage()) { + send(!requestsNext, endTime); + } + } + endMessage(); + transport.endMessage(); + lastMessageSent = System.currentTimeMillis(); + } + + /** + * Complete the message. + */ protected void endMessage() { getWriter().endMap().flush(); } - @Override + /** + * Complete the outgoing responses part of the message. + * + * @see #beginResponses() + * @see #writeResponse(OutboundMessage) + */ protected void endResponses() { getWriter().endList(); } - @Override + /** + * Complete the outgoing requests part of the message. + * + * @see #beginRequests() + * @see #writeRequest(OutboundMessage) + */ protected void endRequests() { getWriter().endList(); } @Override + public DS1LinkConnection getConnection() { + return (DS1LinkConnection) super.getConnection(); + } + public DSIReader getReader() { - return reader; + return getConnection().getReader(); } @Override @@ -137,18 +187,12 @@ public DSIRequester getRequester() { return requester; } - @Override - public DSTransport getTransport() { - return transport; - } - - @Override public DSIWriter getWriter() { - return writer; + return getConnection().getWriter(); } private boolean hasPingToSend() { - return (System.currentTimeMillis() - getLastMessageSent()) > MAX_MSG_IVL; + return (System.currentTimeMillis() - lastMessageSent) > MAX_MSG_IVL; } /** @@ -175,15 +219,13 @@ protected void onStable() { @Override public void onConnect() { super.onConnect(); - transport = getConnection().getTransport(); - reader = getConnection().getReader(); - writer = getConnection().getWriter(); requester.onConnect(); responder.onConnect(); } @Override public void onConnectFail() { + super.onConnectFail(); requester.onConnectFail(); responder.onConnectFail(); } @@ -191,9 +233,6 @@ public void onConnectFail() { @Override public void onDisconnect() { super.onDisconnect(); - transport = null; - reader = null; - writer = null; requester.onDisconnect(); responder.onDisconnect(); } @@ -289,6 +328,45 @@ protected void processMessages(DSIReader reader, boolean areRequests) { } } + /** + * Send messages from one of the queues. + * + * @param requests Determines which queue to use; True for outgoing requests, false for + * responses. + * @param endTime Stop after this time. + */ + private void send(boolean requests, long endTime) { + if (requests) { + if (!hasOutgoingRequests()) { + return; + } + beginRequests(); + } else { + if (!hasOutgoingResponses()) { + return; + } + beginResponses(); + } + OutboundMessage msg = requests ? dequeueOutgoingRequest() : dequeueOutgoingResponse(); + while ((msg != null) && (System.currentTimeMillis() < endTime)) { + if (requests) { + writeRequest(msg); + } else { + writeResponse(msg); + } + if (!shouldEndMessage()) { + msg = requests ? dequeueOutgoingRequest() : dequeueOutgoingResponse(); + } else { + msg = null; + } + } + if (requests) { + endRequests(); + } else { + endResponses(); + } + } + /** * We need to send an ack to the broker. */ @@ -297,4 +375,33 @@ private synchronized void sendAck(int msg) { notifyOutgoing(); } + /** + * Returns true if the current message size has crossed a message size threshold. + */ + public boolean shouldEndMessage() { + return (getWriter().length() + getTransport().messageSize()) > END_MSG_THRESHOLD; + } + + /** + * Write a request in the current message. Can be called multiple times after beginRequests() is + * called. endRequests() will be called once the request part of the message is complete. + * + * @see #beginRequests() + * @see #endRequests() + */ + public void writeRequest(OutboundMessage message) { + message.write(getWriter()); + } + + /** + * Write a response in the current message. Can be called multiple times after beginResponses() + * is called. endResponses() will be called once the response part of the message is complete. + * + * @see #beginResponses() + * @see #endResponses() + */ + public void writeResponse(OutboundMessage message) { + message.write(getWriter()); + } + } diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundInvoke.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundInvoke.java index f6ce3ac3..26632642 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundInvoke.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundInvoke.java @@ -2,7 +2,7 @@ import com.acuity.iot.dsa.dslink.protocol.message.ErrorResponse; import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; -import com.acuity.iot.dsa.dslink.protocol.protocol_v1.DS1Stream; +import com.acuity.iot.dsa.dslink.protocol.DSStream; import java.util.Iterator; import org.iot.dsa.DSRuntime; import org.iot.dsa.dslink.responder.InboundInvokeRequest; @@ -22,7 +22,7 @@ * @author Aaron Hansen */ class DS1InboundInvoke extends DS1InboundRequest - implements DS1Stream, InboundInvokeRequest, OutboundMessage, Runnable { + implements DSStream, InboundInvokeRequest, OutboundMessage, Runnable { /////////////////////////////////////////////////////////////////////////// // Constants diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundList.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundList.java index d101e463..27ac8b70 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundList.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundList.java @@ -2,7 +2,7 @@ import com.acuity.iot.dsa.dslink.protocol.message.ErrorResponse; import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; -import com.acuity.iot.dsa.dslink.protocol.protocol_v1.DS1Stream; +import com.acuity.iot.dsa.dslink.protocol.DSStream; import java.util.Iterator; import org.iot.dsa.DSRuntime; import org.iot.dsa.dslink.responder.ApiObject; @@ -26,7 +26,7 @@ * @author Aaron Hansen */ class DS1InboundList extends DS1InboundRequest - implements DS1Stream, InboundListRequest, OutboundMessage, Runnable { + implements DSStream, InboundListRequest, OutboundMessage, Runnable { /////////////////////////////////////////////////////////////////////////// // Constants diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscription.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscription.java index 9a4176d9..61e9d25b 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscription.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscription.java @@ -1,6 +1,5 @@ package com.acuity.iot.dsa.dslink.protocol.protocol_v1.responder; -import com.acuity.iot.dsa.dslink.DSResponderSession; import java.util.logging.Level; import java.util.logging.Logger; import org.iot.dsa.dslink.responder.InboundSubscribeRequest; @@ -163,7 +162,7 @@ public String toString() { */ void write(DSIWriter out, StringBuilder buf) { //Don't check open state - forcefully closing will send an update - DSResponderSession session = getResponder(); + DS1Responder session = getResponder(); Update update = dequeue(); while (update != null) { out.beginMap(); diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1Responder.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1Responder.java index e376a2f9..3afb881b 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1Responder.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1Responder.java @@ -1,12 +1,11 @@ package com.acuity.iot.dsa.dslink.protocol.protocol_v1.responder; import com.acuity.iot.dsa.dslink.DSProtocolException; -import com.acuity.iot.dsa.dslink.DSResponderSession; +import com.acuity.iot.dsa.dslink.protocol.DSStream; import com.acuity.iot.dsa.dslink.protocol.message.CloseMessage; import com.acuity.iot.dsa.dslink.protocol.message.ErrorResponse; import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; import com.acuity.iot.dsa.dslink.protocol.protocol_v1.DS1Session; -import com.acuity.iot.dsa.dslink.protocol.protocol_v1.DS1Stream; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; @@ -23,18 +22,14 @@ * * @author Aaron Hansen */ -public class DS1Responder extends DSNode implements DSResponderSession { - - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// +public class DS1Responder extends DSNode { /////////////////////////////////////////////////////////////////////////// // Fields /////////////////////////////////////////////////////////////////////////// - private ConcurrentHashMap inboundRequests = - new ConcurrentHashMap(); + private ConcurrentHashMap inboundRequests = + new ConcurrentHashMap(); private Logger logger; private DS1Session session; private DSIResponder responder; @@ -53,7 +48,6 @@ public DS1Responder(DS1Session session) { // Methods - In alphabetical order by method name. ///////////////////////////////////////////////////////////////// - @Override public DSLinkConnection getConnection() { return session.getConnection(); } @@ -91,7 +85,7 @@ public void onConnectFail() { public void onDisconnect() { finer(finer() ? "Close" : null); subscriptions.close(); - for (Map.Entry entry : inboundRequests.entrySet()) { + for (Map.Entry entry : inboundRequests.entrySet()) { try { entry.getValue().onClose(entry.getKey()); } catch (Exception x) { @@ -150,7 +144,7 @@ public void processRequest(final Integer rid, final DSMap map) { if (!method.equals("close")) { throwInvalidMethod(method, map); } - final DS1Stream req = inboundRequests.remove(rid); + final DSStream req = inboundRequests.remove(rid); if (req != null) { DSRuntime.run(new Runnable() { public void run() { @@ -287,12 +281,10 @@ void removeInboundRequest(Integer requestId) { inboundRequests.remove(requestId); } - @Override public boolean shouldEndMessage() { return session.shouldEndMessage(); } - @Override public void sendResponse(OutboundMessage res) { session.enqueueOutgoingResponse(res); } diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/DS2LinkConnection.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/DS2LinkConnection.java index 41ef0e67..1a00002c 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/DS2LinkConnection.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/DS2LinkConnection.java @@ -1,5 +1,6 @@ package com.acuity.iot.dsa.dslink.protocol.protocol_v2; +import com.acuity.iot.dsa.dslink.DSSession; import com.acuity.iot.dsa.dslink.transport.DSBinaryTransport; import com.acuity.iot.dsa.dslink.transport.DSTransport; import com.acuity.iot.dsa.dslink.transport.SocketTransport; @@ -12,9 +13,13 @@ import org.iot.dsa.dslink.DSLink; import org.iot.dsa.dslink.DSLinkConfig; import org.iot.dsa.dslink.DSLinkConnection; -import org.iot.dsa.io.DSBase64; +import org.iot.dsa.io.DSIReader; +import org.iot.dsa.io.DSIWriter; +import org.iot.dsa.io.msgpack.MsgpackReader; +import org.iot.dsa.io.msgpack.MsgpackWriter; import org.iot.dsa.node.DSBool; import org.iot.dsa.node.DSBytes; +import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSStatus; import org.iot.dsa.node.DSString; import org.iot.dsa.security.DSKeys; @@ -22,24 +27,43 @@ import org.iot.dsa.util.DSException; /** - * The default V2 connection implementation. Performs connection initialization with the broker, - * then creates a transport and a protocol based on the broker response. + * The DSA V2 connection implementation. Performs connection initialization with the broker, + * then creates a transport and session based on the broker response. * * @author Aaron Hansen */ public class DS2LinkConnection extends DSLinkConnection { + /////////////////////////////////////////////////////////////////////////// + // Constants + /////////////////////////////////////////////////////////////////////////// + + private static final String BROKER_AUTH = "Broker Auth"; + private static final String BROKER_ID = "Broker DSID"; + private static final String BROKER_PATH = "Broker Path"; + private static final String BROKER_PUB_KEY = "Broker Public Key"; + private static final String BROKER_SALT = "Broker Salt"; + private static final String BROKER_URI = "Broker URI"; + private static final String LINK_SALT = "Link Salt"; + private static final String REQUESTER_ALLOWED = "Requester Allowed"; + private static final String LAST_CONNECT_OK = "Last Connect Ok"; + private static final String LAST_CONNECT_FAIL = "Last Connect Fail"; + private static final String FAIL_CAUSE = "Fail Cause"; + private static final String STATUS = "Status"; + /////////////////////////////////////////////////////////////////////////// // Fields /////////////////////////////////////////////////////////////////////////// - private byte[] brokerAuth; - private String brokerDsId; - private byte[] brokerPubKey; - private byte[] brokerSalt; - private DSLink link; - private byte[] linkSalt; - private boolean requesterAllowed = false; + private DSInfo brokerAuth = getInfo(BROKER_AUTH); + private DSInfo brokerDsId = getInfo(BROKER_ID); + private DSInfo brokerPath = getInfo(BROKER_PATH); + private DSInfo brokerPubKey = getInfo(BROKER_PUB_KEY); + private DSInfo brokerSalt = getInfo(BROKER_SALT); + private DSInfo brokerUri = getInfo(BROKER_URI); + private DSInfo linkSalt = getInfo(LINK_SALT); + private DSInfo requesterAllowed = getInfo(REQUESTER_ALLOWED); + private DSSession session; private DSBinaryTransport transport; /////////////////////////////////////////////////////////////////////////// @@ -47,39 +71,63 @@ public class DS2LinkConnection extends DSLinkConnection { /////////////////////////////////////////////////////////////////////////// @Override - public DSLink getLink() { - return link; + public void declareDefaults() { + declareDefault(STATUS, DSStatus.down).setTransient(true).setReadOnly(true); + declareDefault(LAST_CONNECT_OK, DSDateTime.NULL).setTransient(true).setReadOnly(true); + declareDefault(LAST_CONNECT_FAIL, DSDateTime.NULL).setTransient(true).setReadOnly(true); + declareDefault(FAIL_CAUSE, DSString.NULL).setTransient(true).setReadOnly(true); + declareDefault(BROKER_PATH, DSString.NULL).setTransient(true).setReadOnly(true); + declareDefault(BROKER_ID, DSString.NULL).setTransient(true).setReadOnly(true); + declareDefault(BROKER_AUTH, DSBytes.NULL) + .setTransient(true).setReadOnly(true).setAdmin(true); + declareDefault(BROKER_PUB_KEY, DSBytes.NULL) + .setTransient(true).setReadOnly(true).setAdmin(true); + declareDefault(BROKER_SALT, DSBytes.NULL) + .setTransient(true).setReadOnly(true).setAdmin(true); + declareDefault(LINK_SALT, DSBytes.NULL) + .setTransient(true).setReadOnly(true).setAdmin(true); + declareDefault(REQUESTER_ALLOWED, DSBool.FALSE).setTransient(true).setReadOnly(true); + } + + @Override + public void disconnect() { + if (session != null) { + session.disconnect(); + } } private byte[] getLinkSalt() { - if (linkSalt == null) { - linkSalt = new byte[32]; + if (linkSalt.getObject().isNull()) { + byte[] tmp = new byte[32]; SecureRandom random = new SecureRandom(); - random.nextBytes(linkSalt); - put("Link Salt", DSString.valueOf(DSBase64.encodeUrl(linkSalt))) - .setReadOnly(true) - .setTransient(true); + random.nextBytes(tmp); + put(linkSalt, DSBytes.valueOf(tmp)); } - return linkSalt; + return linkSalt.getElement().toBytes(); } @Override public DSIRequester getRequester() { - return null;//session.getRequester(); + return session.getRequester(); + } + + @Override + public DSBinaryTransport getTransport() { + return transport; } /** * Looks at the connection initialization response to determine the type of transport then * instantiates the correct type fom the config. */ - protected DSTransport makeTransport() { + protected void makeTransport() { DSTransport.Factory factory = null; - String uri = link.getConfig().getBrokerUri(); - put("Broker URI", DSString.valueOf(uri)).setReadOnly(true).setTransient(true); + String uri = getLink().getConfig().getBrokerUri(); + put(brokerUri, DSString.valueOf(uri)); transport = null; if (uri.startsWith("ws")) { try { - String type = link.getConfig().getConfig( + String type = getLink().getConfig().getConfig( DSLinkConfig.CFG_WS_TRANSPORT_FACTORY, "org.iot.dsa.dslink.websocket.StandaloneTransportFactory"); factory = (DSTransport.Factory) Class.forName(type).newInstance(); @@ -95,26 +143,25 @@ protected DSTransport makeTransport() { transport.setConnection(this); transport.setReadTimeout(getLink().getConfig().getConfig( DSLinkConfig.CFG_READ_TIMEOUT, 60000)); - setTransport(makeTransport()); - return transport; } @Override protected void onConnect() { - put("Last Connect Attempt", DSDateTime.currentTime()) - .setReadOnly(true).setTransient(true); - makeTransport().open(); + transport.open(); performHandshake(); - put("Status", DSStatus.ok).setReadOnly(true).setTransient(true); } @Override protected void onDisconnect() { - put("Status", DSStatus.down).setReadOnly(true).setTransient(true); + put(STATUS, DSStatus.down); } @Override protected void onInitialize() { + if (session == null) { + session = null;//TODO + } + makeTransport(); } /** @@ -123,13 +170,7 @@ protected void onInitialize() { */ @Override protected void onRun() { - //session.run(); - } - - @Override - protected void onStable() { - this.link = (DSLink) getParent(); - super.onStable(); + session.run(); } private void performHandshake() { @@ -138,13 +179,12 @@ private void performHandshake() { recvF1(); sendF2(); recvF3(); - put("Last Connect", DSDateTime.currentTime()).setReadOnly(true).setTransient(true); - put("Status", DSStatus.ok).setReadOnly(true).setTransient(true); + put(LAST_CONNECT_OK, DSDateTime.currentTime()); + put(STATUS, DSStatus.ok); } catch (Exception io) { - put("Status", DSStatus.fault).setReadOnly(true).setTransient(true); - put("Last Fail", DSDateTime.currentTime()).setReadOnly(true).setTransient(true); - put("Fail Cause", DSString.valueOf(DSException.makeMessage(io))) - .setReadOnly(true).setTransient(true); + put(STATUS, DSStatus.fault); + put(LAST_CONNECT_FAIL, DSDateTime.currentTime()); + put(FAIL_CAUSE, DSString.valueOf(DSException.makeMessage(io))); DSException.throwRuntime(io); } } @@ -158,16 +198,13 @@ private void recvF1() throws IOException { Integer.toHexString(reader.getMethod())); } //TODO check for header status - brokerDsId = reader.readString(in); - put("Broker DSID", DSString.valueOf(brokerDsId)).setReadOnly(true).setTransient(true); - brokerPubKey = new byte[65]; - in.read(brokerPubKey); - put("Broker Public Key", DSString.valueOf(DSBase64.encodeUrl(brokerPubKey))) - .setReadOnly(true).setTransient(true); - brokerSalt = new byte[32]; - in.read(brokerSalt); - put("Broker Salt", DSString.valueOf(DSBase64.encodeUrl(brokerSalt))) - .setReadOnly(true).setTransient(true); + put(brokerDsId, DSString.valueOf(reader.readString(in))); + byte[] tmp = new byte[65]; + in.read(tmp); + put(BROKER_PUB_KEY, DSBytes.valueOf(tmp)); + tmp = new byte[32]; + in.read(tmp); + put(BROKER_SALT, DSBytes.valueOf(tmp)); } private void recvF3() throws IOException { @@ -179,21 +216,14 @@ private void recvF3() throws IOException { Integer.toHexString(reader.getMethod())); } //TODO check for header status - requesterAllowed = (in.read() == 1); - put("Requester Allowed", DSBool.valueOf(requesterAllowed)) - .setReadOnly(true).setTransient(true); - String sessionId = reader.readString(in); - put("Session ID", DSString.valueOf(sessionId)).setTransient(true); - int lastAck = DSBytes.readInt(in, false); + put(requesterAllowed, DSBool.valueOf(in.read() == 1)); + String sessionId = reader.readString(in); //TODO remove + int lastAck = DSBytes.readInt(in, false); //TODO remove String pathOnBroker = reader.readString(in); - put("Broker Path", DSString.valueOf(pathOnBroker)) - .setReadOnly(true).setTransient(true); - if (brokerAuth == null) { - brokerAuth = new byte[32]; - } - in.read(brokerAuth); - put("Broker Auth", DSString.valueOf(DSBase64.encodeUrl(brokerAuth))) - .setReadOnly(true).setTransient(true); + put(brokerPath, DSString.valueOf(pathOnBroker)); + byte[] tmp = new byte[32]; + in.read(tmp); + put(brokerAuth, DSBytes.valueOf(tmp)); } private void sendF0() { @@ -214,7 +244,7 @@ private void sendF2() throws Exception { MessageWriter writer = new MessageWriter(); writer.setMethod((byte) 0xf2); ByteBuffer buffer = writer.getBody(); - String token = link.getConfig().getToken(); + String token = getLink().getConfig().getToken(); if (token == null) { token = ""; } @@ -223,14 +253,17 @@ private void sendF2() throws Exception { writer.writeString("", buffer); //TODO remove writer.writeIntLE(0, buffer); //TODO remove writer.writeString("", buffer); //blank server path - byte[] sharedSecret = getLink().getKeys().generateSharedSecret(brokerPubKey); - byte[] authBytes = new byte[brokerSalt.length + sharedSecret.length]; - System.arraycopy(brokerSalt, 0, authBytes, 0, brokerSalt.length); - System.arraycopy(sharedSecret, 0, authBytes, brokerSalt.length, sharedSecret.length); + byte[] sharedSecret = getLink().getKeys().generateSharedSecret( + brokerPubKey.getElement().toBytes()); + byte[] tmp = brokerSalt.getElement().toBytes(); + byte[] authBytes = new byte[tmp.length + sharedSecret.length]; + System.arraycopy(tmp, 0, authBytes, 0, tmp.length); + System.arraycopy(sharedSecret, 0, authBytes, tmp.length, sharedSecret.length); MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(authBytes); authBytes = messageDigest.digest(); buffer.put(authBytes); + writer.write(transport); } } diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/DS2Session.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/DS2Session.java new file mode 100644 index 00000000..af094f9e --- /dev/null +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/DS2Session.java @@ -0,0 +1,166 @@ +package com.acuity.iot.dsa.dslink.protocol.protocol_v2; + +import com.acuity.iot.dsa.dslink.DSSession; +import com.acuity.iot.dsa.dslink.protocol.protocol_v1.requester.DS1Requester; +import com.acuity.iot.dsa.dslink.protocol.protocol_v2.responder.DS2Responder; +import com.acuity.iot.dsa.dslink.transport.DSBinaryTransport; +import java.io.IOException; +import org.iot.dsa.dslink.DSIRequester; +import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSInt; + +/** + * Implements DSA 1.1.2 + * + * @author Aaron Hansen + */ +public class DS2Session extends DSSession implements MessageConstants { + + /////////////////////////////////////////////////////////////////////////// + // Constants + /////////////////////////////////////////////////////////////////////////// + + static final int END_MSG_THRESHOLD = 48000; + static final String LAST_ACK_RECV = "Last Ack Recv"; + static final String LAST_ACK_SENT = "Last Ack Sent"; + + static final int MAX_MSG_ID = 2147483647; + static final int MAX_MSG_IVL = 45000; + + /////////////////////////////////////////////////////////////////////////// + // Fields + /////////////////////////////////////////////////////////////////////////// + + private DSInfo lastAckRecv = getInfo(LAST_ACK_RECV); + private DSInfo lastAckSent = getInfo(LAST_ACK_SENT); + private long lastMessageSent; + private MessageReader messageReader; + private int nextAck = -1; + private int nextMsg = 1; + private boolean requestsNext = false; + private DS1Requester requester;// = new DS1Requester(this); + private DS2Responder responder = new DS2Responder(this); + + ///////////////////////////////////////////////////////////////// + // Methods + ///////////////////////////////////////////////////////////////// + + @Override + protected void declareDefaults() { + declareDefault(LAST_ACK_RECV, DSInt.NULL).setReadOnly(true); + declareDefault(LAST_ACK_SENT, DSInt.NULL).setReadOnly(true); + } + + @Override + protected void doRecvMessage() throws IOException { + if (messageReader == null) { + messageReader = new MessageReader(); + } + messageReader.init(getTransport().getInput()); + if (messageReader.isRequest()) { + responder.processRequest(messageReader); + } else if (messageReader.isResponse()) { + ;//requester.processResponse(messageReader); + } + int ack = messageReader.getAckId(); + if (ack > 0) { + put(lastAckRecv, DSInt.valueOf(ack)); + } + } + + @Override + protected void doSendMessage() { + /* + DSTransport transport = getTransport(); + long endTime = System.currentTimeMillis() + 2000; + requestsNext = !requestsNext; + transport.beginMessage(); + if (hasMessagesToSend()) { + send(requestsNext, endTime); + if ((System.currentTimeMillis() < endTime) && !shouldEndMessage()) { + send(!requestsNext, endTime); + } + } + transport.endMessage(); + lastMessageSent = System.currentTimeMillis(); + */ + } + + @Override + public DS2LinkConnection getConnection() { + return (DS2LinkConnection) super.getConnection(); + } + + @Override + public DSIRequester getRequester() { + return requester; + } + + public DSBinaryTransport getTransport() { + return (DSBinaryTransport) super.getTransport(); + } + + private boolean hasPingToSend() { + return (System.currentTimeMillis() - lastMessageSent) > MAX_MSG_IVL; + } + + /** + * Override point, returns true if there are any pending acks or outbound messages queued up. + */ + protected boolean hasSomethingToSend() { + if (nextAck > 0) { + return true; + } + if (hasPingToSend()) { + return true; + } + return super.hasSomethingToSend(); + } + + /* + @Override + protected void onStable() { + put("Requester Session", requesterSession); + put("Responder Session", responderSession); + } + */ + + @Override + public void onConnect() { + super.onConnect(); + requester.onConnect(); + responder.onConnect(); + } + + @Override + public void onConnectFail() { + super.onConnectFail(); + requester.onConnectFail(); + responder.onConnectFail(); + } + + @Override + public void onDisconnect() { + super.onDisconnect(); + requester.onDisconnect(); + //responder.onDisconnect(); + } + + /** + * We need to send an ack to the broker. + */ + private synchronized void sendAck(int msg) { + nextAck = msg; + notifyOutgoing(); + } + + /** + * Returns true if the current message size has crossed a message size threshold. + */ + public boolean shouldEndMessage() { + //TODO + //return (messageWriter.length() + getTransport().messageSize()) > END_MSG_THRESHOLD; + return getTransport().messageSize() > END_MSG_THRESHOLD; + } + +} diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/MessageConstants.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/MessageConstants.java index a7411c4a..e418193b 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/MessageConstants.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/MessageConstants.java @@ -30,19 +30,19 @@ public interface MessageConstants { Byte HDR_TARGET_PATH = (byte) (0x80 & 0xFF); Byte HDR_SOURCE_PATH = (byte) (0x81 & 0xFF); - Byte MSG_SUBSCRIBE_REQ = 0x01; - Byte MSG_SUBSCRIBE_RES = (byte) (0x81 & 0xFF); - Byte MSG_LIST_REQ = 0x02; - Byte MSG_LIST_RES = (byte) (0x82 & 0xFF); - Byte MSG_INVOKE_REQ = 0x03; - Byte MSG_INVOKE_RES = (byte) (0x83 & 0xFF); - Byte MSG_SET_REQ = 0x04; - Byte MSG_SET_RES = (byte) (0x84 & 0xFF); - Byte MSG_OBSERVE_REQ = 0x0A; - Byte MSG_OBSERVE_RES = (byte) (0x8A & 0xFF); - Byte MSG_CLOSE = 0x0F; - Byte MSG_ACK = (byte) (0xF8 & 0xFF); - Byte MSG_PING = (byte) (0xF9 & 0xFF); + int MSG_SUBSCRIBE_REQ = 0x01; + int MSG_SUBSCRIBE_RES = 0x81; + int MSG_LIST_REQ = 0x02; + int MSG_LIST_RES = 0x82; + int MSG_INVOKE_REQ = 0x03; + int MSG_INVOKE_RES = 0x83; + int MSG_SET_REQ = 0x04; + int MSG_SET_RES = 0x84; + int MSG_OBSERVE_REQ = 0x0A; + int MSG_OBSERVE_RES = 0x8A; + int MSG_CLOSE = 0x0F; + int MSG_ACK = 0xF8; + int MSG_PING = 0xF9; Byte STS_OK = 0; Byte STS_INITIALIZING = 0x01; diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/MessageReader.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/MessageReader.java index 44514733..ad960b9a 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/MessageReader.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/MessageReader.java @@ -37,6 +37,9 @@ public class MessageReader implements MessageConstants { // Constructors // ------------ + public MessageReader() { + } + // Methods // ------- @@ -104,7 +107,7 @@ private ByteBuffer getStringBuffer(int len) { return strBuffer; } - public void init(InputStream in) { + public MessageReader init(InputStream in) { try { input = in; headers.clear(); @@ -127,6 +130,22 @@ public void init(InputStream in) { } catch (IOException x) { DSException.throwRuntime(x); } + return this; + } + + public boolean isRequest() { + switch (method) { + case MSG_CLOSE : + case MSG_INVOKE_REQ : + case MSG_LIST_REQ : + case MSG_OBSERVE_REQ : + return true; + } + return false; + } + + public boolean isResponse() { + return (method & 0x80) != 0; } private void parseDynamicHeaders(InputStream in, int len) throws IOException { diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/responder/DS2Responder.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/responder/DS2Responder.java new file mode 100644 index 00000000..dcd2296a --- /dev/null +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v2/responder/DS2Responder.java @@ -0,0 +1,216 @@ +package com.acuity.iot.dsa.dslink.protocol.protocol_v2.responder; + +import com.acuity.iot.dsa.dslink.protocol.DSStream; +import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; +import com.acuity.iot.dsa.dslink.protocol.protocol_v2.DS2Session; +import com.acuity.iot.dsa.dslink.protocol.protocol_v2.MessageConstants; +import com.acuity.iot.dsa.dslink.protocol.protocol_v2.MessageReader; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; +import org.iot.dsa.dslink.DSLinkConnection; +import org.iot.dsa.node.DSNode; + +/** + * Implements DSA 1.1.2 + * + * @author Aaron Hansen + */ +public class DS2Responder extends DSNode implements MessageConstants { + + /////////////////////////////////////////////////////////////////////////// + // Constants + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + // Fields + /////////////////////////////////////////////////////////////////////////// + + private ConcurrentHashMap inboundRequests = + new ConcurrentHashMap(); + private Logger logger; + private DS2Session session; + private DS2Responder responder; + //private DS2InboundSubscriptions subscriptions = + //new DS2InboundSubscriptions(this); + + ///////////////////////////////////////////////////////////////// + // Methods - Constructors + ///////////////////////////////////////////////////////////////// + + public DS2Responder(DS2Session session) { + this.session = session; + } + + ///////////////////////////////////////////////////////////////// + // Methods - In alphabetical order by method name. + ///////////////////////////////////////////////////////////////// + + public DSLinkConnection getConnection() { + return session.getConnection(); + } + + @Override + public Logger getLogger() { + if (logger == null) { + logger = Logger.getLogger( + getConnection().getLink().getLinkName() + ".responderSession"); + } + return logger; + } + + /* + public DS2InboundSubscriptions getSubscriptions() { + return subscriptions; + } + */ + + public void onConnect() { + } + + public void onConnectFail() { + } + + /* + public void onDisconnect() { + finer(finer() ? "Close" : null); + subscriptions.close(); + for (Map.Entry entry : inboundRequests.entrySet()) { + try { + entry.getValue().onClose(entry.getKey()); + } catch (Exception x) { + finer(finer() ? "Close" : null, x); + } + } + inboundRequests.clear(); + } + */ + + /** + * Handles an invoke request. + private void processInvoke(Integer rid, DSMap req) { + DS2InboundInvoke invokeImpl = new DS2InboundInvoke(req); + invokeImpl.setPath(getPath(req)) + .setSession(session) + .setRequestId(rid) + .setResponderImpl(responder) + .setResponder(this); + inboundRequests.put(rid, invokeImpl); + DSRuntime.run(invokeImpl); + } + */ + + /** + * Handles a list request. + private void processList(Integer rid, DSMap req) { + DS2InboundList listImpl = new DS2InboundList(); + listImpl.setPath(getPath(req)) + .setSession(session) + .setRequest(req) + .setRequestId(rid) + .setResponderImpl(responder) + .setResponder(this); + inboundRequests.put(listImpl.getRequestId(), listImpl); + DSRuntime.run(listImpl); + } + */ + + /** + * Process an individual request. + */ + public void processRequest(MessageReader reader) { + switch (reader.getMethod()) { + case MSG_INVOKE_REQ : + break; + case MSG_LIST_REQ : + //processList() + break; + case MSG_OBSERVE_REQ : + break; + case MSG_SET_REQ : + break; + case MSG_SUBSCRIBE_REQ : + break; + default : + throw new IllegalArgumentException("Unexpected method: " + reader.getMethod()); + } + } + + /** + * Handles a set request. + private void processSet(Integer rid, DSMap req) { + DS2InboundSet setImpl = new DS2InboundSet(req); + setImpl.setPath(getPath(req)) + .setSession(session) + .setRequestId(rid) + .setResponderImpl(responder) + .setResponder(this); + DSRuntime.run(setImpl); + } + */ + + /** + * Handles a subscribe request. + private void processSubscribe(int rid, DSMap req) { + DSList list = req.getList("paths"); + if (list == null) { + return; + } + String path; + Integer sid; + Integer qos; + DSMap subscribe; + for (int i = 0, len = list.size(); i < len; i++) { + subscribe = list.getMap(i); + path = subscribe.getString("path"); + sid = subscribe.getInt("sid"); + qos = subscribe.get("qos", 0); + try { + subscriptions.subscribe(responder, sid, path, qos); + } catch (Exception x) { + //invalid paths are very common + fine(path, x); + } + } + } + */ + + /** + * Handles an unsubscribe request. + * private void processUnsubscribe(int rid, DSMap req) { + * DSList list = req.getList("sids"); + * Integer sid = null; + * if (list != null) { + * for (int i = 0, len = list.size(); i < len; i++) { + * try { + * sid = list.getInt(i); + * subscriptions.unsubscribe(sid); + * } catch (Exception x) { + * fine(fine() ? "Unsubscribe: " + sid : null, x); + * } + * } + * } + * } + */ + + void removeInboundRequest(Integer requestId) { + inboundRequests.remove(requestId); + } + + public boolean shouldEndMessage() { + return session.shouldEndMessage(); + } + + public void sendResponse(OutboundMessage res) { + session.enqueueOutgoingResponse(res); + } + + /** + * Used throughout processRequest. + private void throwInvalidMethod(String methodName, DSMap request) { + String msg = "Invalid method name " + methodName; + finest(finest() ? (msg + ": " + request.toString()) : null); + throw new DSProtocolException(msg).setType("invalidMethod"); + } + */ + +} diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkConnection.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkConnection.java index 7ae9e725..b882217e 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkConnection.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkConnection.java @@ -1,17 +1,9 @@ package org.iot.dsa.dslink; -import com.acuity.iot.dsa.dslink.transport.DSBinaryTransport; -import com.acuity.iot.dsa.dslink.transport.DSTextTransport; import com.acuity.iot.dsa.dslink.transport.DSTransport; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.iot.dsa.DSRuntime; -import org.iot.dsa.io.DSIReader; -import org.iot.dsa.io.DSIWriter; -import org.iot.dsa.io.json.JsonReader; -import org.iot.dsa.io.json.JsonWriter; -import org.iot.dsa.io.msgpack.MsgpackReader; -import org.iot.dsa.io.msgpack.MsgpackWriter; import org.iot.dsa.node.DSNode; import org.iot.dsa.time.DSTime; @@ -20,9 +12,6 @@ *

* Implementations must have a no-arg public constructor. It will be dynamically added as * a child of the DSLink. - *

- * The default connection type can be overridden by specifying the config connectionType - * as a Java class name dslink.json. * * @author Aaron Hansen */ @@ -35,9 +24,6 @@ public abstract class DSLinkConnection extends DSNode { private String connectionId; private ConcurrentHashMap listeners; private Logger logger; - private DSIReader reader; - private DSTransport transport; - private DSIWriter writer; // Methods // ------- @@ -63,13 +49,10 @@ public void addListener(Listener listener) { } /** - * Closes an open connection. + * Forcefully closes an open connection. Does not prevent reconnection, intended for + * dealing with problems. */ - public void disconnect() { - if (transport != null) { - transport.close(); - } - } + public abstract void disconnect(); /** * A unique descriptive tag such as a combination of the link name and the broker host. @@ -102,7 +85,9 @@ public String getConnectionId() { /** * The link using this connection. */ - public abstract DSLink getLink(); + public DSLink getLink() { + return (DSLink) getParent(); + } @Override public Logger getLogger() { @@ -112,19 +97,9 @@ public Logger getLogger() { return logger; } - public DSIReader getReader() { - return reader; - } - public abstract DSIRequester getRequester(); - public DSTransport getTransport() { - return transport; - } - - public DSIWriter getWriter() { - return writer; - } + public abstract DSTransport getTransport(); /** * True when a connection is established with the remote endpoint. @@ -147,7 +122,8 @@ public boolean isConnected() { protected abstract void onDisconnect(); /** - * Always called before onConnect. + * Always called before onConnect. If an exception is thrown onConnect and onDisconnect + * will not be called. */ protected abstract void onInitialize(); @@ -174,40 +150,6 @@ public void removeListener(Listener listener) { } } - /** - * Sets the transport and creates the appropriate reader/writer. - */ - protected void setTransport(DSTransport transport) { - if (transport instanceof DSBinaryTransport) { - final DSBinaryTransport trans = (DSBinaryTransport) transport; - reader = new MsgpackReader(trans.getInput()); - writer = new MsgpackWriter() { - @Override - public void onComplete() { - /* How to debug - try { - MsgpackReader reader = new MsgpackReader( - new ByteArrayInputStream(byteBuffer.array()); - System.out.println(reader.getMap()); - reader.close(); - } catch (Exception x) { - x.printStackTrace(); - } - */ - trans.write(byteBuffer, true); - } - }; - } else if (transport instanceof DSTextTransport) { - DSTextTransport trans = (DSTextTransport) transport; - reader = new JsonReader(trans.getReader()); - writer = new JsonWriter(trans.getWriter()); - } else { - throw new IllegalStateException( - "Unexpected transport type: " + transport.getClass().getName()); - } - this.transport = transport; - } - // Inner Classes // ------------- @@ -270,9 +212,6 @@ public void run() { } catch (Exception x) { severe(getPath(), x); } - transport = null; - reader = null; - writer = null; if (listeners != null) { for (Listener listener : listeners.keySet()) { try { diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkSession.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkSession.java deleted file mode 100644 index 0ee0d6ef..00000000 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLinkSession.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.iot.dsa.dslink; - -/** - * Represents a line of communication with a remote entity. - * - * @author Aaron Hansen - */ -public interface DSLinkSession { - - /** - * The associate connection. - */ - public DSLinkConnection getConnection(); - - /** - * Large outbound messages that can be broken into smaller ones should check this occasionally - * when writing. The idea is to prevent buffer overflows as well as not have a single message - * hog communication. - */ - public boolean shouldEndMessage(); - -} diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSPermissionException.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSPermissionException.java index 1f89e710..8e1d53cd 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSPermissionException.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/DSPermissionException.java @@ -7,44 +7,8 @@ */ public class DSPermissionException extends DSRequestException { - ///////////////////////////////////////////////////////////////// - // Constructors - ///////////////////////////////////////////////////////////////// - public DSPermissionException(String message) { super(message); } - ///////////////////////////////////////////////////////////////// - // Methods - Public and in alphabetical order by method name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Methods - Protected and in alphabetical order by method name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Methods - Package and in alphabetical order by method name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Methods - Private and in alphabetical order by method name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Inner Classes - in alphabetical order by class name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Constants - in alphabetical order by field name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Facets - in alphabetical order by field name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Initialization - ///////////////////////////////////////////////////////////////// - } diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSRequestException.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSRequestException.java index e42fd23b..ec0901d8 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSRequestException.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/DSRequestException.java @@ -59,32 +59,4 @@ public void setDetail(Throwable arg) { detail = sw.toString(); } - ///////////////////////////////////////////////////////////////// - // Methods - Protected and in alphabetical order by method name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Methods - Package and in alphabetical order by method name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Methods - Private and in alphabetical order by method name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Inner Classes - in alphabetical order by class name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Constants - in alphabetical order by field name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Facets - in alphabetical order by field name. - ///////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////// - // Initialization - ///////////////////////////////////////////////////////////////// - } diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java index 472d10de..ef607abc 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java @@ -74,11 +74,6 @@ public Integer getRequestId() { return request.getRequestId(); } - @Override - public DSLinkSession getResponder() { - return request.getResponder(); - } - @Override public void insert(int index, DSList... rows) { request.insert(index, rows); diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java index 79b6ba0f..2299e7d7 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java @@ -35,7 +35,7 @@ class ListSubscriber implements DSISubscriber, OutboundListResponse { this.info = path.getInfo(); if (info.isNode()) { this.node = info.getNode(); - node.subscribe(null, this, DSNode.INFO_TOPIC); + node.subscribe(DSNode.INFO_TOPIC, null, this); } } @@ -51,15 +51,13 @@ public ApiObject getTarget() { @Override public void onClose() { if (node != null) { - node.unsubscribe(null, this, DSNode.INFO_TOPIC); + node.unsubscribe(DSNode.INFO_TOPIC, null, this); } } @Override - public void onEvent(DSNode node, + public void onEvent(DSTopic topic, DSIEvent event, DSNode node, DSInfo child, - DSTopic topic, - DSIEvent event, Object... params) { switch ((DSInfoTopic.Event) event) { case CHILD_ADDED: @@ -75,7 +73,7 @@ public void onEvent(DSNode node, } @Override - public void onUnsubscribed(DSNode node, DSInfo child, DSTopic topic) { + public void onUnsubscribed(DSTopic topic, DSNode node, DSInfo child) { request.close(); } diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java index e5623e78..c90c0089 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java @@ -64,11 +64,6 @@ public Integer getRequestId() { return request.getRequestId(); } - @Override - public DSLinkSession getResponder() { - return request.getResponder(); - } - @Override public boolean isOpen() { return request.isOpen(); diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java index 547b4812..bd6a9658 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java @@ -50,11 +50,6 @@ public Integer getRequestId() { return request.getRequestId(); } - @Override - public DSLinkSession getResponder() { - return request.getResponder(); - } - @Override public DSElement getValue() { return request.getValue(); diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java index 596eac6b..192c448a 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java @@ -50,11 +50,6 @@ public Integer getRequestId() { return request.getRequestId(); } - @Override - public DSLinkSession getResponder() { - return request.getResponder(); - } - @Override public Integer getSubscriptionId() { return request.getSubscriptionId(); diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java index 5de320ed..48e6f82e 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java @@ -38,13 +38,13 @@ class ValueSubscriber implements DSISubscriber, SubscriptionCloseHandler { if (info.isNode()) { this.node = info.getNode(); this.info = null; - onEvent(node, info, DSNode.VALUE_TOPIC, Event.NODE_CHANGED, null, node); + onEvent(DSNode.VALUE_TOPIC, Event.NODE_CHANGED, node, info, null, node); } else { this.node = path.getParent(); - onEvent(node, info, DSNode.VALUE_TOPIC, Event.CHILD_CHANGED, info.getValue(), + onEvent(DSNode.VALUE_TOPIC, Event.CHILD_CHANGED, node, info, info.getValue(), info.getValue()); } - node.subscribe(info, this, DSNode.VALUE_TOPIC); + node.subscribe(DSNode.VALUE_TOPIC, info, this); } /////////////////////////////////////////////////////////////////////////// @@ -53,14 +53,12 @@ class ValueSubscriber implements DSISubscriber, SubscriptionCloseHandler { @Override public void onClose(Integer subscriptionId) { - node.unsubscribe(info, this, DSNode.VALUE_TOPIC); + node.unsubscribe(DSNode.VALUE_TOPIC, info, this); } @Override - public void onEvent(DSNode node, + public void onEvent(DSTopic topic, DSIEvent event, DSNode node, DSInfo child, - DSTopic topic, - DSIEvent event, Object... params) { DSIValue value; if (info == null) { @@ -84,7 +82,7 @@ public void onEvent(DSNode node, } @Override - public void onUnsubscribed(DSNode node, DSInfo info, DSTopic topic) { + public void onUnsubscribed(DSTopic topic, DSNode node, DSInfo info) { request.close(); } diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/responder/InboundRequest.java b/dslink-core/src/main/java/org/iot/dsa/dslink/responder/InboundRequest.java index 9924d3a2..5fb91581 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/responder/InboundRequest.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/responder/InboundRequest.java @@ -1,7 +1,5 @@ package org.iot.dsa.dslink.responder; -import org.iot.dsa.dslink.DSLinkSession; - /** * Common to all incoming requests. * @@ -19,10 +17,4 @@ public interface InboundRequest { */ public Integer getRequestId(); - /** - * The corresponding session. - */ - public DSLinkSession getResponder(); - - } diff --git a/dslink-core/src/main/java/org/iot/dsa/logging/AsyncLogHandler.java b/dslink-core/src/main/java/org/iot/dsa/logging/AsyncLogHandler.java index e459f8e8..090f122b 100644 --- a/dslink-core/src/main/java/org/iot/dsa/logging/AsyncLogHandler.java +++ b/dslink-core/src/main/java/org/iot/dsa/logging/AsyncLogHandler.java @@ -271,12 +271,17 @@ public void run() { record = null; synchronized (queue) { emptyQueue = queue.isEmpty(); - if (emptyQueue && (state == STATE_OPEN)) { + if (emptyQueue) { try { queue.notifyAll(); - queue.wait(DSTime.MILLIS_SECOND); } catch (Exception ignore) { } + if (state == STATE_OPEN) { + try { + queue.wait(DSTime.MILLIS_SECOND); + } catch (Exception ignore) { + } + } emptyQueue = queue.isEmpty(); //housekeeping opportunity flag } else if (!emptyQueue) { record = queue.removeFirst(); diff --git a/dslink-core/src/main/java/org/iot/dsa/node/DSInfo.java b/dslink-core/src/main/java/org/iot/dsa/node/DSInfo.java index 38db92da..e0a74c13 100644 --- a/dslink-core/src/main/java/org/iot/dsa/node/DSInfo.java +++ b/dslink-core/src/main/java/org/iot/dsa/node/DSInfo.java @@ -45,14 +45,11 @@ public class DSInfo implements ApiObject { /////////////////////////////////////////////////////////////////////////// static final int ADMIN = 0; + static final int DEFAULT_ON_COPY = 5; static final int HIDDEN = 1; - static final int TRANSIENT = 2; - static final int READONLY = 3; static final int PERMANENT = 4; - //static final int PERMANENTLY_SUBSCRIBED = 5; - //static final int NO_ADD_REMOVE_CHILDREN = 5; - //static final int NO_MODIFY_FLAGS = 5; - //static final int NO_AUDIT = 5; + static final int READONLY = 3; + static final int TRANSIENT = 2; /////////////////////////////////////////////////////////////////////////// // Fields @@ -85,6 +82,13 @@ public DSInfo copy() { DSInfo ret = new DSInfo(); ret.flags = flags; ret.name = name; + if (isDefaultOnCopy()) { + DSIObject val = getDefaultObject(); + if (val != null) { + ret.setObject(val.copy()); + return ret; + } + } if (value != null) { ret.setObject(value.copy()); } @@ -145,8 +149,8 @@ private boolean equivalent(DSInfo arg) { } private void fireInfoChanged() { - if (parent != null) { - parent.fire(this, DSInfoTopic.INSTANCE, DSInfoTopic.Event.METADATA_CHANGED); + if ((parent != null) && (parent.isRunning())) { + parent.fire(DSInfoTopic.INSTANCE, DSInfoTopic.Event.METADATA_CHANGED, this); } } @@ -247,6 +251,13 @@ public boolean isAdmin() { return getFlag(ADMIN); } + /** + * Whether or not the current value, or the default value is copied. + */ + public boolean isDefaultOnCopy() { + return getFlag(DEFAULT_ON_COPY); + } + /** * Whether or not this info represents a declared default. */ diff --git a/dslink-core/src/main/java/org/iot/dsa/node/DSInfoProxy.java b/dslink-core/src/main/java/org/iot/dsa/node/DSInfoProxy.java index fff4489d..5f10e0f6 100644 --- a/dslink-core/src/main/java/org/iot/dsa/node/DSInfoProxy.java +++ b/dslink-core/src/main/java/org/iot/dsa/node/DSInfoProxy.java @@ -38,7 +38,12 @@ public DSInfo copy() { ret.flags = flags; ret.name = name; ret.defaultInfo = defaultInfo; - if (value != null) { + if (isDefaultOnCopy()) { + DSIObject val = getDefaultObject(); + if (val != null) { + ret.setObject(val.copy()); + } + } else if (value != null) { ret.setObject(value.copy()); } return ret; diff --git a/dslink-core/src/main/java/org/iot/dsa/node/DSNode.java b/dslink-core/src/main/java/org/iot/dsa/node/DSNode.java index 73f498e1..15a2f6c0 100644 --- a/dslink-core/src/main/java/org/iot/dsa/node/DSNode.java +++ b/dslink-core/src/main/java/org/iot/dsa/node/DSNode.java @@ -229,7 +229,7 @@ void add(final DSInfo info) { } catch (Exception x) { severe(getPath(), x); } - fire(info, INFO_TOPIC, DSInfoTopic.Event.CHILD_ADDED); + fire(INFO_TOPIC, DSInfoTopic.Event.CHILD_ADDED, info); } } @@ -388,24 +388,22 @@ void dsInit() { /** * Notifies subscribers of the event. - * - * @param child Can be null. - * @param topic Must not be null. + * @param topic Must not be null. * @param event Must not be null. + * @param child Can be null. */ - protected void fire(DSInfo child, DSTopic topic, DSIEvent event) { - fire(child, topic, event, (Object[]) null); + protected void fire(DSTopic topic, DSIEvent event, DSInfo child) { + fire(topic, event, child, (Object[]) null); } /** * Notifies subscribers of the event. - * - * @param child Can be null. - * @param topic Must not be null. + * @param topic Must not be null. * @param event Must not be null. + * @param child Can be null. * @param params Optional */ - protected void fire(DSInfo child, DSTopic topic, DSIEvent event, Object... params) { + protected void fire(DSTopic topic, DSIEvent event, DSInfo child, Object... params) { if (topic == null) { throw new NullPointerException("Null topic"); } @@ -414,9 +412,9 @@ protected void fire(DSInfo child, DSTopic topic, DSIEvent event, Object... param } Subscription sub = subscription; while (sub != null) { - if (sub.matches(child, topic)) { + if (sub.shouldFire(child, topic)) { try { - sub.subscriber.onEvent(this, child, topic, event, params); + sub.subscriber.onEvent(topic, event, this, child, params); } catch (Exception x) { severe(getPath(), x); } @@ -830,6 +828,7 @@ public void onSet(DSInfo info, DSIValue value) { * * @param value The new value. * @see DSIResponder#onSet(InboundSetRequest) + * @see DSValueNode */ public void onSet(DSIValue value) { throw new IllegalStateException("DSNode.onSet(DSIValue) not overridden"); @@ -838,11 +837,11 @@ public void onSet(DSIValue value) { /** * Called for every subscription. * + * @param topic Will not be null. * @param child Can be null. * @param subscriber Will not be null. - * @param topic Will not be null. */ - protected void onSubscribe(DSInfo child, DSISubscriber subscriber, DSTopic topic) { + protected void onSubscribe(DSTopic topic, DSInfo child, DSISubscriber subscriber) { } /** @@ -858,7 +857,7 @@ protected void onSubscribed() { * * @param child Can be null, which indicates node subscriptions. */ - protected void onSubscribed(DSInfo child, DSTopic topic) { + protected void onSubscribed(DSTopic topic, DSInfo child) { } /** @@ -882,11 +881,11 @@ protected void onStopped() { /** * Called for every unsubscribe. * + * @param topic Will not be null. * @param child Can be null. * @param subscriber Will not be null. - * @param topic Will not be null. */ - protected void onUnsubscribe(DSInfo child, DSISubscriber subscriber, DSTopic topic) { + protected void onUnsubscribe(DSTopic topic, DSInfo child, DSISubscriber subscriber) { } /** @@ -898,10 +897,10 @@ protected void onUnsubscribed() { /** * Called when the child and topic pair transitions to having no subscriptions. * - * @param child Can be null. * @param topic Can not be null. + * @param child Can be null. */ - protected void onUnsubscribed(DSInfo child, DSTopic topic) { + protected void onUnsubscribed(DSTopic topic, DSInfo child) { } /** @@ -1007,7 +1006,12 @@ public DSNode put(DSInfo info, DSIObject object) { if (argIsNode) { argAsNode.start(); } else { - fire(info, VALUE_TOPIC, DSValueTopic.Event.CHILD_CHANGED); + try { + onChildChanged(info); + } catch (Exception x) { + severe(getPath(), x); + } + fire(VALUE_TOPIC, DSValueTopic.Event.CHILD_CHANGED, info); } } return this; @@ -1059,11 +1063,11 @@ public DSNode remove(DSInfo info) { } catch (Exception x) { severe(getPath(), x); } - fire(info, INFO_TOPIC, DSInfoTopic.Event.CHILD_REMOVED); + fire(INFO_TOPIC, DSInfoTopic.Event.CHILD_REMOVED, info); Subscription sub = subscription; while (sub != null) { - if (sub.matches(info)) { - unsubscribe(info, sub.subscriber, sub.topic); + if (sub.shouldUnsubscribe(info)) { + unsubscribe(sub.topic, info, sub.subscriber); } sub = sub.next; } @@ -1196,11 +1200,11 @@ public final void stop() { /** * Subscribes the child and topic. * - * @param child Can be null, but cannot be a child node. - * @param subscriber Can not be null. * @param topic Can not be null. + * @param child Can be null, and cannot be a child node. + * @param subscriber Can not be null. */ - public void subscribe(DSInfo child, DSISubscriber subscriber, DSTopic topic) { + public void subscribe(DSTopic topic, DSInfo child, DSISubscriber subscriber) { if (child != null) { if (child.isNode()) { throw new IllegalArgumentException("Must subscribe nodes directly"); @@ -1219,7 +1223,7 @@ public void subscribe(DSInfo child, DSISubscriber subscriber, DSTopic topic) { firstSubscription = (sub == null); Subscription prev = null; while (sub != null) { - if (sub.matches(child, topic)) { + if (sub.equals(child, topic)) { count++; if (DSUtil.equal(subscriber, sub.subscriber)) { return; @@ -1237,13 +1241,13 @@ public void subscribe(DSInfo child, DSISubscriber subscriber, DSTopic topic) { } } try { - onSubscribe(child, subscriber, topic); + onSubscribe(topic, child, subscriber); } catch (Exception x) { severe(getParent(), x); } if (count == 0) { try { - onSubscribed(child, topic); + onSubscribed(topic, child); } catch (Exception x) { severe(getParent(), x); } @@ -1267,18 +1271,18 @@ protected static DSNode toNode(Object obj) { /** * Unsubscribes the tuple. * + * @param topic Can not be null. * @param child Can be null. * @param subscriber Can not be null. - * @param topic Can not be null. */ - public void unsubscribe(DSInfo child, DSISubscriber subscriber, DSTopic topic) { + public void unsubscribe(DSTopic topic, DSInfo child, DSISubscriber subscriber) { int count = 0; Subscription removed = null; synchronized (this) { Subscription sub = subscription; Subscription prev = null; while (sub != null) { - if (sub.matches(child, topic)) { + if (sub.equals(child, topic)) { count++; if (DSUtil.equal(subscriber, sub.subscriber)) { count--; @@ -1296,13 +1300,13 @@ public void unsubscribe(DSInfo child, DSISubscriber subscriber, DSTopic topic) { } if (removed != null) { try { - onUnsubscribe(child, subscriber, topic); + onUnsubscribe(topic, child, subscriber); } catch (Exception x) { severe(getParent(), x); } if (count == 0) { try { - onUnsubscribed(child, topic); + onUnsubscribed(topic, child); } catch (Exception x) { severe(getParent(), x); } @@ -1411,23 +1415,33 @@ private class Subscription { this.topic = topic; } - boolean matches(DSInfo child) { - return !DSUtil.equal(child, this.child); - } - - boolean matches(DSInfo child, DSTopic topic) { - if (!DSUtil.equal(child, this.child)) { + boolean equals(DSInfo child, DSTopic topic) { + if (!DSUtil.equal(topic, this.topic)) { return false; } + return DSUtil.equal(child, this.child); + } + + boolean shouldFire(DSInfo child, DSTopic topic) { if (!DSUtil.equal(topic, this.topic)) { return false; } + if (topic == VALUE_TOPIC) { + return DSUtil.equal(child, this.child); + } return true; } + boolean shouldUnsubscribe(DSInfo child) { + if (topic == VALUE_TOPIC) { + return DSUtil.equal(child, this.child); + } + return false; + } + void unsubscribe() { try { - subscriber.onUnsubscribed(DSNode.this, child, topic); + subscriber.onUnsubscribed(topic, DSNode.this, child); } catch (Exception x) { severe(getPath(), x); } diff --git a/dslink-core/src/main/java/org/iot/dsa/node/DSValueNode.java b/dslink-core/src/main/java/org/iot/dsa/node/DSValueNode.java index 29f2dd84..36fa598b 100644 --- a/dslink-core/src/main/java/org/iot/dsa/node/DSValueNode.java +++ b/dslink-core/src/main/java/org/iot/dsa/node/DSValueNode.java @@ -20,7 +20,7 @@ public void onChildChanged(DSInfo child) { DSInfo info = getValueChild(); if (child == info) { DSIValue val = info.getValue(); - fire(null, VALUE_TOPIC, Event.NODE_CHANGED); + fire(VALUE_TOPIC, Event.NODE_CHANGED, null); } } diff --git a/dslink-core/src/main/java/org/iot/dsa/node/event/DSIEvent.java b/dslink-core/src/main/java/org/iot/dsa/node/event/DSIEvent.java index 55d261e9..3449d5c4 100644 --- a/dslink-core/src/main/java/org/iot/dsa/node/event/DSIEvent.java +++ b/dslink-core/src/main/java/org/iot/dsa/node/event/DSIEvent.java @@ -7,7 +7,7 @@ * This is an empty interface, DSTopics are allowed to define events however they wish. * * @author Aaron Hansen - * @see DSISubscriber#onEvent(DSNode, DSInfo, DSTopic, DSIEvent, Object...) + * @see DSISubscriber#onEvent(DSTopic, DSIEvent, DSNode, DSInfo, Object...) * @see DSTopic */ public interface DSIEvent { diff --git a/dslink-core/src/main/java/org/iot/dsa/node/event/DSISubscriber.java b/dslink-core/src/main/java/org/iot/dsa/node/event/DSISubscriber.java index de75f424..cbe0a944 100644 --- a/dslink-core/src/main/java/org/iot/dsa/node/event/DSISubscriber.java +++ b/dslink-core/src/main/java/org/iot/dsa/node/event/DSISubscriber.java @@ -18,29 +18,29 @@ * * @see DSIEvent * @see DSTopic - * @see org.iot.dsa.node.DSNode#subscribe(DSInfo, DSISubscriber, DSTopic) + * @see DSNode#subscribe(DSTopic, DSInfo, DSISubscriber) */ public interface DSISubscriber { /** * Subscription callback. * - * @param node Required, node subscribed to. - * @param child Optional, if the event concerns a child. * @param topic Required, the topic emanating the event. * @param event Required, the actual event. + * @param node Required, node subscribed to. + * @param child Optional, if the event concerns a child. * @param params Can be null, only used if the event defines it. */ - public void onEvent(DSNode node, DSInfo child, DSTopic topic, DSIEvent event, Object... params); + public void onEvent(DSTopic topic, DSIEvent event, DSNode node, DSInfo child, Object... params); /** * Called no matter how the unsubscribe happens, whether explicitly or if the node * unsubscribes itself. * + * @param topic The topic that was passed to DSNode.subscribe. * @param node Node that was passed to DSNode.subscribe, never null. * @param child The child that was passed to DSNode.subscribe, may be null. - * @param topic The topic that was passed to DSNode.subscribe. */ - public void onUnsubscribed(DSNode node, DSInfo child, DSTopic topic); + public void onUnsubscribed(DSTopic topic, DSNode node, DSInfo child); } diff --git a/dslink-core/src/main/java/org/iot/dsa/node/event/DSTopic.java b/dslink-core/src/main/java/org/iot/dsa/node/event/DSTopic.java index 7b4c05aa..38009210 100644 --- a/dslink-core/src/main/java/org/iot/dsa/node/event/DSTopic.java +++ b/dslink-core/src/main/java/org/iot/dsa/node/event/DSTopic.java @@ -20,7 +20,7 @@ * @see DSISubscriber * @see DSInfoTopic * @see DSValueTopic - * @see org.iot.dsa.node.DSNode#subscribe(DSInfo, DSISubscriber, DSTopic) + * @see org.iot.dsa.node.DSNode#subscribe(DSTopic, DSInfo, DSISubscriber) */ public class DSTopic implements DSIObject { diff --git a/dslink-core/src/main/java/org/iot/dsa/util/DSUtil.java b/dslink-core/src/main/java/org/iot/dsa/util/DSUtil.java index 404319c9..f9d518e1 100644 --- a/dslink-core/src/main/java/org/iot/dsa/util/DSUtil.java +++ b/dslink-core/src/main/java/org/iot/dsa/util/DSUtil.java @@ -11,7 +11,7 @@ private DSUtil() { } /** - * Comparison that takes null into account. Null == null. + * Comparison that takes null into account; null == null. */ public static boolean equal(Object o1, Object o2) { if (o1 == o2) { diff --git a/dslink-core/src/test/java/org/iot/dsa/dslink/LoggingTest.java b/dslink-core/src/test/java/org/iot/dsa/dslink/LoggingTest.java index 801606dc..960e1ae6 100644 --- a/dslink-core/src/test/java/org/iot/dsa/dslink/LoggingTest.java +++ b/dslink-core/src/test/java/org/iot/dsa/dslink/LoggingTest.java @@ -25,7 +25,7 @@ private void aMethod() { log.exiting("LoggingTest", "aMethod"); } - @Test + //@Test public void test1() throws Exception { Logger.getLogger("").info("My first log"); log.info("My second log"); diff --git a/dslink-core/src/test/java/org/iot/dsa/dslink/PerfTest.java b/dslink-core/src/test/java/org/iot/dsa/dslink/PerfTest.java new file mode 100644 index 00000000..27757b1b --- /dev/null +++ b/dslink-core/src/test/java/org/iot/dsa/dslink/PerfTest.java @@ -0,0 +1,124 @@ +package org.iot.dsa.dslink; + +import org.iot.dsa.node.DSBool; +import org.iot.dsa.node.DSDouble; +import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSInt; +import org.iot.dsa.node.DSNode; +import org.iot.dsa.node.DSString; +import org.iot.dsa.node.action.DSAction; +import org.junit.Test; + +/** + * @author Aaron Hansen + */ +public class PerfTest { + + private static final boolean WITH_DEFAULTS = true; + private DSNode root; + + private String format(long mem) { + return (mem / 1000) + "KB"; + } + + private void iterate(DSNode node) { + DSInfo info = node.getFirstInfo(); + while (info != null) { + if (info.isNode()) { + iterate(info.getNode()); + } + info = info.next(); + } + } + + private DSNode makeNode() { + if (WITH_DEFAULTS) { + return new TestNode(); + } + DSNode ret = new DSNode(); + ret.put("string", "abc"); + ret.put("int", DSInt.valueOf(1000)); + ret.put("double", DSDouble.valueOf(12345.1d)); + ret.put("boolean", DSBool.valueOf(true)); + return ret; + } + + private long memoryUsed() { + Runtime rt = Runtime.getRuntime(); + long mem = rt.totalMemory() - rt.freeMemory(); + long tmp; + for (int i = 2; --i >= 0; ) { + System.gc(); + tmp = rt.totalMemory() - rt.freeMemory(); + if (tmp < mem) { + mem = tmp; + } + try { + Thread.sleep(1000); + } catch (Exception x) { + x.printStackTrace(); + } + tmp = rt.totalMemory() - rt.freeMemory(); + if (tmp < mem) { + mem = tmp; + } + } + return mem; + } + + //@Test + public void execute() { + System.out.println("\nStarting " + new java.util.Date()); + test(false); + test(false); + test(true); + } + + public void test(boolean print) { + root = null; + long before = memoryUsed(); + if (print) { + System.out.println("Memory before: " + format(before)); + } + long time = System.currentTimeMillis(); + root = makeNode(); + for (int i = 0; i < 10; i++) { + DSNode iNode = makeNode(); + root.add("test" + i, iNode); + for (int j = 0; j < 100; j++) { + DSNode jNode = makeNode(); + iNode.add("test" + j, jNode); + for (int k = 0; k < 100; k++) { + jNode.add("test" + k, makeNode()); + } + } + } + time = System.currentTimeMillis() - time; + long after = memoryUsed(); + if (print) { + System.out.println("Memory after: " + format(after)); + System.out.println("Memory used: " + format(after - before)); + System.out.println("Create time: " + time + "ms"); + } + time = System.currentTimeMillis(); + iterate(root); + time = System.currentTimeMillis() - time; + after = memoryUsed(); + if (print) { + System.out.println("Iterate time: " + time + "ms"); + System.out.println("Memory used: " + format(after - before)); + } + } + + public static class TestNode extends DSNode { + + public void declareDefaults() { + super.declareDefaults(); + declareDefault("string", DSString.valueOf("abc")); + declareDefault("int", DSInt.valueOf(1000)); + declareDefault("double", DSDouble.valueOf(12345.1d)); + declareDefault("boolean", DSBool.valueOf(true)); + declareDefault("action", DSAction.DEFAULT); + } + } +} diff --git a/dslink-core/src/test/java/org/iot/dsa/dslink/RequesterSubscribeTest.java b/dslink-core/src/test/java/org/iot/dsa/dslink/RequesterSubscribeTest.java index af228073..e9fbece4 100644 --- a/dslink-core/src/test/java/org/iot/dsa/dslink/RequesterSubscribeTest.java +++ b/dslink-core/src/test/java/org/iot/dsa/dslink/RequesterSubscribeTest.java @@ -5,6 +5,7 @@ import org.iot.dsa.dslink.requester.SimpleRequestHandler; import org.iot.dsa.node.DSElement; import org.iot.dsa.node.DSIValue; +import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSInt; import org.iot.dsa.node.DSNode; import org.iot.dsa.node.DSStatus; diff --git a/dslink-java-v2-test/dslink.json b/dslink-java-v2-test/dslink.json index eaa33e8f..9a9fbcf3 100644 --- a/dslink-java-v2-test/dslink.json +++ b/dslink-java-v2-test/dslink.json @@ -16,21 +16,6 @@ "type": "url", "value": null }, - "connectionType": { - "desc": "Can be specified to override the default connection type.", - "type": "string", - "value": null - }, - "isRequester": { - "desc": "Whether or not the link is a requester.", - "type": "boolean", - "value": false - }, - "isResponder": { - "desc": "Whether or not the link is a responder.", - "type": "boolean", - "value": true - }, "key": { "desc": "Path to public/private key pair.", "type": "path", @@ -41,40 +26,10 @@ "type": "enum", "value": "info" }, - "logFile": { - "desc": "Path to the log file.", - "type": "path", - "value": null - }, - "nodes": { - "desc": "Path to configuration database.", - "type": "path", - "value": null - }, - "readTimeout": { - "desc": "Millis timeout for reading from the connection.", - "type": "number", - "value": 60000 - }, - "saveInterval": { - "desc": "How often to backup the node database in minutes.", - "type": "number", - "value": 60 - }, - "stableDelay": { - "desc": "Millis after the node tree is started before calling stable.", - "type": "number", - "value": 5000 - }, "token": { "desc": "Authentication token for the broker.", "type": "string", "value": null - }, - "transportFactory": { - "desc": "Can be specified to override the default standalone transport.", - "type": "string", - "value": null } } } diff --git a/dslink-java-v2-test/src/main/java/org/iot/dsa/dslink/test/MainNode.java b/dslink-java-v2-test/src/main/java/org/iot/dsa/dslink/test/MainNode.java index 00bdee49..fbc0c3c5 100644 --- a/dslink-java-v2-test/src/main/java/org/iot/dsa/dslink/test/MainNode.java +++ b/dslink-java-v2-test/src/main/java/org/iot/dsa/dslink/test/MainNode.java @@ -59,7 +59,7 @@ protected void declareDefaults() { DSJavaEnum.valueOf(MyEnum.Off), "My action description"); declareDefault("Reset", action); - declareDefault("Test", new DSAction()); + declareDefault("Test", DSAction.DEFAULT); } /** From 5f5b5899af4e7217c28b485866be1394d1e9b4d8 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sun, 28 Jan 2018 16:12:14 -0800 Subject: [PATCH 2/3] DSLink no longer DSIResponder. --- .../responder/DS1InboundInvoke.java | 28 ++++- .../protocol_v1/responder/DS1InboundList.java | 67 +++++++++++- .../responder/DS1InboundRequest.java | 36 +++---- .../protocol_v1/responder/DS1InboundSet.java | 35 +++++- .../responder/DS1InboundSubscription.java | 89 ++++++++++++--- .../responder/DS1InboundSubscriptions.java | 28 ++--- .../protocol_v1/responder/DS1Responder.java | 18 ++-- .../protocol_v1/responder}/RequestPath.java | 12 +-- .../main/java/org/iot/dsa/dslink/DSLink.java | 97 +---------------- .../org/iot/dsa/dslink/InvokeWrapper.java | 101 ------------------ .../org/iot/dsa/dslink/ListSubscriber.java | 80 -------------- .../java/org/iot/dsa/dslink/ListWrapper.java | 76 ------------- .../java/org/iot/dsa/dslink/SetWrapper.java | 62 ----------- .../org/iot/dsa/dslink/SubscribeWrapper.java | 67 ------------ .../org/iot/dsa/dslink/ValueSubscriber.java | 90 ---------------- 15 files changed, 234 insertions(+), 652 deletions(-) rename dslink-core/src/main/java/{org/iot/dsa/dslink => com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder}/RequestPath.java (88%) delete mode 100644 dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java delete mode 100644 dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java delete mode 100644 dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java delete mode 100644 dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java delete mode 100644 dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java delete mode 100644 dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundInvoke.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundInvoke.java index 26632642..6e969d21 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundInvoke.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundInvoke.java @@ -1,19 +1,23 @@ package com.acuity.iot.dsa.dslink.protocol.protocol_v1.responder; +import com.acuity.iot.dsa.dslink.protocol.DSStream; import com.acuity.iot.dsa.dslink.protocol.message.ErrorResponse; import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; -import com.acuity.iot.dsa.dslink.protocol.DSStream; import java.util.Iterator; import org.iot.dsa.DSRuntime; +import org.iot.dsa.dslink.DSIResponder; +import org.iot.dsa.dslink.DSRequestException; import org.iot.dsa.dslink.responder.InboundInvokeRequest; import org.iot.dsa.io.DSIWriter; import org.iot.dsa.node.DSIValue; +import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSList; import org.iot.dsa.node.DSMap; import org.iot.dsa.node.action.ActionResult; import org.iot.dsa.node.action.ActionSpec; import org.iot.dsa.node.action.ActionTable; import org.iot.dsa.node.action.ActionValues; +import org.iot.dsa.node.action.DSAction; import org.iot.dsa.security.DSPermission; /** @@ -162,15 +166,29 @@ private void enqueueResponse() { */ public void run() { try { - result = getResponderImpl().onInvoke(this); - if (result == null) { - close(); + RequestPath path = new RequestPath(getPath(), getLink()); + if (path.isResponder()) { + DSIResponder responder = (DSIResponder) path.getTarget(); + setPath(path.getPath()); + result = responder.onInvoke(this); } + DSInfo info = path.getInfo(); + if (!info.isAction()) { + throw new DSRequestException("Not an action " + path.getPath()); + } + //TODO verify incoming permission + DSAction action = info.getAction(); + result = action.invoke(info, this); } catch (Exception x) { severe(getPath(), x); close(x); + return; + } + if (result == null) { + close(); + } else { + enqueueResponse(); } - enqueueResponse(); } /** diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundList.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundList.java index 27ac8b70..e39e96d7 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundList.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundList.java @@ -1,10 +1,11 @@ package com.acuity.iot.dsa.dslink.protocol.protocol_v1.responder; +import com.acuity.iot.dsa.dslink.protocol.DSStream; import com.acuity.iot.dsa.dslink.protocol.message.ErrorResponse; import com.acuity.iot.dsa.dslink.protocol.message.OutboundMessage; -import com.acuity.iot.dsa.dslink.protocol.DSStream; import java.util.Iterator; import org.iot.dsa.DSRuntime; +import org.iot.dsa.dslink.DSIResponder; import org.iot.dsa.dslink.responder.ApiObject; import org.iot.dsa.dslink.responder.InboundListRequest; import org.iot.dsa.dslink.responder.OutboundListResponse; @@ -16,9 +17,14 @@ import org.iot.dsa.node.DSMap; import org.iot.dsa.node.DSMap.Entry; import org.iot.dsa.node.DSMetadata; +import org.iot.dsa.node.DSNode; import org.iot.dsa.node.DSPath; import org.iot.dsa.node.action.ActionSpec; import org.iot.dsa.node.action.DSAction; +import org.iot.dsa.node.event.DSIEvent; +import org.iot.dsa.node.event.DSISubscriber; +import org.iot.dsa.node.event.DSInfoTopic; +import org.iot.dsa.node.event.DSTopic; /** * List implementation for a responder. @@ -26,7 +32,8 @@ * @author Aaron Hansen */ class DS1InboundList extends DS1InboundRequest - implements DSStream, InboundListRequest, OutboundMessage, Runnable { + implements DSISubscriber, DSStream, InboundListRequest, OutboundMessage, + OutboundListResponse, Runnable { /////////////////////////////////////////////////////////////////////////// // Constants @@ -47,6 +54,8 @@ class DS1InboundList extends DS1InboundRequest private DSMetadata cacheMeta = new DSMetadata(cacheMap); private Iterator children; private Exception closeReason; + private DSInfo info; + private DSNode node; private OutboundListResponse response; private boolean enqueued = false; private int state = STATE_INIT; @@ -464,6 +473,11 @@ private DSMap fixType(DSMap arg) { return arg; } + @Override + public ApiObject getTarget() { + return info; + } + private boolean isClosed() { return state == STATE_CLOSED; } @@ -480,15 +494,60 @@ public boolean isOpen() { return (state != STATE_CLOSE_PENDING) && (state != STATE_CLOSED); } + @Override + public void onClose() { + if (node != null) { + node.unsubscribe(DSNode.INFO_TOPIC, null, this); + } + } + + @Override + public void onEvent(DSTopic topic, DSIEvent event, DSNode node, DSInfo child, + Object... params) { + switch ((DSInfoTopic.Event) event) { + case CHILD_ADDED: + childAdded(child); + break; + case CHILD_REMOVED: + childRemoved(child); + break; + case METADATA_CHANGED: //TODO + break; + default: + } + } + + @Override + public void onUnsubscribed(DSTopic topic, DSNode node, DSInfo child) { + close(); + } + @Override public void run() { try { - response = getResponderImpl().onList(this); + RequestPath path = new RequestPath(getPath(), getLink()); + if (path.isResponder()) { + DSIResponder responder = (DSIResponder) path.getTarget(); + setPath(path.getPath()); + response = responder.onList(this); + } else { + info = path.getInfo(); + if (info.isNode()) { + node = info.getNode(); + node.subscribe(DSNode.INFO_TOPIC, null, this); + } + response = this; + } } catch (Exception x) { severe(getPath(), x); close(x); + return; + } + if (response == null) { + close(); + } else { + enqueueResponse(); } - enqueueResponse(); } @Override diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundRequest.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundRequest.java index def50b4c..dd371510 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundRequest.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundRequest.java @@ -1,7 +1,7 @@ package com.acuity.iot.dsa.dslink.protocol.protocol_v1.responder; import com.acuity.iot.dsa.dslink.protocol.protocol_v1.DS1Session; -import org.iot.dsa.dslink.DSIResponder; +import org.iot.dsa.dslink.DSLink; import org.iot.dsa.dslink.responder.InboundRequest; import org.iot.dsa.logging.DSLogger; import org.iot.dsa.node.DSMap; @@ -13,20 +13,16 @@ */ class DS1InboundRequest extends DSLogger implements InboundRequest { - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// // Fields /////////////////////////////////////////////////////////////////////////// + private DSLink link; private String path; - private DS1Session session; private DSMap request; private Integer requestId; - private DSIResponder responderImpl; private DS1Responder responder; + private DS1Session session; /////////////////////////////////////////////////////////////////////////// // Constructors @@ -39,6 +35,10 @@ class DS1InboundRequest extends DSLogger implements InboundRequest { // Methods in alphabetical order /////////////////////////////////////////////////////////////////////////// + public DSLink getLink() { + return link; + } + public String getPath() { return path; } @@ -51,10 +51,6 @@ public DS1Responder getResponder() { return responder; } - public DSIResponder getResponderImpl() { - return responderImpl; - } - public Integer getRequestId() { return requestId; } @@ -63,6 +59,11 @@ public DS1Session getSession() { return session; } + public DS1InboundRequest setLink(DSLink link) { + this.link = link; + return this; + } + public DS1InboundRequest setPath(String path) { this.path = path; return this; @@ -78,11 +79,6 @@ public DS1InboundRequest setRequest(DSMap request) { return this; } - public DS1InboundRequest setResponderImpl(DSIResponder responderImpl) { - this.responderImpl = responderImpl; - return this; - } - public DS1InboundRequest setRequestId(Integer requestId) { this.requestId = requestId; return this; @@ -93,12 +89,4 @@ public DS1InboundRequest setResponder(DS1Responder responder) { return this; } - /////////////////////////////////////////////////////////////////////////// - // Inner Classes - /////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////////// - // Initialization - /////////////////////////////////////////////////////////////////////////// - } diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSet.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSet.java index 667a8fed..e3bd1a6b 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSet.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSet.java @@ -2,9 +2,14 @@ import com.acuity.iot.dsa.dslink.protocol.message.CloseMessage; import com.acuity.iot.dsa.dslink.protocol.message.ErrorResponse; +import org.iot.dsa.dslink.DSIResponder; +import org.iot.dsa.dslink.DSRequestException; import org.iot.dsa.dslink.responder.InboundSetRequest; import org.iot.dsa.node.DSElement; +import org.iot.dsa.node.DSIValue; +import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSMap; +import org.iot.dsa.node.DSNode; import org.iot.dsa.security.DSPermission; class DS1InboundSet extends DS1InboundRequest implements InboundSetRequest, Runnable { @@ -32,7 +37,35 @@ public DSElement getValue() { @Override public void run() { try { - getResponderImpl().onSet(this); + RequestPath path = new RequestPath(getPath(), getLink()); + if (path.isResponder()) { + DSIResponder responder = (DSIResponder) path.getTarget(); + setPath(path.getPath()); + responder.onSet(this); + } else { + DSNode parent = path.getParent(); + DSInfo info = path.getInfo(); + if (info.isReadOnly()) { + throw new DSRequestException("Not writable: " + getPath()); + } + //TODO verify incoming permission + if (info.isNode()) { + info.getNode().onSet(value); + } else { + DSIValue current = info.getValue(); + if (current == null) { + if (info.getDefaultObject() instanceof DSIValue) { + current = (DSIValue) info.getDefaultObject(); + } + } + if (current != null) { + current = current.valueOf(value); + } else { + current = value; + } + parent.onSet(info, current); + } + } getResponder().sendResponse( new CloseMessage(getRequestId()).setMethod(null).setStream("closed")); } catch (Exception x) { diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscription.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscription.java index 61e9d25b..52e12b91 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscription.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscription.java @@ -2,11 +2,19 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.iot.dsa.dslink.DSIResponder; import org.iot.dsa.dslink.responder.InboundSubscribeRequest; import org.iot.dsa.dslink.responder.SubscriptionCloseHandler; import org.iot.dsa.io.DSIWriter; +import org.iot.dsa.node.DSIStatus; import org.iot.dsa.node.DSIValue; +import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSNode; import org.iot.dsa.node.DSStatus; +import org.iot.dsa.node.event.DSIEvent; +import org.iot.dsa.node.event.DSISubscriber; +import org.iot.dsa.node.event.DSTopic; +import org.iot.dsa.node.event.DSValueTopic.Event; import org.iot.dsa.time.DSTime; /** @@ -14,19 +22,18 @@ * * @author Aaron Hansen */ -class DS1InboundSubscription extends DS1InboundRequest implements InboundSubscribeRequest { - - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// +class DS1InboundSubscription extends DS1InboundRequest + implements DSISubscriber, InboundSubscribeRequest { /////////////////////////////////////////////////////////////////////////// // Fields /////////////////////////////////////////////////////////////////////////// + private DSInfo child; private SubscriptionCloseHandler closeHandler; private boolean enqueued = false; private DS1InboundSubscriptions manager; + private DSNode node; private boolean open = true; private Integer sid; private int qos = 0; @@ -37,8 +44,13 @@ class DS1InboundSubscription extends DS1InboundRequest implements InboundSubscri // Constructors /////////////////////////////////////////////////////////////////////////// - DS1InboundSubscription(DS1InboundSubscriptions manager) { + DS1InboundSubscription(DS1InboundSubscriptions manager, Integer sid, String path, int qos) { this.manager = manager; + this.sid = sid; + setPath(path); + this.qos = qos; + setLink(manager.getLink()); + init(); } /////////////////////////////////////////////////////////////////////////// @@ -81,7 +93,33 @@ public Integer getSubscriptionId() { return sid; } - public void onClose() { + private void init() { + RequestPath path = new RequestPath(getPath(), getLink()); + if (path.isResponder()) { + DSIResponder responder = (DSIResponder) path.getTarget(); + setPath(path.getPath()); + closeHandler = responder.onSubscribe(this); + } else { + DSInfo info = path.getInfo(); + if (info.isNode()) { + node = info.getNode(); + node.subscribe(DSNode.VALUE_TOPIC, null, this); + onEvent(DSNode.VALUE_TOPIC, Event.NODE_CHANGED, info.getNode(), null, + (Object[]) null); + } else { + node = path.getParent(); + child = info; + node.subscribe(DSNode.VALUE_TOPIC, info, this); + onEvent(DSNode.VALUE_TOPIC, Event.CHILD_CHANGED, node, info, + (Object[]) null); + } + } + } + + /** + * Called by DSSubcriptions no matter how closed. + */ + void onClose() { synchronized (this) { if (!open) { return; @@ -95,6 +133,34 @@ public void onClose() { } catch (Exception x) { getLogger().log(Level.WARNING, toString(), x); } + try { + if (node != null) { + node.unsubscribe(DSNode.VALUE_TOPIC, child, this); + } + } catch (Exception x) { + getLogger().log(Level.WARNING, toString(), x); + } + } + + @Override + public void onEvent(DSTopic topic, DSIEvent event, DSNode node, DSInfo child, + Object... params) { + DSIValue value; + if (child != null) { + value = child.getValue(); + } else { + value = (DSIValue) node; + } + DSStatus quality = DSStatus.ok; + if (value instanceof DSIStatus) { + quality = ((DSIStatus) value).toStatus(); + } + update(System.currentTimeMillis(), value, quality); + } + + @Override + public void onUnsubscribed(DSTopic topic, DSNode node, DSInfo info) { + close(); } /** @@ -134,11 +200,6 @@ public void update(long timestamp, DSIValue value, DSStatus quality) { manager.enqueue(this); } - DS1InboundSubscription setCloseHandler(SubscriptionCloseHandler closeHandler) { - this.closeHandler = closeHandler; - return this; - } - DS1InboundSubscription setQos(int qos) { this.qos = qos; return this; @@ -211,8 +272,4 @@ Update set(long timestamp, DSIValue value, DSStatus quality) { } } - /////////////////////////////////////////////////////////////////////////// - // Initialization - /////////////////////////////////////////////////////////////////////////// - } diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscriptions.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscriptions.java index 896511b3..b7c1c388 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscriptions.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1InboundSubscriptions.java @@ -5,7 +5,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Logger; -import org.iot.dsa.dslink.DSIResponder; +import org.iot.dsa.dslink.DSLink; import org.iot.dsa.io.DSIWriter; import org.iot.dsa.logging.DSLogger; @@ -32,7 +32,7 @@ class DS1InboundSubscriptions extends DSLogger implements OutboundMessage { new ConcurrentLinkedQueue(); private Map pathMap = new ConcurrentHashMap(); - private DS1Responder session; + private DS1Responder responder; private Map sidMap = new ConcurrentHashMap(); private StringBuilder timestampBuffer = new StringBuilder(); @@ -41,8 +41,8 @@ class DS1InboundSubscriptions extends DSLogger implements OutboundMessage { // Constructors /////////////////////////////////////////////////////////////////////////// - DS1InboundSubscriptions(DS1Responder session) { - this.session = session; + DS1InboundSubscriptions(DS1Responder responder) { + this.responder = responder; } /////////////////////////////////////////////////////////////////////////// @@ -69,13 +69,17 @@ void enqueue(DS1InboundSubscription subscription) { } enqueued = true; } - session.sendResponse(this); + responder.sendResponse(this); + } + + DSLink getLink() { + return responder.getConnection().getLink(); } @Override public Logger getLogger() { if (logger == null) { - logger = Logger.getLogger(session.getConnection().getLink().getLinkName() + logger = Logger.getLogger(responder.getConnection().getLink().getLinkName() + ".responderSubscriptions"); } return logger; @@ -84,18 +88,16 @@ public Logger getLogger() { /** * Create or update a subscription. */ - void subscribe(DSIResponder responder, Integer sid, String path, int qos) { + void subscribe(Integer sid, String path, int qos) { finest(finest() ? "Subscribing " + path : null); DS1InboundSubscription subscription = sidMap.get(sid); if (subscription == null) { - subscription = new DS1InboundSubscription(this); - subscription.setSubscriptionId(sid).setQos(qos).setPath(path); + subscription = new DS1InboundSubscription(this, sid, path, qos); sidMap.put(sid, subscription); pathMap.put(path, subscription); - subscription.setCloseHandler(responder.onSubscribe(subscription)); } else if (!path.equals(subscription.getPath())) { unsubscribe(sid); - subscribe(responder, sid, path, qos); + subscribe(sid, path, qos); } else { subscription.setQos(qos); //TODO refresh subscription, align w/v2 @@ -124,7 +126,7 @@ public void write(DSIWriter out) { out.key("rid").value(ZERO); out.key("updates").beginList(); DS1InboundSubscription sub; - while (!session.shouldEndMessage()) { + while (!responder.shouldEndMessage()) { sub = outbound.poll(); if (sub == null) { break; @@ -140,7 +142,7 @@ public void write(DSIWriter out) { return; } } - session.sendResponse(this); + responder.sendResponse(this); } /////////////////////////////////////////////////////////////////////////// diff --git a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1Responder.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1Responder.java index 3afb881b..adc4fe0a 100644 --- a/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1Responder.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/DS1Responder.java @@ -11,7 +11,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.iot.dsa.DSRuntime; -import org.iot.dsa.dslink.DSIResponder; +import org.iot.dsa.dslink.DSLink; import org.iot.dsa.dslink.DSLinkConnection; import org.iot.dsa.node.DSList; import org.iot.dsa.node.DSMap; @@ -30,9 +30,9 @@ public class DS1Responder extends DSNode { private ConcurrentHashMap inboundRequests = new ConcurrentHashMap(); + private DSLink link; private Logger logger; private DS1Session session; - private DSIResponder responder; private DS1InboundSubscriptions subscriptions = new DS1InboundSubscriptions(this); @@ -103,7 +103,7 @@ private void processInvoke(Integer rid, DSMap req) { invokeImpl.setPath(getPath(req)) .setSession(session) .setRequestId(rid) - .setResponderImpl(responder) + .setLink(link) .setResponder(this); inboundRequests.put(rid, invokeImpl); DSRuntime.run(invokeImpl); @@ -118,7 +118,7 @@ private void processList(Integer rid, DSMap req) { .setSession(session) .setRequest(req) .setRequestId(rid) - .setResponderImpl(responder) + .setLink(link) .setResponder(this); inboundRequests.put(listImpl.getRequestId(), listImpl); DSRuntime.run(listImpl); @@ -128,10 +128,10 @@ private void processList(Integer rid, DSMap req) { * Process an individual request. */ public void processRequest(final Integer rid, final DSMap map) { - if (responder == null) { - responder = getConnection().getLink(); + if (link == null) { + link = getConnection().getLink(); } - if (responder == null) { + if (link == null) { throw new DSProtocolException("Not a responder"); } String method = map.get("method", null); @@ -228,7 +228,7 @@ private void processSet(Integer rid, DSMap req) { setImpl.setPath(getPath(req)) .setSession(session) .setRequestId(rid) - .setResponderImpl(responder) + .setLink(link) .setResponder(this); DSRuntime.run(setImpl); } @@ -251,7 +251,7 @@ private void processSubscribe(int rid, DSMap req) { sid = subscribe.getInt("sid"); qos = subscribe.get("qos", 0); try { - subscriptions.subscribe(responder, sid, path, qos); + subscriptions.subscribe(sid, path, qos); } catch (Exception x) { //invalid paths are very common fine(path, x); diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/RequestPath.java b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/RequestPath.java similarity index 88% rename from dslink-core/src/main/java/org/iot/dsa/dslink/RequestPath.java rename to dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/RequestPath.java index 2785162d..6bc28e19 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/RequestPath.java +++ b/dslink-core/src/main/java/com/acuity/iot/dsa/dslink/protocol/protocol_v1/responder/RequestPath.java @@ -1,5 +1,7 @@ -package org.iot.dsa.dslink; +package com.acuity.iot.dsa.dslink.protocol.protocol_v1.responder; +import org.iot.dsa.dslink.DSIResponder; +import org.iot.dsa.dslink.DSInvalidPathException; import org.iot.dsa.node.DSIObject; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSNode; @@ -12,10 +14,6 @@ */ class RequestPath { - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// // Fields /////////////////////////////////////////////////////////////////////////// @@ -111,8 +109,4 @@ boolean isResponder() { return target instanceof DSIResponder; } - /////////////////////////////////////////////////////////////////////////// - // Inner Classes - /////////////////////////////////////////////////////////////////////////// - } diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLink.java b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLink.java index 8ee0d518..20c0cf32 100644 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/DSLink.java +++ b/dslink-core/src/main/java/org/iot/dsa/dslink/DSLink.java @@ -51,7 +51,7 @@ * * @author Aaron Hansen */ -public class DSLink extends DSNode implements DSIResponder, Runnable { +public class DSLink extends DSNode implements Runnable { /////////////////////////////////////////////////////////////////////////// // Constants @@ -153,7 +153,7 @@ public Logger getLogger() { /** * Returns the root of the node tree. */ - public DSMainNode getNodes() { + public DSMainNode getMain() { return (DSMainNode) main.getNode(); } @@ -268,91 +268,6 @@ public void run() { return super.onInvoke(actionInfo, invocation); } - /** - * Responder implementation. If one of the children in the path implements DSResponder, it will - * be given responsibility for completing the request. - */ - @Override - public ActionResult onInvoke(InboundInvokeRequest request) { - RequestPath path = new RequestPath(request.getPath(), this); - if (path.isResponder()) { - DSIResponder responder = (DSIResponder) path.getTarget(); - return responder.onInvoke(new InvokeWrapper(path, request)); - } - DSInfo info = path.getInfo(); - if (!info.isAction()) { - throw new DSRequestException("Not an action " + path.getPath()); - } - //TODO verify incoming permission - DSAction action = info.getAction(); - return action.invoke(info, request); - } - - /** - * Responder implementation. If one of the children in the path implements DSResponder, it will - * be given responsibility for completing the request. - */ - @Override - public OutboundListResponse onList(InboundListRequest request) { - RequestPath path = new RequestPath(request.getPath(), this); - if (path.isResponder()) { - DSIResponder responder = (DSIResponder) path.getTarget(); - return responder.onList(new ListWrapper(path.getPath(), request)); - } - return new ListSubscriber(path, request); - } - - /** - * Responder implementation. If one of the children in the path implements DSResponder, it will - * be given responsibility for completing the request. - */ - @Override - public SubscriptionCloseHandler onSubscribe(InboundSubscribeRequest request) { - RequestPath path = new RequestPath(request.getPath(), this); - if (path.isResponder()) { - DSIResponder responder = (DSIResponder) path.getTarget(); - return responder.onSubscribe(new SubscribeWrapper(path.getPath(), request)); - } - return new ValueSubscriber(path, request); - } - - - /** - * Responder implementation. If one of the children in the path implements DSResponder, it will - * be given responsibility for completing the request. - */ - @Override - public void onSet(InboundSetRequest request) { - RequestPath path = new RequestPath(request.getPath(), this); - if (path.isResponder()) { - DSIResponder responder = (DSIResponder) path.getTarget(); - responder.onSet(new SetWrapper(path.getPath(), request)); - return; - } - DSNode parent = path.getParent(); - DSInfo info = path.getInfo(); - if (info.isReadOnly()) { - throw new DSRequestException("Not writable: " + getPath()); - } - //TODO verify incoming permission - if (info.isNode()) { - info.getNode().onSet(request.getValue()); - return; - } - DSIValue value = info.getValue(); - if (value == null) { - if (info.getDefaultObject() instanceof DSIValue) { - value = (DSIValue) info.getDefaultObject(); - } - } - if (value != null) { - value = value.valueOf(request.getValue()); - } else { - value = request.getValue(); - } - parent.onSet(info, value); - } - /** * Calls starts, waits the stableDelay, then calls stable. Does not return until this node is * stopped. @@ -575,12 +490,4 @@ public boolean accept(File dir, String name) { } } - /////////////////////////////////////////////////////////////////////////// - // Inner Classes - /////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////////// - // Initialization - /////////////////////////////////////////////////////////////////////////// - } diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java deleted file mode 100644 index ef607abc..00000000 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/InvokeWrapper.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.iot.dsa.dslink; - -import org.iot.dsa.dslink.responder.InboundInvokeRequest; -import org.iot.dsa.node.DSList; -import org.iot.dsa.node.DSMap; -import org.iot.dsa.security.DSPermission; - -/** - * Used by DSMainNode to handle an invoke request. - * - * @author Aaron Hansen - */ -class InvokeWrapper implements InboundInvokeRequest { - - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////////// - // Fields - /////////////////////////////////////////////////////////////////////////// - - private RequestPath path; - private InboundInvokeRequest request; - - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// - - InvokeWrapper(RequestPath path, InboundInvokeRequest request) { - this.path = path; - this.request = request; - } - - /////////////////////////////////////////////////////////////////////////// - // Methods - /////////////////////////////////////////////////////////////////////////// - - @Override - public void clearAllRows() { - request.clearAllRows(); - } - - @Override - public void close() { - request.close(); - } - - @Override - public void close(Exception reason) { - request.close(reason); - } - - public void onClose() { - } - - @Override - public DSMap getParameters() { - return request.getParameters(); - } - - @Override - public String getPath() { - return path.getPath(); - } - - @Override - public DSPermission getPermission() { - return request.getPermission(); - } - - @Override - public Integer getRequestId() { - return request.getRequestId(); - } - - @Override - public void insert(int index, DSList... rows) { - request.insert(index, rows); - } - - @Override - public boolean isOpen() { - return request.isOpen(); - } - - @Override - public void send(DSList row) { - request.send(row); - } - - @Override - public void replace(int idx, int len, DSList... rows) { - request.replace(idx, len, rows); - } - - /////////////////////////////////////////////////////////////////////////// - // Inner Classes - /////////////////////////////////////////////////////////////////////////// - -} diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java deleted file mode 100644 index 2299e7d7..00000000 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ListSubscriber.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.iot.dsa.dslink; - -import org.iot.dsa.dslink.responder.ApiObject; -import org.iot.dsa.dslink.responder.InboundListRequest; -import org.iot.dsa.dslink.responder.OutboundListResponse; -import org.iot.dsa.node.DSInfo; -import org.iot.dsa.node.DSNode; -import org.iot.dsa.node.event.DSIEvent; -import org.iot.dsa.node.event.DSISubscriber; -import org.iot.dsa.node.event.DSInfoTopic; -import org.iot.dsa.node.event.DSTopic; - -/** - * Used by DSMainNode to subscribe to the target of a list request and update the stream when - * changes are detected. - * - * @author Aaron Hansen - */ -class ListSubscriber implements DSISubscriber, OutboundListResponse { - - /////////////////////////////////////////////////////////////////////////// - // Fields - /////////////////////////////////////////////////////////////////////////// - - private DSInfo info; - private DSNode node; - private InboundListRequest request; - - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// - - ListSubscriber(RequestPath path, InboundListRequest request) { - this.request = request; - this.info = path.getInfo(); - if (info.isNode()) { - this.node = info.getNode(); - node.subscribe(DSNode.INFO_TOPIC, null, this); - } - } - - /////////////////////////////////////////////////////////////////////////// - // Methods - /////////////////////////////////////////////////////////////////////////// - - @Override - public ApiObject getTarget() { - return info; - } - - @Override - public void onClose() { - if (node != null) { - node.unsubscribe(DSNode.INFO_TOPIC, null, this); - } - } - - @Override - public void onEvent(DSTopic topic, DSIEvent event, DSNode node, - DSInfo child, - Object... params) { - switch ((DSInfoTopic.Event) event) { - case CHILD_ADDED: - request.childAdded(child); - break; - case CHILD_REMOVED: - request.childRemoved(child); - break; - case METADATA_CHANGED: //TODO - break; - default: - } - } - - @Override - public void onUnsubscribed(DSTopic topic, DSNode node, DSInfo child) { - request.close(); - } - -} diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java deleted file mode 100644 index c90c0089..00000000 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ListWrapper.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.iot.dsa.dslink; - -import org.iot.dsa.dslink.responder.ApiObject; -import org.iot.dsa.dslink.responder.InboundListRequest; - -/** - * Used by DSMainNode to handle and list request. - * - * @author Aaron Hansen - */ -class ListWrapper implements InboundListRequest { - - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////////// - // Fields - /////////////////////////////////////////////////////////////////////////// - - private String path; - private InboundListRequest request; - - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// - - ListWrapper(String path, InboundListRequest request) { - this.path = path; - this.request = request; - } - - /////////////////////////////////////////////////////////////////////////// - // Methods - /////////////////////////////////////////////////////////////////////////// - - @Override - public void childAdded(ApiObject obj) { - request.childAdded(obj); - } - - @Override - public void childRemoved(ApiObject obj) { - request.childRemoved(obj); - } - - @Override - public void close() { - request.close(); - } - - @Override - public void close(Exception reason) { - request.close(reason); - } - - @Override - public String getPath() { - return path; - } - - @Override - public Integer getRequestId() { - return request.getRequestId(); - } - - @Override - public boolean isOpen() { - return request.isOpen(); - } - - /////////////////////////////////////////////////////////////////////////// - // Inner Classes - /////////////////////////////////////////////////////////////////////////// - -} diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java deleted file mode 100644 index bd6a9658..00000000 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/SetWrapper.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.iot.dsa.dslink; - -import org.iot.dsa.dslink.responder.InboundSetRequest; -import org.iot.dsa.node.DSElement; -import org.iot.dsa.security.DSPermission; - -/** - * Used by DSMainNode to handle a set request. - * - * @author Aaron Hansen - */ -class SetWrapper implements InboundSetRequest { - - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////////// - // Fields - /////////////////////////////////////////////////////////////////////////// - - private String path; - private InboundSetRequest request; - - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// - - SetWrapper(String path, InboundSetRequest request) { - this.path = path; - this.request = request; - } - - /////////////////////////////////////////////////////////////////////////// - // Methods - /////////////////////////////////////////////////////////////////////////// - - @Override - public String getPath() { - return path; - } - - @Override - public DSPermission getPermission() { - return request.getPermission(); - } - - @Override - public Integer getRequestId() { - return request.getRequestId(); - } - - @Override - public DSElement getValue() { - return request.getValue(); - } - - /////////////////////////////////////////////////////////////////////////// - // Inner Classes - /////////////////////////////////////////////////////////////////////////// - -} diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java b/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java deleted file mode 100644 index 192c448a..00000000 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/SubscribeWrapper.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.iot.dsa.dslink; - -import org.iot.dsa.dslink.responder.InboundSubscribeRequest; -import org.iot.dsa.node.DSIValue; -import org.iot.dsa.node.DSStatus; - -/** - * Used by DSMainNode to handle subscribe requests. - * - * @author Aaron Hansen - */ -class SubscribeWrapper implements InboundSubscribeRequest { - - /////////////////////////////////////////////////////////////////////////// - // Constants - /////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////////// - // Fields - /////////////////////////////////////////////////////////////////////////// - - private String path; - private InboundSubscribeRequest request; - - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// - - SubscribeWrapper(String path, InboundSubscribeRequest request) { - this.path = path; - this.request = request; - } - - /////////////////////////////////////////////////////////////////////////// - // Methods - /////////////////////////////////////////////////////////////////////////// - - @Override - public void close() { - request.close(); - } - - @Override - public String getPath() { - return path; - } - - @Override - public Integer getRequestId() { - return request.getRequestId(); - } - - @Override - public Integer getSubscriptionId() { - return request.getSubscriptionId(); - } - - @Override - public void update(long timestamp, DSIValue value, DSStatus quality) { - request.update(timestamp, value, quality); - } - - /////////////////////////////////////////////////////////////////////////// - // Inner Classes - /////////////////////////////////////////////////////////////////////////// - -} diff --git a/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java b/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java deleted file mode 100644 index 48e6f82e..00000000 --- a/dslink-core/src/main/java/org/iot/dsa/dslink/ValueSubscriber.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.iot.dsa.dslink; - -import org.iot.dsa.dslink.responder.InboundSubscribeRequest; -import org.iot.dsa.dslink.responder.SubscriptionCloseHandler; -import org.iot.dsa.node.DSIStatus; -import org.iot.dsa.node.DSIValue; -import org.iot.dsa.node.DSInfo; -import org.iot.dsa.node.DSNode; -import org.iot.dsa.node.DSStatus; -import org.iot.dsa.node.DSString; -import org.iot.dsa.node.event.DSIEvent; -import org.iot.dsa.node.event.DSISubscriber; -import org.iot.dsa.node.event.DSTopic; -import org.iot.dsa.node.event.DSValueTopic.Event; - -/** - * Used process value subscriptions. - * - * @author Aaron Hansen - */ -class ValueSubscriber implements DSISubscriber, SubscriptionCloseHandler { - - /////////////////////////////////////////////////////////////////////////// - // Fields - /////////////////////////////////////////////////////////////////////////// - - private DSInfo info; - private DSNode node; - private InboundSubscribeRequest request; - - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// - - ValueSubscriber(RequestPath path, InboundSubscribeRequest request) { - this.request = request; - this.info = path.getInfo(); - if (info.isNode()) { - this.node = info.getNode(); - this.info = null; - onEvent(DSNode.VALUE_TOPIC, Event.NODE_CHANGED, node, info, null, node); - } else { - this.node = path.getParent(); - onEvent(DSNode.VALUE_TOPIC, Event.CHILD_CHANGED, node, info, info.getValue(), - info.getValue()); - } - node.subscribe(DSNode.VALUE_TOPIC, info, this); - } - - /////////////////////////////////////////////////////////////////////////// - // Methods - /////////////////////////////////////////////////////////////////////////// - - @Override - public void onClose(Integer subscriptionId) { - node.unsubscribe(DSNode.VALUE_TOPIC, info, this); - } - - @Override - public void onEvent(DSTopic topic, DSIEvent event, DSNode node, - DSInfo child, - Object... params) { - DSIValue value; - if (info == null) { - if (this.node instanceof DSIValue) { - value = (DSIValue) this.node; - } else { - value = DSString.valueOf(this.node.toString()); - } - } else { - if (info.isValue()) { - value = info.getValue(); - } else { - value = DSString.valueOf(info.getObject().toString()); - } - } - DSStatus quality = DSStatus.ok; - if (value instanceof DSIStatus) { - quality = ((DSIStatus) value).toStatus(); - } - request.update(System.currentTimeMillis(), value, quality); - } - - @Override - public void onUnsubscribed(DSTopic topic, DSNode node, DSInfo info) { - request.close(); - } - - -} From 0200f5ac162ef8cd6159a0ed99faaedce2fdd046 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 29 Jan 2018 09:24:08 -0800 Subject: [PATCH 3/3] Update testing link. --- build.gradle | 2 +- .../org/iot/dsa/dslink/test/MainNode.java | 234 ++++++++---------- 2 files changed, 106 insertions(+), 130 deletions(-) diff --git a/build.gradle b/build.gradle index 36d814da..b89b43f9 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'java' apply plugin: 'maven' group 'org.iot.dsa' -version '0.14.0' +version '0.15.0' sourceCompatibility = 1.6 targetCompatibility = 1.6 diff --git a/dslink-java-v2-test/src/main/java/org/iot/dsa/dslink/test/MainNode.java b/dslink-java-v2-test/src/main/java/org/iot/dsa/dslink/test/MainNode.java index fbc0c3c5..d2f918d4 100644 --- a/dslink-java-v2-test/src/main/java/org/iot/dsa/dslink/test/MainNode.java +++ b/dslink-java-v2-test/src/main/java/org/iot/dsa/dslink/test/MainNode.java @@ -3,6 +3,7 @@ import org.iot.dsa.DSRuntime; import org.iot.dsa.dslink.DSMainNode; import org.iot.dsa.node.DSBool; +import org.iot.dsa.node.DSDouble; import org.iot.dsa.node.DSElement; import org.iot.dsa.node.DSFlexEnum; import org.iot.dsa.node.DSInfo; @@ -27,6 +28,8 @@ public class MainNode extends DSMainNode implements Runnable { // Constants /////////////////////////////////////////////////////////////////////////// + private static final boolean TEST_DEFAULTS = false; + /////////////////////////////////////////////////////////////////////////// // Fields /////////////////////////////////////////////////////////////////////////// @@ -36,10 +39,6 @@ public class MainNode extends DSMainNode implements Runnable { private DSInfo test = getInfo("Test"); private DSRuntime.Timer timer; - /////////////////////////////////////////////////////////////////////////// - // Constructors - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////////// @@ -62,13 +61,6 @@ protected void declareDefaults() { declareDefault("Test", DSAction.DEFAULT); } - /** - * Handles the reset action. - * - *

- * - * {@inheritDoc} - */ @Override public ActionResult onInvoke(DSInfo actionInfo, ActionInvocation invocation) { if (actionInfo == this.reset) { @@ -97,12 +89,6 @@ protected synchronized void onSubscribed() { timer = DSRuntime.run(this, System.currentTimeMillis() + 1000l, 1000l); } - /* - protected void onStable() { - new TestRuntime(); - } - */ - /** * Cancel an active timer if there is one. */ @@ -115,52 +101,17 @@ protected void onStopped() { } /** - * Build a large database + * Action impl, build a large databases. */ private void onTest() { - long start = System.currentTimeMillis(); - clear(); - long now = System.currentTimeMillis(); - long dur = now - start; - info("********** Clear duration: " + dur); - start = System.currentTimeMillis(); - DSNode node = new TestNode(); - add("test", node).setAdmin(true); - for (int i = 0; i < 10; i++) { - DSNode iNode = new TestNode(); - node.add("test" + i, iNode); - for (int j = 0; j < 100; j++) { - DSNode jNode = new TestNode(); - iNode.add("test" + j, jNode); - for (int k = 0; k < 100; k++) { - jNode.add("test" + k, new TestNode()); - } - } - } - now = System.currentTimeMillis(); - dur = now - start; - info("********** Add duration: " + dur); - start = System.currentTimeMillis(); - getLink().save(); - now = System.currentTimeMillis(); - dur = now - start; - info("********** Save duration: " + dur); - start = System.currentTimeMillis(); - onTestIterate(node); - now = System.currentTimeMillis(); - dur = now - start; - info("********** Iterate duration: " + dur); - } - - /** - * Walk the test tree. - */ - private void onTestIterate(DSNode node) { - for (DSInfo info : node) { - if (info.isNode()) { - onTestIterate(info.getNode()); - } - } + info("Start Test: " + new java.util.Date()); + info("Test run (1/3) ..."); + test(false); + info("Test run (2/3) ..."); + test(false); + info("Test run (3/3) ..."); + test(true); + info("Test Finished: " + new java.util.Date()); } /** @@ -182,80 +133,109 @@ public void run() { put(incrementingInt, DSElement.make(value.toInt() + 1)); } - /////////////////////////////////////////////////////////////////////////// - // Inner Classes - /////////////////////////////////////////////////////////////////////////// + public void test(boolean print) { + remove("test"); + DSNode root; + long startMem = testMemUsed(); + if (print) { + info("Memory before: " + testFormat(startMem)); + } + long time = System.currentTimeMillis(); + root = testMakeNode(); + put("test", root); + for (int i = 0; i < 10; i++) { + DSNode iNode = testMakeNode(); + root.add("test" + i, iNode); + for (int j = 0; j < 100; j++) { + DSNode jNode = testMakeNode(); + iNode.add("test" + j, jNode); + for (int k = 0; k < 100; k++) { + jNode.add("test" + k, testMakeNode()); + } + } + } + time = System.currentTimeMillis() - time; + long endMem = testMemUsed(); + if (print) { + info("Memory after: " + testFormat(endMem)); + info("Memory used: " + testFormat(endMem - startMem)); + info("Create time: " + time + "ms"); + } + time = System.currentTimeMillis(); + testTraverse(root); + time = System.currentTimeMillis() - time; + endMem = testMemUsed(); + if (print) { + info("Traverse time: " + time + "ms"); + info("Memory used: " + testFormat(endMem - startMem)); + } + } - public static class TestNode extends DSNode { + private String testFormat(long mem) { + return (mem / 1000) + "KB"; + } - @Override - protected void declareDefaults() { - /* - super.declareDefaults(); - declareDefault("Incrementing Int", DSInt.valueOf(1)).setReadOnly(true); - declareDefault("Writable Boolean", DSBool.valueOf(true)); - declareDefault("Writable Enum", - DSFlexEnum.valueOf("On", - DSList.valueOf("Off", "On", "Auto", "Has Space"))); - declareDefault("Java Enum", DSJavaEnum.valueOf(MyEnum.Off)); - declareDefault("Message", DSString.EMPTY).setReadOnly(true); - DSAction action = new DSAction(); - action.addParameter("Arg", - DSJavaEnum.valueOf(MyEnum.Off), - "My action description"); - declareDefault("Reset", action); - */ + private DSNode testMakeNode() { + DSNode ret; + if (TEST_DEFAULTS) { + ret = new TestNode(); + } else { + ret = new DSNode(); + ret.put("string", "abc"); + ret.put("int", DSInt.valueOf(1000)); + ret.put("double", DSDouble.valueOf(12345.1d)); + ret.put("boolean", DSBool.valueOf(true)); } + return ret; + } - @Override - protected void onStable() { - put("Number", DSInt.valueOf(1)); - DSAction action = new DSAction(); - action.addParameter("Arg1", DSString.valueOf("ID1"), null); - action.addParameter("Arg2", DSString.valueOf("ID2"), null); - action.addParameter("Arg3", DSString.valueOf("ID3"), null); - put("action1", action); - /* - action = new DSAction(); - action.addParameter("Arg1", DSString.valueOf("ID1"), null); - action.addParameter("Arg2", DSString.valueOf("ID2"), null); - action.addParameter("Arg3", DSString.valueOf("ID3"), null); - */ - put("action2", action); - /* - action = new DSAction(); - action.addParameter("Arg1", DSString.valueOf("ID1"), null); - action.addParameter("Arg2", DSString.valueOf("ID2"), null); - action.addParameter("Arg3", DSString.valueOf("ID3"), null); - */ - put("action3", action); + private long testMemUsed() { + Runtime rt = Runtime.getRuntime(); + long mem = rt.totalMemory() - rt.freeMemory(); + long tmp; + for (int i = 2; --i >= 0; ) { + System.gc(); + tmp = rt.totalMemory() - rt.freeMemory(); + if (tmp < mem) { + mem = tmp; + } + try { + Thread.sleep(1000); + } catch (Exception x) { + x.printStackTrace(); + } + tmp = rt.totalMemory() - rt.freeMemory(); + if (tmp < mem) { + mem = tmp; + } } - + return mem; } - private class TestRuntime implements Runnable { - - long i = 0; - long last = System.currentTimeMillis(); - public TestRuntime() { - DSRuntime.run(this); + private void testTraverse(DSNode node) { + DSInfo info = node.getFirstInfo(); + while (info != null) { + if (info.isNode()) { + testTraverse(info.getNode()); + } + info = info.next(); } + } - @Override - public void run() { - long now = System.currentTimeMillis(); - if (now - last > 1000) { - System.out.println("Failure"); - System.exit(1); - } - info("Hello " + i); - if (++i > 1000) { - System.out.println("Success"); - System.exit(0); - } - last = now; - DSRuntime.runDelayed(this, 100); + /////////////////////////////////////////////////////////////////////////// + // Inner Classes + /////////////////////////////////////////////////////////////////////////// + + public static class TestNode extends DSNode { + + public void declareDefaults() { + super.declareDefaults(); + declareDefault("string", DSString.valueOf("abc")); + declareDefault("int", DSInt.valueOf(1000)); + declareDefault("double", DSDouble.valueOf(12345.1d)); + declareDefault("boolean", DSBool.valueOf(true)); + declareDefault("action", DSAction.DEFAULT); } } @@ -266,8 +246,4 @@ public enum MyEnum { Auto } - /////////////////////////////////////////////////////////////////////////// - // Initialization - /////////////////////////////////////////////////////////////////////////// - }