Monday, July 25, 2011

Everest Framework 1.0 Formatter Changes


Well, it's been awhile since I've posted anything on this blog and for good reason. I've been a busy little bee working on several projects and one of these is the all important rush to get Everest 1.0 ready for production. Seeing as I have to wait for 5,200 unit tests to finish, I thought I'd take some time to blog about Everest 1.0.

Everest 1.0 introduces many new features, the biggest being jEverest (a Java version of Everest), which I might blog about in the future (I might just make you wait, depends on the mood) but not today. Today's post is about the exciting world of Formatters in Everest 1.0 ... yay! I've never been happy with the way that formatting messages has been handled in Everest and when it came time to implement them in Java, I decided an overhaul was needed (don't worry, I made sure it was backwards compatible). First, let's create a simple message to illustrate the formatting process:

static
IGraphable CreateASimpleMessage()

{
   return new MCCI_IN000002CA(
      Guid.NewGuid(),

      DateTime.Now,

      ResponseMode.Immediate,

      MCCI_IN000002CA.GetInteractionId(),
      MCCI_IN000002CA.GetProfileId(),
      ProcessingID.Production,
      AcknowledgementCondition.Always);
}

I've chosen a general acknowledgement for the sample as you can tell :) Anyways, in Everest, we'd traditionally do the following to format the message and get the formatter details:

static void FormatAMessage()
{
    var fmtr = new MARC.Everest.Formatters.XML.ITS1.Formatter()
                  { ValidateConformance = false };
    fmtr.GraphAides.Add(typeof(MARC.Everest.Formatters.XML.Datatypes.R1.Formatter));
    fmtr.GraphObject(Console.OpenStandardOutput(), CreateASimpleMessage());

    // Output details
    foreach (var dtl in fmtr.Details)
      if (dtl.Type == MARC.Everest.Connectors.ResultDetailType.Error)
         Console.WriteLine("Error: {0}", dtl.Message);
}


The process should look familiar if you've used Everest before. We setup the formatter, turn off validation (I know that the message I create is invalid), and graph to the console. Then I can iterate over the Details array in the Formatter object and get the problems encountered while formatting.

While this works, it does present some problems:

  1. Details[] is attached to the instance of Formatter, which means that the formatter cannot be shared across threads or concurrent functions as they will attempt to write to the same array
  2. Anytime we want to reuse the formatter we have to call .Clone() to copy our settings over (or create one of those *gag* factories and call NewInstance())
So, I fixed the design issue in Everest 1.0. I liked the manner that we send/receive messages using the ISendResult and IReceiveResult interfaces in Connectors so I decided to model the formatters in the same way. We now have IFormatterGraphResult and IFormatterParseResult which can be used to share a formatter and call Graph() or Parse() on different threads. Here is an example of how this used:

static void FormatAMessageInACleanWay()
{
    var fmtr = new MARC.Everest.Formatters.XML.ITS1.Formatter()
         { ValidateConformance = false };
    fmtr.GraphAides.Add(typeof(MARC.Everest.Formatters.XML.Datatypes.R1.Formatter));

    // Now we get an IFormatterGraphResult from Graph()
    IFormatterGraphResult result = fmtr.Graph(Console.OpenStandardOutput(), CreateASimpleMessage());

    // Output details
    foreach(var dtl in result.Details)
       if (dtl.Type == MARC.Everest.Connectors.ResultDetailType.Error)
           Console.WriteLine("Error: {0}", dtl.Message);
}

Much better. I recommend using the new method of Formatting in Everest whenever you can as it is cleaner and we'll probably remove the GraphObject/ParseObject methods in future versions of Everest (not immediately though).

Also, you don't have to worry about all your Everest code not working because of this change. GraphObject and ParseObject are wrappers for this new pattern and are completely backwards compatible (at least that's what about 3,000 unit tests are telling me).

No comments:

Post a Comment