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).
2. The Message Creation Process
3.1. Field ConstraintsField Constraints
3.2. Fixed and Default Field Values
3.5. ‘Send’ Subject Specification
5.4.1. CustomTags
and Attribute Names
10. Appendix A: Supported Field Types
11. Appendix B: Supported Message Formats
12. Appendix C: Sample Message Specification
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.
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
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.
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.
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.
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>
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
}
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>
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.
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>
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.
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).
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.
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.
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.
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]
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
}
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
}
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
}
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.
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).
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.
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.
The code-generator creates one Java class per message specification.
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
}
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.
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
}
}
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
}
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.
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.
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.
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. |
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:
Any bean that satisfies these requirements can be used within the framework and can be marshaled/unmarshalled using the framework converter classes.
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 |
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
[2] See http://java.sun.com/products/jms for details.
[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.
[4] See http://www.w3.org/XML/Schema for details.
[5] See http://www.tibco.com/products/extensibility/solutions/turbo_xml.html for details.
[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.
[8] See http://java.sun.com/xml/jaxb/ for details.
[9] See http://castor.exolab.org/index.html for details.
[10] See http://zeus.enhydra.org/project/aboutProject/index.html for details.
[jp1]Do
spell check with