CodeStreet Messaging Framework

 


A White Paper

Author: Jawaid Hakim

Published: January 13, 2006

 

 

 

 

 



Abstract

 

Message oriented middleware, such as TIBCO Rendezvous and JMS, is a popular foundation for distributed systems.  But the advantages of messaging for building such decoupled systems are also weak points: mismatches between message specifications and code can occur, type safety issues arise, and different teams develop varying understanding of message semantics. The CodeStreet Messaging Framework addresses many of these problems by providing transparency (XML message specifications), type-safety (generated message classes), a host of critical features (extended data types, message validation, etc.), and flexibility (converter classes).

 

 


 

1.      Introduction.. 3

2.     The Message Creation Process. 4

3.     XML Message Specification.. 4

3.1.        Field ConstraintsField Constraints. 5

3.2.        Fixed and Default Field Values. 6

3.2.1.     Optional Fields 6

3.3.        Tagged Fields. 7

3.4.        JMS Property Fields. 8

3.5.        ‘Send’ Subject Specification.. 8

3.5.1.     Request/Reply 9

3.5.2.     Request/Reply 9

3.6.        Extended Field Types. 10

3.7.        Inheritance.. 10

3.8.        Containment.. 11

4.     Message Validation.. 11

5.     Converter.. 11

5.1.        ConverterTibrv.. 12

5.2.        ConverterTibaerv.. 13

5.3.        ConverterJMS. 14

5.4.        ConverterXML. 15

5.4.1.     CustomTags and Attribute Names 17

5.5.        Converter Performance.. 17

6.     Code Generator.. 18

6.1.        Message Classes. 18

6.2.        Message Factory.. 18

6.3.        Type-Safe Interface.. 18

6.4.        Generic Interface.. 19

6.5.        Convenience Methods. 20

7.     Support for .NET/C#.. 21

8.     Framework Comparison.. 21

9.     Conclusion.. 22

10.        Appendix A: Supported Field Types. 23

10.1.      Bean Fields. 24

11.    Appendix B: Supported Message Formats. 25

12.        Appendix C: Sample Message Specification.. 26

 


1.                Introduction

 

Message oriented middleware (MOM) has gained considerable currency as the preferred way to build highly decoupled and scalable systems. Many messaging protocols and their associated development frameworks exist in the marketplace. Some popular protocols and frameworks are TIBCO/Rendezvous[1], JMS[2], and XML.

 

Sending and receiving messages using these frameworks is extremely intuitive and easy. However, the very generic nature of these messaging frameworks makes it difficult to ensure consistency and type-safety across all the processing components.

 

To illustrate this point let us consider a typical application that uses TIBCO/Rendezvous. Suppose this application publishes Order messages. Each Order message contains three fields: TICKER, Quantity, and BuySell. Figure 1 illustrates the structure of this message.

 

 

 

Figure 1

 

The publisher of this Order message will have to perform the following processing steps to create the message:

 

TibrvMsg rvMsg = new TibrvMsg();

rvMsg.add(“TICKER”, “rtrsy”);

rvMsg.add(“Quantity”, new Long(“1000”));

rvMsg.add(“BuySell”, new Integer(“1”));

 

Now, suppose the message specification changes and the Quantity field is changed from a Long to a Float. The code shown above will continue to work – both compile and run - even though the Order message that is being published is no longer valid. This mismatch between code and specification occurs because the message class has no link (binding) to the external message definition[3]. The same drawback is present in other messaging frameworks.

 

In a typical application development environment message specifications are usually defined in some text document. It is the responsibility of the development team to periodically synchronize the code with these external message specifications. As we all know from experience, this is a recipe for disaster. A significant amount of effort has to be devoted by both development and quality assurance teams to ensure that messages contain all the required fields and that these fields are of the correct type.

 

To address these issues and provide a number of other useful features, CodeStreet has developed a messaging framework. This framework works seamlessly with TIBCO/ Rendezvous and with other industry-standard protocols. The framework is described in the following sections.

 

2.                The Message Creation Process

 

The messaging framework requires the following steps: create XML message specifications, use the code-generator to create classes from the message specifications, compile the generated classes, and use the compiled message classes in the application. Figure 2 illustrates the general build process for a Java client application.

 

 

 

Figure 2

 

3.                XML Message Specification

Messages are specified using XML. This should be no surprise as XML is a very rich and industry-standard language for describing content.

 

The library provides a XML schema[4] for defining message specifications. Schema-aware editors can be used to easily create message specifications based on this schema. Many such editors available in the marketplace including TIBCO TurboXML[5].

 

Enough talk. Let us jump straight in and look at a snippet of the XML that is created for the Order message definition:

 

            <msgspec name=”Order” …>

<fldlist>

<fldstr name=”TICKER” desc=”Asset identifier”/>

<fldi64 name=”Quantity” desc=“Quantity”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag” />

</fldlist>

      </msgspec>

 

It is clear from the XML snippet shown above that three fields are being defined for the Order message. The type of each field is easily identified from the XML tag – e.g. i32 is a four-byte field that maps to an Integer in Java.

 

Notice that each field has an optional description. This description makes clear to anyone looking at the specification what the purpose (semantics) of the field is.

 

3.1.                       Field ConstraintsField Constraints

 

In many applications it is necessary to constrain field values.

 

In the example shown above, it might be necessary to guarantee that the TICKER contains no more than 8 characters. One way to specify this constraint would be to add it to the field description – again, it would then be up to the developers to read and honour[jp1]  the constraint. We can do a lot better than that.

 

Look at the following XML specification:

 

<fldstr name=”TICKER” desc=”Asset identifier” maxInclusive=”8”/>

<fldi64 name=”Quantity” desc=“Quantity” minInclusive=”0”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag”>

  <constraints>

      <enums>

        <enum value=”1” />

        <enum value=”2” />

      </enums>

  </constraints>

</fldi32>

 

A number of interesting things are happening here. TICKER has been constrained to a maximum length of 8, Quantity has been constrained to a value greater than or equal to 0, and BuySell has been converted into an enumeration – it can only take on the values 1 or 2.

 

 

3.2.                       Fixed and Default Field Values

 

Message specifications often define fields whose values are fixed at design time and cannot be changed by the application. In addition, fields can sometimes have default values.

 

For example, suppose an application requires every message to participate in a workflow and the name of the workflow to be fixed at message design time. In addition, suppose each message has a Transient field whose default value is true. Instead of requiring the application developer to set the workflow name and the transient flag, the following message specification can be created:

 

<fldstr name=”Workflow” desc=”Workflow” maxInclusive=”8” fixedVal=”Order”/>

<fldbool name=”Transient” desc=”Persistence” defaultVal=”true”/>

<fldstr name=”TICKER” desc=”Asset identifier” maxInclusive=”8”/>

<fldi64 name=”Quantity” desc=“Quantity” minInclusive=”0”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag”>

  <constraints>

      <enums>

        <enum value=”1” />

        <enum value=”2” />

      </enums>

  </constraints>

</fldi32>

 

In this example message specification, the framework automatically sets the values of the Workflow and Transient fields. In addition, the value of Workflow is fixed (locked) when the message object is instantiated and cannot be set by the application.

 

 

3.2.1.                                                                  Optional Fields

 

Message specification can tag fields as optional. If a field is not marked optional then it must be set by the application. When a message is validated (see Message Validation) the framework ensures that all non-optional fields have been set.

 

In the example shown below, the Timeout field is optional.

 

<fldstr name=”Workflow” desc=”Workflow” maxInclusive=”8” fixedVal=”Order”/>

<fldbool name=”Transient” desc=”Persistence” defaultVal=”true”/>

<flddate name=”Timeout” desc=”Timeout” optional=”true”/>

<fldstr name=”TICKER” desc=”Asset identifier” maxInclusive=”8”/>

<fldi64 name=”Quantity” desc=“Quantity” minInclusive=”0”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag”>

  <constraints>

      <enums>

        <enum value=”1” />

        <enum value=”2” />

      </enums>

  </constraints>

</fldi32>

 

 

3.3.                       Tagged Fields

 

Any field in a message can be tagged. A tag is any string that has some specific semantic within an application. Tagged fields can be used by an application to implement a number of useful functions.

 

For example, suppose a message has a large number of fields. Furthermore, suppose that a small subset of these fields are required for comparing message instances for equality. An application could tag the relevant fields in the message with a tag, and, at run-time, use the tag to fetch and compare the tagged fields.

 

In the example shown below, the field OrderId has been tagged with the tag EQ.

 

<fldstr name=”Workflow” desc=”Workflow” maxInclusive=”8” fixedVal=”Order”/>

<fldstr name=”OrderId” desc=”Order Id”>

  <tags>

      <tag value=”EQ”/>

  </tags>

<fldbool name=”Transient” desc=”Persistence” defaultVal=”true”/>

<flddate name=”Timeout” desc=”Timeout” optional=”true”/>

<fldstr name=”TICKER” desc=”Asset identifier” maxInclusive=”8”/>

<fldi64 name=”Quantity” desc=“Quantity” minInclusive=”0”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag”>

  <constraints>

      <enums>

        <enum value=”1” />

        <enum value=”2” />

      </enums>

  </constraints>

</fldi32>

 

At run-time, the application could use the following code to compare two instances of the message shown above:

 

 try

{

// Get first message instance

Order first = …;

 

// Get second message instance

Order second = …;

 

// Use special equals method for comparing instances

if (first.equalsUsingTaggedFields(second, “EQ”, false))

{

     

}

      }

      catch (Exception ex)

      {

            // Handle exception

      }

 

3.4.                       JMS Property Fields

 

Messages in JMS have three distinct parts – headers (set by the JMS provider), properties (set by application), and the message payload. The framework provides a convenient API for applications to tag property fields – i.e. fields whose value is place in the properties section in addition to the payload.

 

In the example shown below, the field OrderId has been tagged with as a property. When this message is marshaled by the JMS converter, the run-time value of OrderId will be added to the properties section of the message.

 

<fldstr name=”Workflow” desc=”Workflow” maxInclusive=”8” fixedVal=”Order”/>

<fldstr name=”OrderId” propertyName=”ORDER_ID” desc=”Order Id”>

  <tags>

      <tag value=”EQ”/>

  </tags>

<fldbool name=”Transient” desc=”Persistence” defaultVal=”true”/>

<flddate name=”Timeout” desc=”Timeout” optional=”true”/>

<fldstr name=”TICKER” desc=”Asset identifier” maxInclusive=”8”/>

<fldi64 name=”Quantity” desc=“Quantity” minInclusive=”0”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag”>

  <constraints>

      <enums>

        <enum value=”1” />

        <enum value=”2” />

      </enums>

  </constraints>

</fldi32>

 

3.5.                       ‘Send’ Subject Specification

 

Typically, once a message has been created and its fields have been set by the application, the message is published on a specific send subject. Application developers have many ways for determining the send subject of the message – it can be hard-wired into the code, read from a configuration file, etc.

 

The framework provides a flexible way of specifying send subjects within message specifications. As a result a message object itself can be queried for its send subject.

 

 

 

3.5.1.                                                                  Request/Reply

 

Fixed send subjects, by definition, are fixed at message specification time. These subjects cannot be changed by the application.

 

In the message specification shown below, the send subject has been set to APP.NYSE.Order.

 

<msgspec … subjectSpec=”APP.NYSE.Order” />

<fldlist>

 

<fldstr name=”Workflow” desc=” Workflow” maxInclusive=”8” fixedVal=”Order”/>

<fldstr name=”Exch” desc=”Exchange” />

<fldbool name=”Transient” desc=”Persistence” defaultVal=”true”/>

<flddate name=”Timeout” desc=”Timeout” optional=”true”/>

<fldstr name=”TICKER” desc=”Asset identifier” maxInclusive=”8”/>

<fldi64 name=”Quantity” desc=“Quantity” minInclusive=”0”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag”>

  <constraints>

      <enums>

        <enum value=”1” />

        <enum value=”2” />

      </enums>

  </constraints>

</fldi32>

</fldlist>

      </msgspec>

 

 

3.5.2.                                                                  Request/Reply

 

The subject on which a class of messages is published can sometimes take on different values at run-time. These ‘send’ subjects are often derived from the value one or more fields in the message being published. The framework provides a flexible way to construct these parameterized send subjects.

 

In the message specification shown below, the send subject contains two parameterized components –Workflow and Exch. The send subject is dynamically constructed at run-time by the framework from the actual values of these two fields.

 

<msgspec … subjectSpec=”APP.[Exch].[Workflow]” />

<fldlist>

<fldstr name=”Workflow” desc=” Workflow” maxInclusive=”8” fixedVal=”Order”/>

<fldstr name=”Exch” desc=”Exchange” />

<fldbool name=”Transient” desc=”Persistence” defaultVal=”true”/>

<flddate name=”Timeout” desc=”Timeout” optional=”true”/>

<fldstr name=”TICKER” desc=”Asset identifier” maxInclusive=”8”/>

<fldi64 name=”Quantity” desc=“Quantity” minInclusive=”0”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag”>

  <constraints>

      <enums>

        <enum value=”1” />

        <enum value=”2” />

      </enums>

  </constraints>

</fldi32>

</fldlist>

      </msgspec>

 

In addition to using field names as parameterized components, the application can use the framework API to supply additional name/value pairs at run time. This might be necessary in case a send subject component is not a field in the published message.

 

3.6.                       Extended Field Types

 

The framework extends the set of field types supported by Rendezvous.

 

<fldstr name=”TICKER” desc=”Asset identifier”/>

<fldi64 name=”Quantity” desc=“Quantity”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag” />

<fldstrarray name=”ExchList” />

<fldstrhashtable name=”Exch2Country” />

<fldmsgobj name=”Stuff” className=”com.codestreet.msg.Stuff”/>

 

The message specification shown above contains a string array, a string hashtable, and a nested message object (defined in a separate message specification).

 

3.7.                       Inheritance

 

Once a message specification has been created, another message specification can inherit from it. This feature allows a high-degree of reuse.

 

For example, in the message specification shown below, the LimitOrder message inherits all the fields of the Order message:

           

            <msgspec name=”LimitOrder” … extends=”Order”>

<fldlist>

<fldf32 name=”LimitPrice” desc=”Limit price”/>

</fldlist>

      </msgspec>

 

The framework implements message inheritance as true Java inheritance – i.e. the LimitOrder class extends the Order class.

 

3.8.                       Containment

 

Another way to re-use message definitions is through containment. Instead of inheriting from an existing message, a message can contain an instance of the existing message.

 

In the message specification shown below, the LimitOrder message contains an instance of the Order message:

           

            <msgspec name=”LimitOrder”>

<fldlist>

                        <fldmsgobj name=”Order” className=”com.codestreet.msg.Order” />

<fldf32 name=”LimitPrice” desc=”Limit price”/>

</fldlist>

      </msgspec>

 

Notice that the definition includes the Java class name of the contained message object. The Java class name allows the framework to ensure the run-time type safety of the field.

 

 

4.                Message Validation

 

The framework performs message validation at two levels – at the individual field level and at the message level. Together, these two validations ensure that all messages conform to their specifications.

 

At the field level, the framework implements an early fail validation strategy – i.e. it detects and reports errors as early as possible. Whenever a message field is set by the application, the framework validates the new value against all constraints attached to that field. A field validation exception is thrown if the new field value violates any constraint.

 

At the message level, the application developer can (and always should) explicitly call the validate method on message objects. The validate method ensures all non-optional fields in the message have been set.

 

5.                Converter

Application developers work with message objects – i.e. instances of message classes. This object-oriented API ensures type-safety and provides a host of useful features, e.g. message validation.

 

However, when a message has to be published on the wire, it must be converted to a format that the underling transport can process. For example, a message object must be converted into its TibrvMsg representation before it can be published on the Rendezvous message bus.

 

The framework provides a number of converter classes. A converter class is responsible for marshalling a message object to another representation, and for unmarshalling a message object from another representation.[6]

 

5.1.                       ConverterTibrv

 

This converter can marshal/unmarshal message objects to/from TibrvMsg.

 

Given the following specification:

 

<fldstr name=”TICKER” desc=”Asset identifier”/>

<fldi64 name=”Quantity” desc=“Quantity”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag” />

 

The code fragment shown below illustrates how the publisher can convert an instance of the Order message into a TibrvMsg:

 

try

{

// Create message object

Order ord = new Order();

 

// Set fields

ord.setTICKER(“rtrsy”);

ord.setQuantity(1000);

ord.setBuySell(1);

 

// Always validate

ord.validate();

 

// Marshal message object to TibrvMsg

TibrvMsg rvMsg = ConverterTibrv.marshal(ord);

 

// Set send subject

rvMsg.setSendSubject(ord.getSendSubject());

 

// Publish the message using RV transport

      }

      catch (FieldValidationException ex)

      {

            // Handle validation exception

      }

      catch (ConverterException ex)

      {

            // Handle converter exception

      }

 

The code fragment shown below illustrates how the receiver of the message can convert the published TibrvMsg into a message object:

 

try

{

// Create message object

Order ord = new Order();

 

      // Unmarshal message object from TibrvMsg

      ConverterTibrv.unmarshal(ord, rvMsg);

 

// Always validate

ord.validate();

      }

      catch (ConverterException ex)

      {

            // Handle converter exception

      }

 

5.2.                       ConverterTibaerv

 

The TIBCO Active Enterprise (AE) wire format allows applications to embed control fields within messages. These control fields allow participating AE applications to perform message validations. The framework provides support for AE messages that contain the data content as a nested RV message.

 

This converter can marshal/unmarshal message objects to/from TibrvMsg using the AE wire format.

 

Given the following specification:

 

<fldstr name=”TICKER” desc=”Asset identifier”/>

<fldi64 name=”Quantity” desc=“Quantity”/>

<fldi32 name=”BuySell” desc=”Buy (1)/Sell(2) flag” />

 

The code fragment shown below illustrates how the publisher can convert an instance of the Order message into a TibrvMsg:

 

try

{

// Create message object

Order ord = new Order();

 

// Set fields

ord.setTICKER(“rtrsy”);

ord.setQuantity(1000);

ord.setBuySell(1);

 

// Always validate

ord.validate();

 

// Marshal message object to TibrvMsg

TibrvMsg rvMsg = ConverterTibaerv.marshal(ord);

 

// Set send subject

rvMsg.setSendSubject(ord.getSendSubject());

 

// Publish the message using RV transport

      }

      catch (FieldValidationException ex)

      {

            // Handle validation exception

      }

      catch (ConverterException ex)

      {

            // Handle converter exception

      }

 

The code fragment shown below illustrates how the receiver of the message can convert the published TibrvMsg into a message object:

 

try

{

// Create message object

Order ord = new Order();

 

      // Unmarshal message object from TibrvMsg

      ConverterTibaerv.unmarshal(ord, rvMsg);

 

// Always validate

ord.validate();

      }

      catch (ConverterException ex)

      {

            // Handle converter exception

      }

 

5.3.                       ConverterJMS

 

This converter can marshal/unmarshal message objects to/from JMS.

 

All primitive Java types and their objectified versions supported by JMS – e.g. String, int, Integer, float, Float, double, Double, byte[], etc. – are converted to corresponding fields in the MapMessage. Field types that are not supported by JMS – e.g. String arrays, nested messages, etc. – are converted to XML. This allows the framework to seamlessly marshal/unmarshal all field types.

 

The code fragment shown below illustrates how the publisher can convert an instance of Order into its JMS representation:

 

try

{

// Create message object

Order ord = new Order();

 

// Set fields

ord.setTICKER(“rtrsy”);

ord.setQuantity(1000);

ord.setBuySell(1);

 

// Always validate

ord.validate();

 

// Create/get JMS MapMessage

javax.jms.MapMessage mapMsg = …;

 

      // Marshal message object to MapMessage

ConverterJMS.marshal(mapMsg, ord);

 

// Wrap the MapMessage in a JMS message and publish

 

      }

      catch (FieldValidationException ex)

      {

            // Handle validation exception

      }

      catch (ConverterException ex)

      {

            // Handle converter exception

      }

 

The code fragment shown below illustrates how the receiver of the JMS message can recreate the message object:

 

try

{

      // Extract the MapMessage from the received JMS message

      MapMessage mapMsg = …;

 

      // Unmarshal message object from MapMessage

      Order ord = (Order)ConverterJMS.unmarshal(mapMsg);

 

// Always validate

ord.validate();

 

// Process order

      }

      catch (ConverterException ex)

      {

            // Handle converter exception

            }

 

5.4.                       ConverterXML

 

This converter can marshal/unmarshal message objects to/from XML.

 

The code fragment shown below illustrates how the publisher can convert an instance of Order into its XML representation:

 

try

{

// Create message object

Order ord = new Order();

 

// Set fields

ord.setTICKER(“rtrsy”);

ord.setQuantity(1000);

ord.setBuySell(1);

 

// Always validate

ord.validate();

 

      // Marshal message object to XML

String xml = ConverterXML.marshal(ord);

 

// Do something with the XML string

 

      }

      catch (FieldValidationException ex)

      {

            // Handle validation exception

      }

      catch (ConverterException ex)

      {

            // Handle converter exception

      }

 

The XML that is generated from marshalling the Order message object is shown below:

 

<?xml version="1.0"?>

<msg name="Order">

  <str name="Ticker" val="rtrsy" />

  <i64 name="Quantity" val="1000" />

  <i32 name="BuySell" val="1" />

</msg>

 

The code fragment shown below illustrates how the receiver of the XML data can recreate the message object:

 

try

{

      // Extract the XML field from the TibrvMsg

      String xml = (String)rvMsg.get(“xml”);

 

      // Unmarshal message object from XML

      Order ord = (Order)ConverterXML.unmarshal(xml);

 

// Always validate

ord.validate();

 

// Process order

      }

      catch (ConverterException ex)

      {

            // Handle converter exception

      }

 

Notice that the receiver of the XML data does not explicitly create an instance of Order. This bit of magic happens because the XML data carries the message name and the converter can use that information to instantiate the appropriate class.

 

Byte arrays – used for the field types RFldOpaque and RFldXml – are converted to their Base64[7] representation for inclusion in XML.

 

5.4.1.                                                                  CustomTags and Attribute Names

 

It is possible to register custom names for the XML tags used by the converter.

 

The code shown below illustrates how to register custom tags for fields and how to change the names of field attribute:

 

try

{

      // Create mapping of old tags to new tags

      java.util.Map tags = new java.util.HashMap();

 

      // Change i32 tag to int

      tags.put(“i32”, “int”);

 

      // Change str tag to string

      tags.put(“str”, “string”);

 

      RFld.setXmlTags(tags);

 

      // Change names of attributes

      RFld.setXmlAttrNames(“nameis”, “valueis”, “idis”);

 

      }

      catch (FieldValidationException ex)

      {

            // Handle exception

      }

 

In the code shown above, the XML tags i32 and str have been replaced with the tags int and string. In addition, the field attribute names have been changed to nameis (from name), valueis (from value), and idis (from id).

 

5.5.                       Converter Performance

 

All converters have been tested in real-world situations with high message rates. Using this feedback, each converter has been tuned to ensure efficient conversions. Special attention has been paid to ensuring that object creation is kept to a minimum.

 

The bottom line is that the framework does not introduce a bottleneck in the application’s performance.

 

 

6.                Code Generator

 

The magic of generating C#/Java classes, from XML message specifications, is performed by the code-generator. This code generator was written in pure Java and can easily be integrated into the daily build process. As a result, every build of the application ensures that all application components – from the front-end to the backend - are working with the latest and greatest message classes. A majority of discrepancies between message specifications and application code are caught at compile time.

 

6.1.                       Message Classes

 

The code-generator creates one Java class per message specification.

 

6.2.                       Message Factory

 

The code-generator automatically generates a single message factory class. This message factory provides the API for creating message objects from message names, and from fully qualified class name. Applications can also plug in their own message object factories into the framework.

 

The code fragment given below uses the generated factory class to create a message object by name:

 

try

{

      // Get singleton instance from generated factory

      RFactory fact = MsgFactory.getInstance();

 

      // Create Order message object by message name

      Order ord = (Order)fact.createMsgObj(“Order”);

 

// Do something useful

      }

      catch (FactoryException ex)

      {

            // Handle factory exception

      }

 

6.3.                       Type-Safe Interface

 

The biggest benefit for applications is to get a type-safe interface to messages. Using the type-safe API enables the Java compiler to catch any mismatch between the message specification and the application code at compile time. Finding and correcting errors at compile time is significantly quicker than correcting errors at run time.

 

In the code fragment shown below, the Action field is used to do custom processing.

 

void director(RMsg request)

{

      if (request instanceof OrderAction)

      {

            OrderAction ordAct = (OrderAction)request;

            int action = ordAct.getAction();

            if (action == MODIFY)

                  modifyOrder(ordAct);

            else if (action == DELETE)

                  deleteOrder(ordAct);

           

      }

}

 

 

Suppose that the Action field is changed in the message specification from an int to a String. The code fragment given above will fail to compile and the mismatch can be quickly corrected.

 

6.4.                       Generic Interface

 

Frequently, an application requires a generic interface to an object. For example, suppose the CreateOrder and Trade have a field named Timestamp, and this Timestamp has to be updated. The following code fragment could be written to do the update:

 

void updateTimestamp(RMsg request)

{

      try

{

      long time = System. currentTimeMillis ();

            if (request instanceof CreateOrder)

            {

                  CreateOrder ord = (CreateOrder)request;

            Ord.setTimestamp(time);

            }

            else if (request instanceof Trade)

            {

                  Trade trd = (Trade)request;

            trd.setTimestamp(time);

            }

      }

catch (FieldValidationException ex)

{

                  // Handle validation exception

}

}

 

The problem with this code is that if the Timestamp field is added to more messages, the code will have to be updated as well. It would be nice for the application to be able to access the Timestamp field via a generic API – i.e. not have to do a Java instanceof followed by a typecast.

 

The framework provides just such an API. Each message class generated by the code-generator implements the RMapMessage interface – this interface is an extension of the Java MapMessage interface. The RMapMessage interface allows an application to access the fields in the message without requiring any typecasts.

 

In the code fragment shown below, the Timestamp field is updated in all messages that contain this field. Moreover, the method does not have to be modified if the Timestamp field is added to more messages:

 

void updateTimestamp(RMsg request)

{

      try

{

      // Check if message contains the Timestamp field

      if (request.fieldExists(“Timestamp”))

      {

                  // Update using generic API

                  long time = System.currentTimeMillis();

                  request.setDate(“Timestamp”, time);

      }

      }

catch (FieldValidationException ex)

{

                  // Handle validation exception

}

}

 

6.5.                       Convenience Methods

 

The API provides many convenience methods that ease the application development effort.

 

For example, suppose the application has an OrderIn message that is sent by the front-end. In response, the application is required to publish an OrderOut message. These two messages have a number of fields in common. Instead of copying each field explicitly in the application code, a single convenience method can be invoked to copy all common fields from the incoming message to the outgoing message:

 

void copyFields(OrderIn in, OrderOut out)

{

      try

{

      // Copy common fields from OrderIn to OrderOut

            out.setFields(in);

 

            // Set the fields that are unique to OrderOut

      }

catch (FieldValidationException ex)

{

                  // Handle validation exception

}

}

 

 

7.                Support for .NET/C#

A C# version of the messaging framework is also available. The C# version of the framework allows messages to be exchanged seamlessly between Java and .NET applications. The framework code generator can generate either Java or C# classes from the same XML definition.

 

8.                Framework Comparison

 

Other software packages exist that provide solutions for Java to XML binding. Basically, given a description of objects and their attributes in XML, these packages provide the ability to automatically generate corresponding Java classes for unmarshalling and marshalling from XML.

 

Some well-known packages in this category are JAXB[8], Castor[9], and Zeus[10]. These general-purpose packages are extremely useful in many situations. As a matter of fact, the CodeStreet Messaging Framework build environment itself uses Castor for processing configuration descriptors. 

 

However, these packages were not built from the ground-up with a MOM environment in mind. As a result, these packages have many shortcomings. Some major shortcomings are the following:

 

·        Java objects can be umarshalled and marshalled only to XML – there is no built-in support for MOM representations such as TIBCO/Rendezvous, JMS, or SOAP. This is a performance concern since parsing XML can become a bottleneck in high-performance applications.

·        No support for send-subject specifications.

·        Each package has its own specific drawbacks. For example, JAXB does not currently support XML Schemas (only DTDs are supported), and Castor cannot correctly marshal/unmarshal hashtable/map collections.


 

·        No planned support for .NET environment. It is quite likely that some components of an application – at the very least, some client-side components - will be deployed on the .NET platform. The support in the CodeStreet framework for the .NET platform allows Windows applications to use the same framework and message definitions as their Java counterparts.

 

 

9.                Conclusion

 

The messaging framework developed by CodeStreet provides transparency (XML message specifications), type-safety (generated message classes), a host of critical features (extended data types, message validation, etc.), and flexibility (converters classes).

 

Using this framework for development ensures that all the system components work with the same set of message definitions. Mismatches between application code and message definitions are caught as early as possible. Most discrepancies are caught at compile time, and, message validation ensures that the rest are caught at run time.

 

This framework should be a valuable addition to the toolbox of developers who are using messaging.


 

10.           Appendix A: Supported Field Types

This appendix lists all field types supported by the framework.

 

Message Specification Tag

Framework Class

Java Type

C# Type

fldstr

RFldString

String

String

fldi8

RFldI8

Byte

Byte

fldi16

RFldI16

Short

Short

fldi32

RFldI32

Integer

Int

fldi64

RFldI64

Long

Long

fldf32

RFldF32

Float

Float

fldf64

RFldF64

Double

Double

flddecimal

RFldDecimal

BigDecimal

Decimal

fldbool

RFldBool

Boolean

Bool

flddate

RFldDatetime

Date

DateTime

flddatearray

RFldDatetimeArray

Date[]

DateTime[]

fldi8Array

RFldI16Array

byte[]

Byte[]

fldi16Array

RFldI16Array

short[]

Short[]

fldi32Array

RFldI32Array

int[]

Int[]

fldi64Array

RFldI64Array

long[]

Long[]

fldf32Array

RFldF32Array

float[]

Float[]

fldf64array

RFldF64Array

double[]

Double[]

fldstrarray

RFdStringArray

String[]

String[]

fldstrhashtbl

RFldStringHashtable

Map

Hashtable

fldxml

RFldXml

String

String

fldmemo

RFldMemo

String

String

fldopaque

RFldOpaque

byte[]

Byte[]

fldmsgobj

RFldMsgObj

RMsg

RMsg

fldmsgobjarray

RFldMsgObjArray

RMsg[]

RMsg[]

fldmsgobjhashtbl

RFldMsgObjHashtable

Map

Hashtable

fldtibrvmsg

RFldTibrvMsg

TibrvMsg

-

fldtibrvmsgarray

RFldTibrvMsgArray

TibrvMsg[]

-

fldipport

RFldTibrvIPPort

TibrvIPPort

-

fldipaddr

RFldTibrvIPAddr

TibrvIPAddr

-

fldbean

RFldBean

Application defined bean class.

Application defined bean class.

fldbeanlist

RFldBeanList

List of application defined bean class.

List of application defined bean class.

 

10.1.                   Bean Fields

Bean fields are of special interest because they allow a client bean class to be used within the framework. Any client bean can be used as a bean field. However, in order to use the converter classes on messages that contain bean fields, client beans must conform to the following specifications:

 

  • The bean must provide a public zero-argument constructor.
  • Bean properties that are to be marshaled must provide public accessor and mutator methods. Accessor methods must be named getXXX() and mutator methods must be named setXXX(), where XXX is the name of the bean property. The return type of the accessor method and the argument to the mutator method must be a supported data type (see below).
  • Bean properties must be an integral data type (String, int, long, short, float, double, and byte), or an array of integral data types, or a bean, or an array of beans. Collection properties are ignored by the framework.

 

Any bean that satisfies these requirements can be used within the framework and can be marshaled/unmarshalled using the framework converter classes.

 


 

11.           Appendix B: Supported Message Formats

 

This appendix lists all the message formats supported by the framework and the converter classes responsible for marshalling/unmarshalling to these formats.

 

Message Format

Converter Class

JMS

ConverterJMS

TIBCO RV

ConverterTibrv

XML

ConverterXML

 


12.           Appendix C: Sample Message Specification  

 

This appendix shows a complete sample XML definition for a creating an order.

 

<msgspec  xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance" xsi:noNamespaceSchemaLocation="msgspec.xsd" name="CreateOrder" desc="Create order message.">

     <fldlist>

          <fldstr name="MessageName" desc="Message Name" fixedVal="CreateOrder" maxInclusive="80"/>

          <fldstr name="SessionId" desc="Session Id." maxInclusive="30"/>

          <fldstr name="WatchlistId" desc="Watchlist Id" maxInclusive="30"/>

          <fldstr name="Pin" optional="true" desc="PIN" maxInclusive="30"/>

          <fldstr name="CorrelationId" desc="Closure argument for identifying purposes" maxInclusive="30"/>

          <fldf64 name="Quantity" desc="Quantity."/>

          <fldf64 name="Markup" desc="Price Markup" optional="true"/>

     <fldmsgobj name=”Inst” javaClassName=”com.codestreet.Instrument” csharpClassName=”CodeStreet.Instrument” desc=”Instrument identification.” />

     <fldbean name=”Foo” javaBeanName=”com.codestreet.beans.Foo” csharpBeanName=”CodeStreet.Beans.Foo” desc=”Foo bean.” />

</msgspec>

 

 

<msgspec xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance" xsi:noNamespaceSchemaLocation="msgspec.xsd" name="Instrument" desc="Create order message.">

     <fldlist>

          <fldstr name="MessageName" desc="Message Name" fixedVal="Instrument" maxInclusive="80"/>

          <fldstr name="Description" desc="Long name" maxInclusive="30"/>

          <fldstrarray name="InstId" desc="Set of instrument Ids to identify asset. Can be TICKERS, CUSIPS, etc"/>

     </fldlist>

</msgspec>


 

 

 

 

 

 

 

 

Jawaid Hakim

CTO

jawaid.hakim@codestreet.com

 
 

 

 

 

 



[3] This scenario is similar to working with a generic collection library. Typically, any object can be put into a collection. Getting an object out of a generic collection is more difficult because of the necessity of casting the object to the correct type. The reason this problem arises is that the collection classes are generic containers with no build-in type safety. Ideally, developers would like to have both a type-safe and a generic interface available.

[6] For those not familiar with the terms "marshal" and "unmarshal", it's simply the act of converting a stream (sequence of bytes) of data to and from an Object. The act of "marshalling" consists of converting an Object to a stream, and "unmarshalling" from a stream to an Object. The concept is similar to the notion of serialization/de-serialization in Java.

 

[7] XML has gained popularity as a platform- and language-independent format for transferring data across systems. In situations where you need to transfer binary data within an XML document, you must encode that binary data into an acceptable character format. Base64 is the encoding scheme defined by MIME, and it's designed to be robust across all the transformations that a message can experience in traversing the Internet.


 [jp1]Do spell check with US dictionary not British