The Talon Manual

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

A goal of the platform is to make working with messages and state no more of a burden on developers than simply working with plain old java objects. The X Platform's Application Data Modeler (ADM) allows modeling of messages and entities in XML and provides utilities to generate source code that is instrumented to transparently handle encoding, serialization and transactional functionality that underpin the platform. The model allows allows the definition of 3 three types of complex or composite types: Entities, Embedded Entities and Messages. These will be discussed in a more detail below, but at . Here is a high level here is how to think about definition of the three:.

  • Entity: Entities are used to model application state. In Talon, an application exposes its state to the platform by providing it with a single root object that references the rest of the application state. The single root forms an object graph that is held fully in memory, supports transactional semantics, and is replicated asynchronously to backup instances in a delta based pipelined fashion as application code modifies it in the course of processing. 
  • Message: A message can be thought of as a special type of entity that is enhanced to allow transport over the platform's Simple Message Abstract (SMA) layer. A message can't reference other entities because it is serializezd serialized as an independently transportable unit. 
  • Anchor
    EmbeddedEntity
    EmbeddedEntity
    Embedded Entity: An embedded entity models a collection of fields of primitive types or other embedded entities. An embedded entity can be embedded into a Message, Entity or other Embedded entity as a field, and because become part of that type's serializable unit (, e.g. not another node from the standpoint of ROG, and a transported part of a message from the standpoint of SMA. 

As a convenience, there is also:

  • Inlined Entity: An embedded entity can be "inlined" into another type such that it inherits the embedded entity's fields.  

...

The X Platform supports the following built-in types which can be used as fields when building composite entity types:

...


Collections and Arrays

The following built-in collection types are supported. 

...

  • Array types are limited to primitive, built-in, and embedded entity types. They are intended to allow embedded values that are transported along with a message or entity, and consequently consequently:
  • Queue, Set and Map types can only be used with non-embedded entities and may only use other non embedded entities as their values.
Tip

Thinks of arrays as being for use used with messages and embedded entities.

Collections are for use used in referencing non-embedded embedded entities elsewhere in an application's state graph.

...

The X Platform supports modeling of enumeration types. Enumeration types can be of type int, char or string and, according to their type, they must be assigned a code value that is used to generate their field tag for for protobuf/xbuf encoding:

  • For integer coded enums, the value is the same as the (integer) code, 
  • For character coded enums, the value is the ASCII value of the (character) code, and
  • For string coded enums, the value is the hash code of the (string) code.

...

An entity that serves as the root of an object graph can be marked as root, which indicates it will not have any parents in the object tree. A root object should not be declared as the child of another object. Application provided state should not be defined as root objects as because the server appends them to an underlying root object internally. 

An entity type can be marked as transactional. The platform tracks field updates to transactional entities and can rollback changes made to them in the event of an application exception or an explicit rollback. By default, entities and collections are transactional while messages are not. A message can be declared transactional, which is useful in the event that a message is stored as part of an application's state graph. 

See State Graph Limitations for some important limitations on the current state graph implementation that may have an impact on state modeling.  

...

...

The X Platform generates source code for entities and messages for use with the runtime from an xml model specified by the x-adml.xsd schema included at the root of the nvx-adm-<version>.jar and also bundle bundled into the nvx-all.jar. Be sure to update your editor's schema validator to reference it.

...

The root element of a message model is the model element, which is used to define a namespace qualified set of modeling elements. To conform with x-adml.xsd schema and pass validation, a model must define target XML namespace xmlns="http://www.neeveresearch.com/schema/x-adml". This is not to be confused with namespace attribute described below.

...

AttributeDescriptionRequired
nameThe Model name can now be specified in the model element itself instead of being supplied externally. If the name contains spaces, then in cases where it used as type name it will be converted to camel case with no spaces (for example "Trading model" would become "TradingModel"No
namespaceThe model's name space. Model elements use the model name space as the package name when generating other models, and model importing another model refer to imported model elements using the imported model's namespace.Yes
defaultFactoryIdThe default factory id to be used on model elements that require a factoryId but don't specify one. If the model doesn't define a factory element with a matching id, one is created implicitly by camel casing the model name and appending 'Factory', e.g. TradingFactory.No
docA brief one line description of the model.No

...

  • Name of .proto file when IDL is generated.
  • Name of outer java class that wraps model types  when when code is generated with Protobuf encoding.

...

For the example above, name property will have value of ModelNameGoesHere (it will be converted to Pascal Notation). If we were do not to define name attribute, the model name would be derived from filename: sampleModel.xml -> SampleModel.

Note
titleNaming the model

Conversion of user defined name to internally used ADM name can only handle white space as word separator. Since name will be used to declare a Java class, developer must take care with model name to only use white space and characters that are allowed in class name. 
If we named the model sample-model, the resulting ADM name would be Sample-model, which cannot be used as a Java class name because it contains a dash character. Generating code with Protobuf encoding would then cause Java compilation errors. This applies both to name defined through XML attribute and filename-derived ones.

...

All methods related to a deprecated field are marked as deprecated, the . The code snippet below shows some examples of this. 

...

The factories section defines object factories that are used to instantiate the generated object. Each factory in an application must have a unique factory id. The ID is serialized along with object or transported in MessageMetadata and is used to reconstitute its objects during deserialization. A single model can define multiple factories, allowing Messages and Entities to be grouped together as the application sees fit. A factory can contain a maximum of 32767 types, so in practice it is rarely a requirement to use multiple factories within a single model. Each type in the model defines the factory to which it belongs via its factoryid attribute. User application may define factories with ids greater than or equal to 1, ids <= 0 are reserved for platform internal use. 

...

AttributeDescriptionRequired
nameThe factory name to be used for code generation.No
idThe factory's id which is used to register the factory with the runtime and identify the factory to use when decoding the factory's serialized objects.Yes
docA brief one line description for the factory. If more detailed documentation is needed, a child <documentation> element can be used. 

...

Enumeration types can be modeled as follows and can be declared as fields of entity or message types. Unlike other model elements, enumerations aren't tied to a particular factory despite being scoped to the model's name space. Generated java enumeration don't have dependencies on the rest of the platform and can be used standalone. 

...

Generated Enumerations can specify a type which is accessible in the generated code. The type can be int, char or String. A const should not be removed from an enumeraionenumeration, if the newly generated code is expected to deserialize enums that were persisted with an earlier version, in addition the order of constants is important and should not be changed.

...

For an entity named "MyEntity" an interface and an implementation are generated using the model's namespace. Entities will extend IRogNode or IRogContainerNode marking so that they can be used as nodes with the platform's Replicated Object Graph (ROG) framework. 

...

The ADM model allows fields either to be defined in place (directly on the message or entity that is using the field) or by reference to a field declared in the model's <fields> section. It is a matter of preference which approach an application developer uses, : if many messages contain the same field, then it may be more convenient to model the fields in a reusable fashion in the <fields> element, but in most cases it is more convenient to define the fields in place. 

...

AttributeDescriptionRequired
type

The type of the element. If the type is defined in this namespace or is a primitive or collection type, only the simple name of the type need be used. If the type belongs to another namespace from an imported file, then the fully qualified name should be used. If the field is an arraytype, it should be suffixed with array indeces indices such as MyEntity[] to denote it as an array.

(tick)
nameThe name of the field, must be unique within the model.(tick)
jsonName

Contains the name of the JSON property that will be used for the field when the message is serialized to JSON. Defaults to use the value defined in name.

 
idThe id of the field. The id must be unique with the scope of the containing type. For Xbuf/Protobuf encoding this tag is used as the tag value for the field on the wire. If not set, a unique id will be generated by the source code generator. For better control over compatibility it is reccomended that the application set this value manually. 
length

If this field refers to a variable length type (such as string), this indicates the maximum length of the field.

 
docA brief, one line string describing the field. If more detailed documentation is desired, a child <documentation> element may be used. 
Note
titleQualifying conflicting type names

It is not possible to define two types with the same name in a given model. One exception to this rule is that it is possible (though strongly discouraged) to define a type with the same name as a built-in type. In this case, using an unqualified type reference will favor the built-in type. In this case, it is possible to reference the local type by using the this keyword, or the fully qualified name:

Code Block
xml
xml
<model xmlns="http://www.neeveresearch.com/schema/x-adml" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance"
       name="Trading" 
       namespace="com.mycompany.trading" 
       defaultFactoryId="100">
       doc="Contains Messages used by the Trading Application">
 
  <import model="../orderprocessing.xml"/>
 
  <!--An unfortunately named entity:-->
  <entity name="Currency" id="1">
    <field name="currencyCode" type="String"/>
  </entity>
 
  <entity name="MyEntity" id="2">
    <!-- 
      Without qualifying the Currency type, the built 
      in Currency type is used, and the generated entity
      will return a java.util.Currency.
    -->
    <field name="javaCurrency" type="Currency" />
    <!-- 
      The 'this' keyword indicates that this is the Currency
      entity defined in this model:
    -->
    <field name="currencyEntity" type="this.Currency"/>
    <!-- 
      Namespace qualifying the typeindicates that this is the Currency
      entity defined in this model:
    -->
    <field name="currencyEntity2" type="com.mycompany.trading.Currency"/>
    <!-- 
      Namespace qualifying the type indicates that this is the Currency
      entity defined in some imported model:
    -->
    <field name="currencyEntity3" type="com.mycompany.orderprocessing.Currency"/>
  </entity>
</model>

...

AttributeDescriptionRequired
refThe name of the referenced field. Name may be with or without the namespace of owner model (i.e. full-qualified name). In both cases, field will be looked up in both current model and its imports. If non-fully-qualified name is declared in current model and in one of imports, the field from current model will take precedence. If multiple imports define the same name, an error will be reported. Best correction in such case is to use fully qualified name.(tick)
nameOptionally, a name for the field that overrides the referenced field's name. Otherwise the field name defaults to that of the referenced field. 
jsonNameOptionally overrides the referenced field's jsonName. 
idOptionally overrides the id specified by the field being referenced. 
requiredWhether or not the field is a required field for the type. Fields declared as required will cause a check to be added for the value being set in the generated types isValid() method. 
pinned

Indicates whether or not the field is pinned. A pinned field always occupies the same space on the wire.

 
isKey

True if this field should serve as the key when inserting the entity into a LongMap or StringMap collection. In this case, insertion of the message in the collection with a given key will update this field with the key value.

 
docThe doc to use for the field which overrides that specified by the referenced field. 

...

For UUID and XString types, additional zero garbage accessors to the underlying raw type which is backed by the message buffer are additionally generated (though only zero garbage when used in conjunction with Xbuf with pooling enabled). For these types, some additional accessors are also generated to allow copying the value into and out of the message in an efficient zero garbage fashion:

...

The setXXXFrom / getXXXTo accessors allow zero garbage copying of the field value when Xbuf encoding is used for the message. These copying operations are zero garbage and can be very efficient because the copy operation can be done directly using the preserialized bytes for the type. 

Unsafe Accessor

The unsafe accessor allows the user to get a direct reference to the field with no copy - ; the returned type directly references the underlying raw bytes in the serialized message. This accessor is useful in cases where the the caller will itself copy the value into an outbound message using a copying setter, or if where the caller will use the source type purely within the handler of the message from which it takes the reference. The caller is not permitted to retain the object beyond the source object's lifespan and may not in any way modify the returned type.  

This operation can be dangerous in the presence of object pooling because, if the application erroneously retains a reference to the type, its value will be invalidated when the message is returned to its pool and its backing buffer will be wiped or repurposed. Applications that use this api should operate with special care and should avoid using this accessor unless performance is of the utmost concern. 

...

When xbuf encoding is used for an array type and pooling is enabled, the underlying implementation doesn't store the field value in an array as because pooling varying array sizes is impractical. To achieve zero garbage for fields modeled as arrays, a List interface is more appropriate, and an XIndexedList is returned. An XIndexedList implements java.util.List, but also provides reusable iterators. These accessors are discussed in detail in the knowledge base article at http://docs.neeveresearch.com/display/KB/Zero+Garbage+Array+Accessors

...

To avoid garbage for embedded messages entities when using Xbuf encoding, while still provided providing a mechanism for applications to hold such fields in state beyond the lifespan of the message from whence they came, additional take/lend accessors are generated for embedded entity fields. Considerations for using embedded entity fields in the context of message pooling are discussed in detail in the knowledge base article at: http://docs.neeveresearch.com/display/KB/Zero+Garbage+Nested+Entities

...

When a field has not been set on a type, the following table indicates what the getXXX() accessor will return:

Type
Unset Field Return Value
hasXXX
Notes
Java PrimitiveDefault value for java primitive type(tick)For numeric values 0, for boolean false, for char ''.
String, Date,null(tick) 
Enumnull(tick) 
Entitynull(tick) 
ArrayEmpty Array

(error)

Depends on code gen configuration property generateArrayGetterEmptyIfNull

For java primitive types, the hasXXX() method can be called to distinguish between whether the value was explicitly set or is just returning the default value.

For arrays it is not possible to distinguish between an empty array or the array not being set. This allows ADM code to operate with protobuf encoding under the covers, which is not able to distinguish between an empty or unset array. For this reason, ADM generated code does not generate a hasXXX method for arrays. Setting the code generation property generateArrayGetterEmptyIfNull controls whether an empty or null array is returned when there are no values encoded in the underlying message or entity. 

...

Note that "Price" doesn't result in a new java type being created. The generated source uses it's its base type (float) directly and inherits the documentation of the semantic type. This is true of all semantic types, with the exception of types with a base of String that are declared as poolable (See poolable string types below).

...

The X Platform provides an XString implementation that allows zero garbage manipulation of Strings. Low latency applications should use XStrings instead of Java Strings in domain state  This allows for zero garbage copy of fields to and from outbound and inbound messages and zero garbage manipulation of fields by domain logic. Although the use of XStrings will eliminate garbage, it will not eliminate object promotion across heap generations which can be an expensive operation. To avoid promotions, low latency applications also need the ability to pool and pre-allocate such fields. To make working with such types easier, the ADM model provides support for creating sub types of XString that are poolable. For String fields that are declared as poolable, the ADM code generator will generate code implementing a new type for the field that is a subtype of XString and the associated classes that allow for the pooling and pre-allocation of fields of this type. All of the methods currently available on XStrings will be available to the subtype. The XString documentation can be found at: http://build.neeveresearch.com/core/javadoc/LATEST/com/neeve/lang/XString.html

...

  • Creating a corresponding pooled string factory that is configured for preallocation, and using that to source strings stored in state at runtime that are intialized initialized by copying a value out of a message. It is important that string fields that are held in application state are copied out of the message because if the message itself is pooled, the value of the string will be reset after the message handler returns. 

    Code Block
    MyPooledString.Factory myStringFactory;
     
    @EventHandler
    public void onMessage(MyMessage inboundMessage, MyApplicationState state) {
      //Get the value of a string from a message by copying it to a value sourced from the factory:
      MyPooledString myStateString = inboundMessage.getMyPooledStringTo(myStringFactory);
     
      //Copy the value into an outbound message:
      MyOutboundMessage outboundMessage = MyOutboundMessage.create();
      outboundMessage.setMyPooledStringFrom(myStateString); 
      
      // retain the value copied in application state
      
      state.setMyPooledString(myStateString);
    }
  • Creating a working variable to temporarily hold a mutable pooled string value.

    Code Block
    ComplianceId complianceIdTemp = ComplianceId.create(true);
     
    @EventHandler
    public void onMessage(MyMessage inboundMessage) {
      //Copy the value from the message. 
      //Because the handler is single threaded it 
      //it is safe to overwrite the value on each invocation,
      //if it isn't retained in application state. 
      inboundMessage.getComplianceIdTo(complianceIdTemp );
      
      //Copy the value into an outbound message:
      //Because this is copied overwriting myString on the next
      //handler invocation is okay. 
      MyOutboundMessage outboundMessage = MyOutboundMessage.create();
      outboundMessage.setComplianceIdFrom(complianceIdTemp);
      ... 
    }

    Note that this pattern doesn't actually leverage the pooling capabilities of the MyPooledString type, as it retains the reference reference.

Declaring a String field to be poolable

...

  • The field or type element that defines it has a poolable="true" attribute. The String type will be created based on the name of the field. 
  • A semantic type is defined with a base type of string and the poolable attribute is set to true. The String type will be based on the name of the type. 
  • For applications that would rather separate the notion of pooling from the model itself, The the Code Generator can be run with a directive indicating that all String fields should generate a poolable type (see Directives in the sections below).
  • In the future, different narrower directives may be introduced for finer grained control over what String fields will create poolable types.

...

As stated above, a poolable string type's name may not clash with built-in platform types, other types declared in the model or types in java.lang. For pooled string types that are generated from a field name (rather than a semantic type definition), the ADM code generator supports two optional directives that can make such collisions less likely: pooledStringFieldTypeNameSuffixPolicy, which specifies the policy for resolving conflicts detected by the code generator, and pooledStringFieldTypeNameSuffix to allow the developer to specify a suffix that should be applied to generated pooled types (defaults to 'String' if not specified). 

  • None: (Default) With this policy, field names are not suffixed, and it will result in a either a code generation or compilation error if a conflict occurs. This policy is useful where the model developer want to proactively monitor for such conflicts, and perhaps declare the pooled string field as a semantic type in the <types> section to resolve the conflict.  
  • Always: With this policy, the pooled string type is always suffixed with String, which greatly reduces the likely hood of a name conflict. 
  • OnConflict: with With this policy, the code generator will suffix the generated pooled string type name with 'String' when the name conflicts with another type. For example:

    Code Block
    <enumerations>
      <enum name="MyEnum">
         <const name="Value1" value="1"/>
      </enum>
    </enumerations>
    
    <fields>
       <field name="MyEnum" type="String" poolable="true"/>
    </fields>

    would yield a pooled string type of MyEnumString to avoid conflicting with the MyEnum enumeration type.

Note

Note that usage of OnConflict is unsafe for models that may evolve over time in a fashion that could introduce a field conflict, since it would mean that at such time as a conflict arises, the type name of the pooled string would then be changed and result in compile errors.

...

Note that for pooled strings, if the length parameter is not specified, the backing buffer size for preallocated Strings will default to 32 bytes. If length is specified, then that size will be used for the default backing buffer size value for preallocating Strings of that type.

Code Block
xml
xml
<factories>
	<factory name="MyFactory" id="1"/>
</factories>
  
<messages>
  <message name="Message" factoryid="1" id="1">
    <field name="complianceId" type="String" length="32" poolable="true">
	<documentation>
		A Compliance id is a 32 character String that uniquely 
		identifies a message for compliance purposes.
      </documentation>
    </field>
  </message>
<messages>

...

From the standpoint of a message or entity, a pooled string field is treated no differently than a normal String field. The field's setters accept an XString and the getters return an XString. The field value in the message is not itself pooled, rather the value is pooled along with the message. In other words, a pooled string type allows pooling of strings in application code, not message code. The code generator will generate a subclass of XString with an inner class factory type that can be used for preallocating XStrings for use by the application:

...

  • The application can create factories for the generated types to allow application preallocation of the pooled type. 
  • The generated factory returns unitialized XStrings with the option of creating immutable (constant) instances. 
  • A newly created object will start with an ownership count of 1. The ownership count is used to track an object’s reference count. Users can acquire a pooled object to prevent it from being disposed and cleared. Calling dispose on the type will return it to the pool when the ownership count reaches 0. If the objects returned by the pool will be acquired / disposed across multiple threads, the factory should be created as threaded. 

...

Strings from inbound message fields are often stored in domain state. To that end, poolable string types need to be preallocated along with the rest of application to avoid creating new instances at run time. There are two approaches to handling preallocation:

  1. Preallocate the pooled string type with preallocated application state, and copy the string from the inbound message into domain state. In this approach, the message keeps it's its reference to the pooled string field and reuses the same reference when the message is recycled through its pool. 
  2. Use take/lend semantics in the same fashion as with a Nested Entity as described in Xbuf and Embedded Entities. The domain state needn't preallocate the pooled string type, but rather takes if from the message which then draws a new instance from a globally configured preallocated when the message itself is recycled to its pool.  

...

Using the static creation methods on a generated pooled type will draw a new instances from the generated type's default factory. The default factory is created in the pooled types static initializer block. So Therefore, any configuration of the default factory needs to be done before the class is loaded for the values to take effect. Generated Messages draw their values from the default pool. Because messages are themselves pooled, it is not necessarily required that the factory's pool use preallocation to avoid allocating new pooled types at runtime : if the application preallocates the strings and copies values out of the message. 

...

  Default
<qualifiedClassName>.initialLengthThe initial length of the backing buffer to allocate to back the string in serialized form.taken from length attribute if specified in the model, otherwise 1
<qualifiedClassName>.isNativeWhether or not a native backing buffer should be used. This is recommend for performance sensitive apps.true
<qualifiedClassName>.pool.enabledWhether the factory is backed by a pool or if new instances are created on demand.true
<qualifiedClassName>.pool.threadedWhether or not the backing pool is thread safe. The pool must be thread safe if more than one thread will create strings from this factory, or crucially, if a separate thread may return the item to the pool. For the default pool this value should almost always be true, because the thread creating the string and the one that ultimately disposed it is are usually different.true
<qualifiedClassName>.pool.preallocateCountThe number of objects to preallocate, or 0 if no preallocation should be done. Note , that preallocation is not done until the class is loaded.0

...

If the application will handle allocating Strings used in application state up front, and will copy values from messages into application state, then the application should create a separate Factory that preallocates the expected number of types. This can be done with the Factory's newFactory(...) method:

...

Depending on the application, it is possible that the Factory need not be threaded if only one thread will be creating Strings from this factory and will never dispose the strings it creates. If this is the case, then there is no risk that another thread will return the string to the pool and introduce concurrency issues.  

...

It is also possible to create pooled types in a more generic fashion, as illustrated below. This approach can be more convenient in cases where application code dynamically pre-allocates objects at runtime. 

...

Entities are defined in the entities element of the model and must have a unique id with respect to other types within the scope of their factory factory.

XML

Code Block
xml
xml
<entities>
  <entity name="EntityA" factoryid="1" id="1">
    <field name="enumField" type="Enumeration" doc="tests an enum field" id="1"/>
    <field name="enumArrayField" type="Enumeration[]" doc="tests an enum array field." id="2"/>
    <field name="intEnumField" type="IntEnumeration" id="3"/>
    <field name="intEnumArrayField" type="IntEnumeration[]" id="4"/>
    <field name="charEnumField" type="CharEnumeration" id="5"/>
    <field name="charEnumArrayField" type="CharEnumeration[]" id="6"/>
    <field name="stringEnumField" type="StringEnumeration" id="7"/>
    <field name="stringEnumArrayField" type="StringEnumeration[]" id="8"/>
    <field name="booleanField" type="Boolean" id="9"/>
    <field name="booleanArrayField" type="Boolean[]" id="10"/>
    <field name="byteField" type="Byte" id="11"/>
    <field name="byteArrayField" type="Byte[]" id="12"/>
    <field name="shortField" type="Short" id="13"/>
    <field name="shortArrayField" type="Short[]" id="14"/>
    <field name="intField" type="Integer" id="15"/>
    <field name="intArrayField" type="Integer[]" id="16"/>
    <field name="longField" type="Long" id="17"/>
    <field name="longArrayField" type="Long[]" id="18"/>
    <field name="floatField" type="Float" id="19"/>
    <field name="floatArrayField" type="Float[]" id="20"/>
    <field name="doubleField" type="Double" id="21"/>
    <field name="doubleArrayField" type="Double[]" id="22"/>
    <field name="stringField" type="String" id="23"/>
    <field name="stringArrayField" type="String[]" id="24"/>
    <field name="dateField" type="Date" id="25"/>
    <field name="dateArrayField" type="Date[]" id="26"/>
    <field name="charField" type="Char" id="27"/>
    <field name="charArrayField" type="Char[]" id="28"/>
    <field name="currencyField" type="Currency" id="29"/>
    <field name="currencyArrayField" type="Currency[]" id="30"/>
    <field name="embeddedEntityField" type="EmbeddedEntity" id="32"/>
    <field name="embeddedEntityArrayField" type="EmbeddedEntity[]" id="33"/>
    <field name="entityField" type="EntityB" id="34"/>
    <field name="entityBMapField" type="EntityBLongMap" id="35"/>
  </entity>
  
  </entity name="EntityB" factoryid="1" id="2">
    <field name="intField", type="Integer" id="1"/>
  <entity>
  
  </entity name="EmbeddedEntity" factoryid="1" id="3" asasEmbedded="true">
	<field name="longField", type="Long" id="1"/>
  <entity>
   
</entities>

...

AttributeDescriptionRequired
name

The name of the field, must be unique within the model.

(tick)
idThe id of the entity, which must be unique within the scope of all types in the entity's factory.(tick)
factoryIdThe id of the factory, which must be unique within the scope of all factories used within an application. When a message is received, the factoryId and entityId uniquely identify the type to be deserialized. factory ids <= 0 are reserved for platform use.(tick)
asEmbeddedDefaults to false, Indicates whether or not this entity is generated to be used as an embedded or child field of another entity. Embedded entities are always serialized transported with its parent entity. Entities used as fields in messages must be declared as embedded. 
transactional

Whether or not this entity supports transaction commit and rollback via the applications ODS store.

 
Entity Field Attributes
AttributeDescriptionRequired
type

The type of the element. If the type is defined in this namespace, is defined in only one of the imported models, or is a primitive or collection type, only the simple name of the type need be used. Non fully qualified name will be looked up in both current model and its imports. If name is declared in current model and in one of imports, the type from current model will take precedence. If multiple imports define the same name, an error will be reported. Best correction in such case is to use fully qualified name.

 If the field is an arraytype, it should be suffixed with array indeces indices such as MyEntity[] to denote it as an array.

(tick)
nameThe name of the field, must be unique within the model.(tick)
jsonName

Contains the name of the JSON property that will be used for the field when the message is serialized to JSON. Defaults to use the value defined in name.

 
idThe id of the field. The id must be unique with the scope of this entity. For Xbuf/Protobuf encoding, this tag is used as the tag value for the field on the wire. If not set, a unique id will be generated by the source code generator. For better control over compatibility, it is reccomended recommended that application set this value manually. 
length

If this field refers to a variable length type (such as string), this indicates the maximum length of the field.

 
pinnedA pinned field always occupies the same space on the wire. 

isKey

True if this field should serve as the key when inserting the entity into a LongMap or StringMap collection. In this case, insertion of the message in the collection with a given key will update this field with the key value. 

...

Messages are defined in the messages element and must have a unique id with respect to other types within the scope of their factory. Message Messages can use all of modeling capabilities of entities, but cannot use non-embedded entities or non-array collections as fields.

...

Message fields support the same attributes as entities (listed above).

Notes

For Xbuf and Protobuf encoding, the id is used to generate the protobuf field tag, so changing field ids will break wire compatibility. If fields are not explicitly assigned ids in the model, then the ADM generate will assign them automatically. in this case, fields should not be removed or changed in order to maintain wire compatibility with earlier version of the generated code. 

...

For an entity named "MyMessage" an interface and an implementation will be generated using the model's namespace. Entities will extend IRogNode or IRogMessage IRogMessage, marking that they can be used as nodes with the platform's Replicated Object Graph (ROG) framework and the platforms platform's Simple Message Abstraction (SMA) Layer.

...

  • An embedded entity cannot be inlined into another embedded entity in a manner that will produce inlining cycles.
  • An embedded entity cannot inline a Message or Entity.
  • Messages and embedded entities cannot inline non embedded entities. 
  • A Message can't inline 2 embedded entities that have conflicting field definitions (e.g. fields with the same name but with attributes that differ in any way other than their doc or required attributes). In the case of required, if an embedded entity defines a field as required, then it will be required on the message.
  • The isKey and pinned field attributes on an embedded entity are ignored when the entity is inlined into another message or entity. 
  • A Message can't define fields that conflict with the any of its inlined embedded entities (e.g. fields with the same name but with attributes that differ in any way other than their doc or required attributes). 
    Exceptions to this rule are:
    • required: In the case of the required attribute, the value set on a message field wins (a particular message can override the required field attribute on an inlined embedded entity).
    • isKeyField: entity or message inlining the embedded entity can set the isKey attribute for a given inlined field. 
    • pinned: the pinned attribute can be overridden by the inlinining entity. 

Generated Source

Note that javadoc is omitted below and only the simple accessors are listed for brevity:.

Code Block
languagejava
public interface IMyEmbeddedEntity {
	public void setMyStringField(final String field);
	public String getMyStringField();

	public void setMyIntField(final int field);
	public int getMyIntField();
}
 
public interface IMyOtherEmbeddedEntity {
	public void setMyDateField(final Date field);
	public Date getMyDateField();

	public void setMyBooleanField(final boolean field);
	public boolean getMyBooleanField();
}
 
public interface IMessage extends IRogNode, IRogMessage, IMyEmbeddedEntity, IMyOtherEmbeddedEntity{
	public void setMyEnumerationField(final MyEnumeration field);
	public MyEnumeration getMyEnumerationField();
 
	public void setMyStringField(final String field);
	public String getMyStringField();

	public void setMyIntField(final int field);
	public int getMyIntField();
	public void setMyDateField(final Date field);
	public Date getMyDateField();

	public void setMyBooleanField(final boolean field);
	public boolean getMyBooleanField();
}

Inlined Messages

Warning

At present, it is not possible to declare a Message as a field of another Message or Embedded Entity. This functionality may be added in a future release.

...

A required field can be declared by setting its required attribute. The required attribute is optional and defaults to false. When required fields are defined, checks for their presence will be added to the types type's isValid() method. Calling getValidationErrors() on a type missing that field will result in an error of the form: "myDoubleField is not set" being added to the provided list. Neither the the Messaging Engine nor the transport layer will invoke isValid() on a given message, validation checks are left to the application code. 

To generate logic for required fields, the generateRequiredFieldValidators=true directive should be passed to the AdmCodeGenerator.

XML

Code Block
xml
xml
<message name="Message" factoryid="1" id="1">
	<field name="myDoubleField" type="Double" required="true"/>
</message>

...

Because Message and Entities are serialized and deserialized, it is important that modeled objects can evolve over time without breaking the ability to deserialize objects from a previous evolution of a model. For example, if the type of a message field is changed from one release of your application to the next, a receiver that is still on an older version that receives the message won't be able to deserialize the message. To ensure that such such issues don't occur, follow the set of guidelines and rules outlined below. Similarly, if your application is configured to persist to a transaction log and you later try to read from the transaction log with the newly generated code, it won't be able to read from the older serialization. 

  1. Don't change the ids of factories. 
  2. Don't change the ids of types. 
    1. If a type will no longer be used, it is better to deprecate it to avoid accidentally declaring a new type the reuses the old type id later. 
  3. Declare the id attribute on fields. 
    1. If you don't declare field ids, ADM will assign them in the order they are declared, but this means that any reordering or removal of the field would result in different type ids. 
  4. Don't remove fields on messages.
    1. It is better to deprecate a field rather than remove it. This avoids the potential for accidentally reusing the field id. 
  5. Don't change the type of a field. If a field type needs to be changed, deprecate the existing field and add a new field with the same type. 
    1. It is generally okay to change a scalar type such as a byte to a short, int or long, or a float to a double, but not vice-versa.
  6. Don't change the name of fields.
    1. For Json encodings, changing the field name is a breaking change.
    2. For Xbuf and Protobuf encodings, it is technically okay to change the name of the field as long as the id remains the same. 


Following the above steps will allow applications generated with the new versions of the model can to still interoperate.

There are a few additional compatibility issues to consider if the generated code will be shared between multiple projects which is covered in Runtime Compatibility.

Next Steps

Once you have modeled your messages and state, you'll need to decide on what message encoding to generate and generate the state. The following sections cover these topics. 

  • Generating Source Code - Discusses how to integrate code generation into your project's build. 
  • Choosing an Encoding - Discusses the differences between the various encoding types supported by ADM. 
  • Runtime Compatibility - Discusses implications on runtime compatiblity compatibility when using code generated with a different platform version. 

...