Table of Contents
Just a second...

Metadata

Metadata defines the format of a Diffusion™ topic message.

Diffusion metadata is a generic mechanism for describing message formats regardless of the exact data representation. It describes a message in terms of fields within it and these fields can be grouped into records which themselves can be further subdivided into fields and/or records.

This generic representation of metadata can potentially be used for many different types of message data. For example, metadata can be used to describe a Diffusion record-based message where fields and records have the same meaning but certain constraints exist (for example, nesting of records is not permitted).

The purpose of metadata is to allow for programmatic modeling of data structures. At this point in time it is only available within the Java™ Classic API and used in the following ways:

  • To define the layout of a record so that fields within the record can be addressed by name.

  • To define the layout of the messages used by record topic data in terms of one or more record definitions.

Metadata structure

A Metadata definition is made up of a hierarchy of metadata nodes (class MNode ) with a message node (class MMessage ) at the top. A message is defined as one or more field (class MField ) and/or record(class MRecord ) nodes. A record is also defined as one or more field and/or record nodes (MMessage is a specialization of MRecord ). A Field defines an elementary data item of a particular data type.

Every node has a name which must be unique within its parent node. Every child node can represent one or more possible occurrences of that record or field within the parent. The number of possible occurrences of a node is described by its multiplicity.

The order in which nodes are defined within their parent defines the order that they will appear in within a message.

message metadata

A metadata message (class MMessage ) is the top level object of a metadata definition which represents a whole message layout. Metadata messages are created using the MetadataFactory class specifying the type of topic data that the metadata describes. Even though metadata definitions are generic in form, the type of data imposes constraints on the metadata. A message can be made up of one or more fields and/or records.

Field metadata

A metadata field (class MField ) defines an elementary data item within a message or record. Every field has a data type which defines its actual representation within the message. A field can have a default value specified to use when initializing message data. A field has a multiplicity within its parent node.

Data types

The data type of a field defines its actual representation within the message. The data types available varies according to the topic data type that the metadata is defining. The same data type can result in different physical data representations for different topic data types. The available data types are defined by the enum MDataType and the following are currently available:

Table 1. Data types
STRING A character string. Represented internally as a java.lang.String. The initial default value for this type is a zero length string.
INTEGER STRING An integral number represented in the message as a character string. This is represented internally as a java.math.BigInteger. If a field is defined as this type, it can only contain numeric digits with an optional leading sign. By default, empty fields are not allowed. The initial default value for this type is 0.
DECIMAL STRING A decimal number represented in the message as a character string. This is represented internally as a java.math.BigDecimal

. Decimal fields have the number of places to the right of the decimal point defined by the scale, the default being 2.

Such values can be parsed from a character String with any number of digits to the right of the decimal point but half-up rounding is applied to achieve the target scale and output of the field is rendered with the specified scale.

By default, empty fields are not allowed. The initial default value for this type is 0.00 (depending on scale). For comparison purposes the scale is ignored: a value of 1.50 is the same as 1.5.

CUSTOM STRING This is a special type where the behavior is delegated to a user written CustomFieldHandler. See API documentation for more detail or the example below. This type is available in all topic data types.

CustomFieldHandler example

A custom field handler can be used for defining a data type other than those provided and can deal with any String data format required. The following example shows a handler used to represent a Double value:

public class DoubleFieldHandler implements CustomFieldHandler {
    
    public Object getInitialDefaultValue() {
        return new Double(0.0);
    }   

    public Object parse(Object object) throws APIException {
        if (object==null) {
            return new Double(0.0);
        }
        try {
            return (Double.parseDouble(object.toString()));
        }
        catch (Throwable ex) {
            throw new APIException(
                "Unable to parse "+object+" as double value",
                ex);
        }
    }
    
    public boolean areEqual(Object source,Object target) {
        return source.equals(target);
    }  
}

Scales

Decimal format fields (such as DECIMAL_STRING) can have a scale which defines the number of places to the right of the decimal point.

The scale of a field is set using the setScale method. If not specified, a value of 2 is assumed. This value is ignored for all but decimal format fields.

Default values

Every field can have a default value specified for it using the setDefaultValue method. If not specified, the default value is assumed to be the default for the data type. When a message is created using metadata, default initialization applies the default values specified for each field.

Empty values

By default STRING type fields will accept an empty field (a zero length string) as input but other types do not. However, you can allow other field types to allow empty input also. This is done using the setAllowsEmpty method on the MField object.

Multiplicity

The multiplicity of a metadata field or record defines the number of times the corresponding data can occur within its parent.

Multiplicity (defined by the Multiplicity class) is defined in terms of the minimum and maximum number of occurrences. Some data representations (such as protocol buffers) support variable numbers of nodes, whereas others (such a record data) only support fixed number of nodes (where minimum=maximum) except in the last position.

Fixed multiplicity can be defined by a single number. For example a multiplicity of 5 (for example, new Multiplicity(5) ) indicates that there must be exactly 5 occurrences of the node within its parent.

Variable multiplicity is defined in terms of a minimum value and a maximum value and is represented with the notation n..n. So a multiplicity of 1..5 ( new Multiplicity(1,5)) indicates that there can be between 1 and 5 occurrences of the node within its parent. A special maximum value of -1 is used to represent no maximum so a multiplicity of 1..n ( new Multiplicity(1,-1) ) means that there can be any number of occurrences of the node but there must be at least one.

Optional nodes are indicated by a minimum value of 0. So 0..1 represents a node that can occur 0 or once and 0..n represents a node that can occur any number of times or not at all. Of course a fixed multiplicity of 0 is not allowed.

Record data example

The example below shows a method that defines some message metadata to be used with record data.

The message comprises two records.

MMessage defineMyMetadata() throws APIException {
        MMessage message =
            MetadataFactory.newMetadata("MyMessage",TopicDataType.RECORD);
        
        MRecord record=message.addRecord("Record1");
        MField field;
        field=record.addField("Name");
        field=record.addField("AccountNumber",MDataType.INTEGER_STRING);
        field.setDefaultValue(-1);
        field=record.addField(
            "Price",
            MDataType.DECIMAL_STRING,
            new Multiplicity(2));
        field.setScale(3);
        
        record=message.addRecord("Record2");
        record.addField("AddressLine",new Multiplicity(5));
        
        return message;
    }

Loading metadata

Currently metadata can only be described programmatically so it can be useful to employ the singleton pattern to encapsulate metadata definitions so that they only have to be loaded once. The following class is an example of such a singleton:

public class MyMetadata {
   
    private static MyMetadata theInstance = null;
   
    private HashMap<String,MMessage> theMetadata =
        new HashMap<String,MMessage>();
   
    private MyMetadata() throws APIException {
        loadMetadata();
    }
   
    private static MyMetadata instance() throws APIException {
       if (theInstance==null) {
            synchronized(MyMetadata.class) {
                if (theInstance==null) {
                    theInstance=new MyMetadata();
                }
            }
        }
        return theInstance;
    }
   
    public static MMessage get(String name) throws APIException {
        return instance().getMetadata(name);
    }
   
    private MMessage getMetadata(String name) {
        return theMetadata.get(name);
    }
   
    private void loadMetadata() throws APIException {
        loadMessage1();
        loadMessage2();
        //... and so on
    }
   
    private void loadMessage1() throws APIException {
        MMessage message =
            MetadataFactory.newMetadata("Message1",TopicDataType.RECORD);
       
        MRecord record=message.addRecord("Record1");
        record.addField("Name");
        record.addField("AccountNumber",MDataType.INTEGER_STRING);
          
        record=message.addRecord("Record2");
        record.addField("AddressLine",new Multiplicity(5));
       
        theMetadata.put("Message1",message);
    }
   
    private void loadMessage2() throws APIException {
        // load another message format
    }
} 

Metadata definitions can be obtained using the MyMetadata.get(name) method, for example:

    MMessage metadata = MyMetadata.get("Message1");