= Java Binding Specification 2 Tony Tkacik ; Robert Varga ; Martin Sunal ; Martin Ciglan :rfc6020: https://tools.ietf.org/html/rfc6020 :toc: :toclevels: 4 == Introduction === Terminology Namespace:: naming space, in which all identifiers needs to be unique identifiers. Data Node:: Data Tree:: Instance Identifier:: Instantiated Data Node:: Instantiated Data Tree:: Transfer Object:: Builder:: builder === Binding Specification v2 Concepts <>:: Represent node, which is instantiable by users as a part of notification, rpc, action or data tree. <>:: Represents node, which is part of instantiated data tree, this interface is not used directly, but rather via <>. See <> for more information. <>:: Represents virtual root of instantiated data tree node. <>:: Represents node, which is part of instantiated data tree and is not root of data tree. <>:: Represents instantiated node, which is subjectible to be extended / augmented by `augment` statement from external module. <>:: Represents extension to instantiated node, which is introduced from different model than instantiated node. <>:: Unique identifier of node / subtree in data tree, which provides unambiguous information, how to reference node / subtree in Instantiated Data Tree. [cols="6"] |=== .2+|Statement .2+| In groupings 3+| Instantiable .2+| Augmentable | In Data | In RPC | In Notification | `grouping` | Yes | No | No | No | No | `container` | Yes | Yes | Yes | Yes | Yes | `leaf` | Yes | Yes | Yes | Yes | No | `leaf-list` | Yes | Yes | Yes | Yes | No | `list` | Yes | Yes | Yes | Yes | Yes | `anydata` | Yes | Yes | Yes | Yes | No | `anyxml` | Yes | Yes | Yes | Yes | No | `choice` | Yes | Yes | Yes | Yes | Yes | `case` | Yes | Yes | Yes | Yes | Yes | `input` | Yes | No | Yes | No | Yes | `output` | Yes | No | Yes | No | Yes | `notification` | Yes | No | No | Yes | Yes |=== == Namespaces YANG defines several namespaces and naming space of YANG is wider then applicable namespace of JAVA language. In order to decrease conflicts between various YANG-defined namespaces and classes introduced by Binding Specification, it is needed to: * separate namespaces by Java package hierarchy ** each namespace must define rules how to construct package name, which will not conflict with other namespace * if multiple classes are generated for YANG statement they need to be in separate packages to decrease possible conflicts with siblings. * if Binding Specification introduces new concepts, which does not have explicit namespace rules in YANG, these concepts needs to be in their own, separate namespaces, in order to not conflict on valid YANG namespace items. This rules allows to identify two types of namespaces: .Namespace types by source of namespace YANG namespace:: Naming space explicitly defined in YANG specification, which needs to be explicitly supported in order to prevent naming conflicts. Binding namespace:: Naming space introduced by Binding Specification for additional properties and functionality of Binding Specification. This namespaces needs to be separate from YANG namespaces in order to not have naming conflict with YANG-derived names. Binding Specification v2 uses following namespaces: .Concrete namespaces used in Binding Specification <>:: YANG namespace containing representation for all modules. <>:: YANG namespace containing representation for all `identity` statements. Identities needs to be separated to prevent naming conflict between Grouping, Data, Type namespaces. <>:: YANG namespace containing representation for all `typedef` statements and annonymous definitions of `union`, `enumeration` and `bits` types. Types needs to be seperated to prevent naming conflict between Identity, Grouping and Data namespaces. <>:: YANG namespace containing representation for all `grouping` statements and their child data node statements. Groupings needs to be separated to prevent naming conflict between Identity, Type, Data namespaces. <>:: Binding namespace containing representation for all `key` statements. Representations of key statements needs to be in separate namespace, since it is not defined in YANG specification. <>:: YANG namespace containing representation of instantiated data tree. Data needs to be separated to prevent naming conflict between Identity, Type, Grouping namespaces. <>:: Binding namespace containing Transfer Objects and Builders representing instantiated data tree items. NOTE: Most of Binding Namespaces were introduced to decrease possibility of name conflict between concepts defined in YANG and additional concepts introduced by Binding Specification. === Package hierarchy .Package hierarchy for model [cols="1,1,4"] |=== |Namespace | Package | Description | <> | `ident` | flat package containing representation for all `identity` .3+| <> | `type` | flat package containing representations for all top-level `typedef` statements | `type.grp` | path-based package hierarchy containing representation for `typedef` statements nested in grouping statements, or anonymous types requiring code generation defined inside groupings | `type.data` | path-based package hierarchy containing representation for `typedef` statements nested in grouping statements, or anonymous types requiring code generation defined inside instantiated data nodes | <> | `key` | path-based package hierarchy containing representation of key statements for grouping code generation defined inside groupings | <> | `grp` | path-based package hierarchy containing representation for `grouping` statements and data node statements nested in these groupings | <> | `data` | path-based package hierarchy containing representation of instantiated data nodes | <> | `dto` | path-based package hierarchy containing Tranfer Objects and their builders for instantiated data nodes |=== [[module-namespace]] === Module Namespace [[identity-namespace]] === Identity Namespace [[type-namespace]] === Type Namespace [[grouping-namespace]] === Grouping Namespace [[key-namespace]] === Key Namespace [[data-namespace]] === Data Namespace [[dto-namespace]] === Builder Namespace == Generic rules [[class-naming]] === Class Naming [[subpackage-structure]] === Subpackage Naming [[accessor-rules]] === Accessor rules == Type rules === `typedef` statement ==== Globally scoped `typedef` ==== Subtree scoped `typedef` Subtree scoped `typedef` statement is type definition, which is not substatement of `module` or `submodule`, and is only visible to child elements of parent statement. * Representation is generated in Type namespace according to following rules: === Type mapping YANG types does not provide single, simple model of behaviour - some times exhibits special properties to extensibility or limiting scope of valid values when type is derived //// .Base types and their behaviours |=== | YANG Type | Description | Java Mapping | `binary` | Any binary data | `Binary`? | `bits` | A set of bits or flags | Custom class | `boolean` | `true` or `false` | `Boolean` | `decimal64` | 64-bit signed decimal number | No | `empty` | A leaf that does not have any value | No | `enumeration` | Enumerated strings | No | `identityref` | A reference to an abstract identity | Yes | `instance-identifier` | References a data tree node | Yes | `int8` | 8-bit signed integer | No | `int16` | 16-bit signed integer | No | `int32` | 32-bit signed integer | No | `int64` | 64-bit signed integer | No | `leafref` | A reference to a leaf instance | Maybe | `string` | Human-readable string | No | `uint8` | 8-bit unsigned integer | No | `uint16` | 16-bit unsigned integer | No | `uint32` | 32-bit unsigned integer | No | `uint64` | 64-bit unsigned integer | No | `union` | Choice of member types | Maybe |=== FIXME: Finalize table //// ==== `boolean` type ==== `empty` type ==== `int8` type ==== `int16` type ==== `int32` type ==== `int64` type ==== `uint8` type ==== `uint16` type ==== `uint32` type ==== `uint64` type ==== `string` type ==== `binary` type ==== `enumeration` type ==== `bits` type ==== `identityref` type ==== `instance-identifier` type ==== `leafref` type ==== `union` type [[data-node-rules]] == Data Node rules Data nodes could be separated into two distinct groups, based on presence of child nodes: Leaf node:: Node, which according to YANG schema does not have child nodes, is leaf node and carries only simple value. Interior node:: Node, which according to YANG schema may have child nodes, node itself does not carry values, values are stored in descendant leaf nodes. === `leaf` Statement === `leaf-list` Statement === `container` Statement === `list` Statement ==== Keyed List ==== List without Key === `choice` Statement === `case` Statement [source,yang] ---- container top { choice base { case foo { container foo; } case bar { leaf bar { type string; } } } } ---- [plantuml] .... set namespaceSeparator none package spec { interface Choice interface Case } interface data.Top { + getBase() : data.top.Base; } interface data.top.Base interface data.top.base.Foo { + getFoo() : data.top.base.foo.Foo } interface data.top.base.foo.Foo interface data.top.base.Bar { + getBar() : String } data.top.Base -u-|> Choice data.top.base.Foo -u-|> Case data.top.base.Bar -u-|> Case data.top.base.Foo -u-|> data.top.Base data.top.base.Bar -u-|> data.top.base.Foo data.Top o- data.top.Base data.top.base.Foo o- data.top.base.foo.Foo .... == Specific rules [[instantiated-data-node-rules]] === Instantiated Data Node Rules //// FIXME: Do we need section per type, or should just general rules be described. //// ==== `container` Statement //// FIXME: Here should be Augmentable & Instantiated //// ==== `leaf` Statement ==== `leaf-list` Statement ==== `choice` Statement ==== `case` Statement //// FIXME: Here should be Augmentable & Instantiated //// ==== `list` Statement //// FIXME: Here should be Augmentable & Instantiated, List signature uses concrete interfaces //// ==== `input` Statement //// FIXME: Here should be Augmentable & Instantiated //// ==== `output` Statement //// FIXME: Here should be Augmentable & Instantiated //// ==== `notification` Statement //// FIXME: Here should be Augmentable & Instantiated //// [[instantiated-data-tree-rules]] === Instantiated Data Tree Rules ==== `container` Statement //// FIXME: Here should be Augmentable & Instantied & ChildDataNode //// ==== `leaf` Statement ==== `leaf-list` Statement ==== `case` Statement //// FIXME: Here should be Augmentable & Instantied & ChildDataNode //// ==== `list` Statement //// FIXME: Here should be Augmentable & Instantied & ChildDataNode //// === `grouping` Statement * `grouping` statement is represented by `interface` ** class name is generated according to <> with suffix `Grouping` * Representations of `grouping` statements are generated into <> * data schema nodes under grouping are represented by `interface` and are generated into <> ** getters (accessors) from parent nodes are generated according to <> ** class name is generated according to <> with suffix `Data` ** data schema nodes does not follow <>, these interfaces are used only in instantiated data tree. .Simple Grouping ==== .YANG Snippet [source, yang] ---- grouping simple { <1> container foo; <2> leaf bar { type string;} <3> } ---- <1> Is represented by interface `grp.SimpleGrouping` <2> Is represented by interface `grp.simple.FooData` and getter in `grp.SimpleGrouping` with signature `public grp.simple.FooData getFoo();` <3> Is represented by getter in `grp.SimpleGrouping` with signature `public String getBar()` [plantuml] .... interface grp.SimpleGrouping { + getBar() : String + getFoo() : grp.simple.FooData } interface grp.simple.FooData grp.SimpleGrouping o- grp.simple.FooData .... ==== ==== Data Node substatements Representations of data node substatements are generated according to rules described in <> with following changes: * Interface names for `case`, `choice`, `container` and `list`, is suffixed by `Data` suffix, in order to not conflict with same named groupings inside same package ** Getters in parent node, are still generated without `Data` suffix, so the getter signature is in form `FooData getFoo()` ** If return value of getter is constructed using generics (eg. `list`) instead of signature `List` or `Map`, wildcarded `? extends ListItem` generic argument are used to allow for overriding during <>. ==== `list` substatement //// FIXME: Add reasoning / examples for need to use ? extends, instead of directly using generics. //// ==== `leaf-list` susbstatement //// FIXME: Add reasoning / examples for need to use ? extends, instead of directly using generics for types, which may need instantiation //// [[uses-statement]] === `uses` Statement * `uses` statement triggers interface of parent statement to extend (implement) interface of `grouping` referenced by `uses` argument. * As in YANG `uses` statement triggers instatiation of data children of `grouping` which will result in generation of these children as-if they were direct children of parent statement ** data node children are generated according to rules defined for parent statement. Different rules apply based on parent type (instantiated data tree, `input`, `output` or `grouping`) ** interfaces generated for data children extends (implements) interfaces for same children generated for referenced `grouping` .Simple Grouping and Uses ==== .YANG Snippet [source, yang] ---- grouping simple { container foo; leaf bar { type string;} } container top { uses simple; } ---- [plantuml] .... set namespaceSeparator none interface grp.SimpleGrouping { + getBar() : String + getFoo() : grp.simple.FooData } interface grp.simple.FooData interface data.Top { + getFoo() : data.top.Foo } interface data.top.Foo grp.SimpleGrouping o-- grp.simple.FooData data.Top o-- data.top.Foo data.Top -|> grp.SimpleGrouping data.top.Foo -|> grp.simple.FooData .... NOTE: Diagram does not show all details for `data.Top` and `data.top.Foo`, which are based on <> ==== .Grouping with Nested Grouping ==== .YANG Snippet [source, yang] ---- set namespaceSeparator none grouping with-inner { grouping inner { container cont; } uses inner; } container top { uses with-inner; } ---- [plantuml] .... set namespaceSeparator none interface grp.withinner.inner.ContData interface grp.withinner.InnerGrouping { + getCont() : grp.withinner.inner.ContData } interface grp.withinner.ContData interface grp.WithInnerGrouping { + getCont() : grp.withinner.ContData } interface data.Top { + getCont() : data.top.Cont } interface data.top.Cont { } data.Top o-- data.top.Cont : contains data.Top -|> grp.WithInnerGrouping data.top.Cont -|> grp.withinner.ContData grp.WithInnerGrouping -|> grp.withinner.InnerGrouping : uses (implements) grp.WithInnerGrouping o-- grp.withinner.ContData : contains grp.withinner.InnerGrouping o-- grp.withinner.inner.ContData : contains grp.withinner.ContData -|> grp.withinner.inner.ContData : is concretization of (implements) .... NOTE: Diagram does not show all details for `data.Top` and `data.top.Cont`, which are based on <> ==== [[uses-augment]] ==== `augment` substatement .Uses & Augment in instantiated Data Tree ==== [source,yang] ---- grouping example { container nested { leaf foo { type string; } } } container top { uses example { augment nested { container bar { } } } } ---- [plantuml] .... set namespaceSeparator none interface data.Top interface data.top.Nested interface data.top.nested.Bar data.Top o-- data.top.Nested data.top.Nested o-- data.top.nested.Bar interface grp.ExampleGrouping interface grp.example.NestedData grp.ExampleGrouping o-- grp.example.NestedData data.Top -|> grp.ExampleGrouping data.top.Nested -|> grp.example.NestedData .... NOTE: Diagram does not show all details for `data.Top`, `data.top.Nested` and `data.top.nested.Bar`, which are based on <> ==== .Uses & Augment in grouping ==== [source,yang] ---- grouping example { container nested { leaf foo { type string; } } } grouping top { uses example { augment nested { container bar { } } } } ---- [plantuml] .... set namespaceSeparator none interface grp.TopGrouping interface grp.top.NestedData interface grp.top.nested.BarData grp.TopGrouping o-- grp.top.NestedData grp.top.NestedData o-- grp.top.nested.BarData interface grp.ExampleGrouping interface grp.example.NestedData grp.ExampleGrouping o-- grp.example.NestedData grp.TopGrouping -|> grp.ExampleGrouping grp.top.NestedData -|> grp.example.NestedData .... ==== === `augment` statement Representation of `augment` statement depends on module in which target node of augment statement is defined * <> - data nodes are represented as-if their statements were inlined in target node. See <> section for details. * <> - data nodes are represented as-if their statements were inlined in target. See <> for details & examples. * <> - interface representing augmentation is generated, child data nodes are generated by rules for <>. See <> for details & examples. `augment` statement targets only instantiated data nodes, so child data node representation are always ge [[augment-same-module]] ==== Augmentation target in same module All data node children are generated as-if they were directly defined inside target node. There are no externally observable artefacts in generated representation of these nodes, which would point out that they were defined using `augment` statement instead of directly inlining them in target node. .Why augment of same module is same as inlining [IMPORTANT] ==== This rule may seems counterintuitive at first sight, but YANG defines backwards compatibility in terms of effective model instead of way how model is represented. `augment` statement, when targeting node in same module is not externally observable and could factored out by inlining these statements. Definition of `augment` statement in YANG also defines different behaviour when target is same module and allows all features as-if this statements were directly inlined. ==== .Augment with target in same module ==== .YANG module written using augmentations [source,yang] ---- container top { } augment "/top" { container foo { } } ---- .Same module written without need to augment ---- container top { container foo { } } .Java representation for both variants ---- [plantuml] .... set namespaceSeparator none interface data.Top interface data.top.Foo data.Top o- data.top.Foo .... ==== [[augment-other-module]] ==== Augmentation target in other module .Augment with target in other module ==== [source,yang] ---- module top { ... container top { } } module foo { ... import top { prefix top; } ... augment "/top:top" { container bar { } } } ---- [plantuml] .... set namespaceSeparator none interface Augmentable interface Augmentation interface top.data.Top interface foo.data.FooTop { + getBar() : Bar } interface foo.data.top.Bar top.data.Top -u-|> Augmentable : T = top.data.Top foo.data.FooTop -u-|> Augmentation : T = top.data.Top top.data.Top o-- foo.data.FooTop foo.data.FooTop o-- foo.data.top.Bar .... ==== .Multiple augments with same target ==== [source,yang] ---- module top { ... container top { } } module foo { ... import top { prefix top; } ... augment "/top:top" { container bar { } } augment "/top:top" { container baz { } } } ---- [plantuml] .... set namespaceSeparator none interface Augmentable interface Augmentation interface top.data.Top interface foo.data.FooTop { + getBar() : Bar + getBaz() : Baz } interface foo.data.top.Bar interface foo.data.top.Baz top.data.Top -u-|> Augmentable : T = top.data.Top foo.data.FooTop -u-|> Augmentation : T = top.data.Top top.data.Top o-- foo.data.FooTop foo.data.FooTop o-- foo.data.top.Bar foo.data.FooTop o-- foo.data.top.Baz .... ==== .Multiple augments with different targets ==== [source,yang] ---- module target { ... container first { } container second { } } module foo { ... import target { prefix t; } ... augment "/t:first" { container bar { } } augment "/t:second" { container baz { } } } ---- [plantuml] .... set namespaceSeparator none interface Augmentable interface Augmentation interface target.data.First interface target.data.Second interface foo.data.FooFirst { + getBar() : Bar } interface foo.data.FooSecond { + getBaz() : Baz } interface foo.data.first.Bar interface foo.data.second.Baz target.data.First -u-|> Augmentable : T = target.data.First target.data.Second -u-|> Augmentable : T = target.data.Second foo.data.FooFirst -u-|> Augmentation : T = target.data.First foo.data.FooSecond -u-|> Augmentation : T = target.data.Second target.data.First o-- foo.data.FooFirst target.data.Second o-- foo.data.FooSecond foo.data.FooFirst o-- foo.data.first.Bar foo.data.FooSecond o-- foo.data.second.Baz .... ====