Wednesday, January 25, 2012

log4Net: RollingFileAppender Class

What is log4net?
Apache log4net from their website: "The Apache log4net library is a tool to help the programmer output log statements to a variety of output targets. log4net is a port of the excellent Apache log4j™ framework to the Microsoft® .NET runtime."

What is the RollingFileAppender?
RollingFileAppender exists within the log4net in the log4net.Appender namespace. According to the website it's designed to append "log files based on size or date or both.". A pretty basic description, but it does describe what this logging type does: it appends we log messages to an existing log (or creates a new one if none exists), and when it reaches the roll condition it creates a new file.

The conditions under which a roll condition are reached are configured via the RollingStyle  property. You can set the log to role based upon a date, file size, or a combination of the two (known as a composite in the documentation).

Configuration for the RollingFileAppender:
Configuration for the RollingFileAppender is primarily done in an xml configuration file, or your application configuration file.

The good part of a configuration based approach is the ability to customize your logging based upon your life-cycle environment (think QA logging and production logging), or client your individual customer or client's needs.

Log4net also supports run time (or in-code) configuration as well. If your interested in this type of configuration, you can get started here: http://logging.apache.org/log4net/release/manual/configuration.html


In this example, the RollingFileAppender is configured to write the a file named log.txt, with the rolling condition of size, and specifies the maximum file size. One important thing to note is the   in the example below. This will cause log4net to append a count number to the end of the log file as it creates new ones (Example: log.txt.1, log.txt.2, log.txt.3)


<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"></appender><br />
    <file value="log.txt"></file><br />
    <appendtofile value="true"></appendtofile><br />
    <rollingstyle value="Size"></rollingstyle><br />
    <maxsizerollbackups value="10"></maxsizerollbackups><br />
    <maximumfilesize value="100KB"></maximumfilesize><br />
    <staticlogfilename value="true"></staticlogfilename><br />
    <layout type="log4net.Layout.PatternLayout"></layout><br />
        <conversionpattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"></conversionpattern>

   


This example is a little different. It's rolling is determined by the date setting. Note the datePattern used to control the format of the date for file names:

<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="logfile">
    <appendtofile value="true">
    <rollingstyle value="Composite">
    <datepattern value="yyyyMMdd">
    <maxsizerollbackups value="10">
    <maximumfilesize value="1MB">
    <layout type="log4net.Layout.PatternLayout">
        <conversionpattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline">
    </conversionpattern></layout>
</maximumfilesize></maxsizerollbackups></datepattern></rollingstyle></appendtofile></file></appender>


This represents a typical rolling date configuration section for log4net (thanks to dommer):

<log4net>
    <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
        <file value="c:\Logs\Today.log"/>
        <rollingStyle value="Date"/>
        <datePattern value="yyyyMMdd"/>
        <appendToFile value="true"/>
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%level %logger %date{ISO8601} - %message%newline"/>
        </layout>
    </appender>
    <root>
        <!-- Options are "ALL", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "OFF". -->
        <level value="ERROR"/>
        <appender-ref ref="RollingFile"/>
    </root>
</log4net>

Consider your larger logging strategy with RollingFileAppender
Every single application should generate log entries. The world is an imperfect place, and the log at a minimum should tell you about those imperfections. However, depending on your logging "enthusiasm" and logging level you are going to write messages. Even a small application can generate a tremendous amount of logging data.

RollingFileAppender can help with that by:

  1. Naturally segmenting your data into smaller files (making a potential search later on) easier
  2. If you use the date RollingStyle then you will have files seperated by day
  3. RollingFileAppender can also manage the destruction of older log files with the maxSizeRollBackups. This will allow you to set the number of archive files to keep online.
If you combine these features with a strategy to potentially compress, and store files (or destroy them with maxSizeRollBackups) then you can have a self-managed and effective strategy to track messages.


Notes:
... RollingFileAppender full name is log4net.Appender.RollingFileAppender

... Build this into a larger logging and archiving strategy.

... Is log4Net's RollingFileAppender class any better than any other logging libraries rolling file appender? Just is still out.

Resources:
log4net
RollingFileAppender Class
log4net Manual Configuration
log4net Tutorial

Tuesday, January 24, 2012

Adding CDATA - (Unparsed) Character Data to XElement

When working with the System.Linq.Xml namespace, and creating an XDocument you may need to create create document data that  that should not be parsed by the XML parser. For example: data with XML reserved characters, xml content, html content (or really anything you do not want to be parsed).

Creating data within a CDATA tag is simple, simply wrap the content within the XElement in an additional object called XCData.
XDocument contactsDoc =
   new XDocument(
      new XDeclaration("1.0", "utf-8", "yes"),
      new XComment("LINQ to XML Contacts XML Example"),
      new XProcessingInstruction("MyApp", "123-44-4444"),
      new XElement("contacts",
         new XElement("contact",
            new XElement("name", "Patrick Hines"), 
            new XElement("comment", new XCData("ALERT! Do not call!")),
            new XElement("address",
               new XElement("street1", "123 Main St"),
               new XElement("city", "Mercer Island"),
               new XElement("state", "WA"),
               new XElement("postal", "68042")
            )
         )
      )
   );
Sample adapted from .NET Language-Integrated Query for XML Data 
References:
CDATA
.NET Language-Integrated Query for XML Data
XCData Class

Monday, January 23, 2012

Check -> Argument -> Condition: .NET 4.0 Code Contract, CuttingEdge.Conditions, Shrinkr Check


An early part of designing an effective, maintainable system is consistency. The more consistent your applications responses, the better it will be in the long term. One place that breeds inconsistency in parameter validation, and exception throwing.

So I set out to find a condition checking library capable of accomplishing a few simple goals:

  • Must provide a simple interface for validating conditions (object and state)
  • Must have a consistent response algorithm or structure
  • Must be written in .NET 
  • Must be open-source

With the additional side-goals (or bonus points):

  • A fluent interface
  • Documentation or code samples
  • NuGet package
There are plenty of libraries and source-code examples out there that accomplish these goals:

  • .NET 4.0 Code Contracts - Code Contracts provide a language-agnostic way to express coding assumptions in .NET programs.. The Common Language Runtime (CLR) team is introducing a library to allow programming with contracts in the Microsoft .NET Framework 4. 
  • Shrinkr's Check -  "a Url Shortening Service which demonstrates some of the best practices in developing real life web applications." and contains a fluent checking class structure.
  • CuttingEdge.Conditions - CuttingEdge.Conditions is a library that helps developers to write pre- and postcondition validations in their C# 3.0 and VB.NET 9.0 code base. 
  • GuardQ - validating function arguments.
  • Fluent Validation - "A small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules for your business objects."
  • FluentValidation for .NET 2.0 - Rework of the popular FluentValidation library to work in .NET 2.0. There is one minor syntax change and the LinqBridge (lambda syntax for 2.0) library is required, so please read the docs. Namespaces and licenses are similar to FluentValidation.
Here are a few:
CuttingEdge.Conditions

CuttingEdge. Condition Validation Extensions
Conditions is an open-source project (available on CodePlex) that "CuttingEdge.Conditions is a library that helps developers to write pre- and postcondition validations". The library supports a wide-variety of conditions (including those great string based IsNotNullOrEmpty). On the right is a screenshot of the condition validation extensions if you want a quick look.

The library provides a consist way to manage condition checking, and even provides a way to pass a formatted string for the exception message:
Condition.Requires(context, "id").IsNotNull("Parameter {0} cannot be a null reference");

Another feature that this library provides is the WithExceptionOnFailure (AlternativeExceptionCondition WithExceptionOnFailure() where TException : Exception) allowing you to define the exception to be raised when the condition fails. While this is not something you would generally want to do, the added flexibility is certainly appreciated.

.NET 4.0 Code Contracts

.NET Code Contracts is part of the 4.0 version of the .NET framework. From MSDN: "Code contracts provide a way to specify preconditions, postconditions, and object invariants in your code. Preconditions are requirements that must be met when entering a method or property. Postconditions describe expectations at the time the method or property code exits. Object invariants describe the expected state for a class that is in a good state."

It seems intuitive, and easy.

The downside? It requires your application run on .NET 4.0. If your already ugpraded, this seems like a great choice. I mean, it's built into the Framework (no external dependencies ++), and generally offers a comparable offering to other open-source implementations designed to provide this functionality.

Compared side-to-side with Conditions, the syntax and code appear very similar:

            Contract -> Contract.Requires(context != null, "Parameter context cannot be a null reference");
            Condition-> Condition.Requires(context, "context").IsNotNull("Parameter {0} cannot be a null reference");


It's slightly more flexible in it's implementation than Condition. Most notably because Condition has a default set of validations, and exceptions, and Contract is wide open.

As an additional bonus, Code Contracts offer Legacy Requires Statements. From their description it is:

Most code already contains some parameter validation in the form of if-then-throw code. The contract tools recognize if-then-throw statements as preconditions when the statements appear first inside a method, and the entire set of such statements is followed by an explicit Contract method call, such as a Requires, Ensures, EnsuresOnThrow, or EndContractBlock.
When if-then-throw statements appear in this form, the contract tools recognize them as legacy-require statements. The EndContractBlock form is used only if no other contracts follow the if-then-throw sequences, but they should still be tagged as legacy-requires.
Shrinkr's Check.Argument
Shrinkr is "a Url Shortening Service which demonstrates some of the best practices in developing real life web applications." and contains a fluent checking class structure. It's simple, clean and provides a way to validate arguments and provide predicable exception handling.

Here's an example of the Check class. I have also seen a version of this where the Check, Argument classes are not static (from another location):
public static class Check
{
    public static class Argument
    {
        public static void IsNotNull(object parameter, 
                                     string parameterName)
        { ... }

        public static void IsNotNullOrEmpty(string parameter, 
                                            string parameterName)
        { ... }

 .... etc ....
}
This provides the basic context that Conditions provides, but is a far simpler implementation. It's also more of a larger project and really out there as a stand-alone library. However, it does meet the goals above, and would be an easy addition to any enterprise's library.

End-Notes:
... I still like Conditions the best, but only slightly. Mostly it's the little things, like formatted messages (so I can extract it to a resource) or the validations already built and ready to go.

... There are plenty of options to choose from. Pick one and start using it. Almost all of these will produce better, more consistent results than role-your-own. If your code does not validate add this and get started.

... This is an easy upgrade to your library that will produce good results in the short, middle and long term.

Resources:
Shrinkr
CuttingEdge.Conditions
CuttingEdge.Conditions (NuGet)
NuGet
Fluent Validation
.NET 4.0 Code Contracts on MSDN Magazine
DevLabs Code Contracts
Fluent Validation
FluentValidation for .NET 2.0
GuardQ

Friday, January 20, 2012

Amazon DynamoDB: First Impressions

An e-mail arrived this morning from Amazon Web Services (Aws):

It was a new feature announcement from Aws.

The new product is DynamoDB... a new NoSQL database engine for Amazon Web Services (Aws)

From the e-mail, it's described as "Amazon DynamoDB, a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability".

It has some interesting key points:
  • Data on Solid State Drives (SSDs)
  • Replicated data synchronously across multiple zones.
  • Supports two types of primary keys: Hash Type and Hash and Range Type 
  • JSON for data transport
This new product is a NoSQL database engine built into their cloud infrastructure (similar to SimpleDB as a product, but with some notable changes and upgrades).

From CTO Werner Vogels' DynamoDB blog post he states the differences between SimpleDB and DynamoDB: "While SimpleDB has been successful and powers the applications of many customers, it has some limitations that customers have consistently asked us to address." Listing: Domain scaling limitations, Predictability of Performance, and SimpleDB's Pricing complexity.


From the documentation, it appears to be similar to their existing  SimpleDB offering, but is designed for large scale needs (and predictable) performance. Most notably no limits or restrictions on database size, and automatic management of scalability across multiple servers.


What is NoSQL? 
According to Wikipedia's  NoSQL article:
In computingNoSQL (sometimes expanded to "not only SQL") is a broad class of database management systems that differ from the classic model of the relational database management system (RDBMS) in some significant ways, most important being they do not use SQL as their query language.
DynamoDB Storage Model (1000 ft view)
From their documentation, each object (or item) is stored within a table, with the individual values within the item stored as attributes:
From Amazon DynamoDB (beta)
Getting Started
In order to get started with DynamoDB, you have to make sure you have a couple of things:
  • Amazon Web Services developer account
  • Your AWS account has to be set up to use Amazon DynamoDB
  • Your AWS Access and Secret Key (in order to run the examples, or start your development project)
DynamoDB and .NET
The AWS SDK for .NET has been updated to contain samples for using DynamoDB with the .NET framework (fair warning if you have a previous version of the SDK you may have to remove it to install this one).

The SDK contains (basically) 4 samples:

  • Data Model Sample - creation of a context, persistence of items using the context, and some queries against the context.
  • Data Model Schema - Samples of objects that can be stored within DynamoDB. Specific would be the use attributes: DynamoDBTable that "Specifies that this object can be stored in DynamoDB". DynamoDBHashKey, DynamoDBProperty, and DynamoDBRangeKey to name a few.
  • Document Model Sample - Demonstrates the document model. Shows creation of a table, and adding documents to a table, persistence, and querying from the model.
  • Table Operations - Basic table operations: Contains, CreateTable, table status, table deletes.

These samples are available for VS 2008 and VS 2010. With my installation, the samples were located at C:\Program Files (x86)\AWS SDK for .NET\Samples\AmazonDynamoDB_Sample.

Pricing (as of 1/19/2012)
Notes About Amazon DynamoDB states it pretty well "pricing is based on actual write/read operations and not API calls (e.g. a query returning 100 results accounts for 100 ops and not 1 op)".


You pay a flat, hourly rate based on the capacity you reserve:
Throughput Capacity
Write Throughput: $0.01 per hour for every 10 units of Write Capacity
Read Throughput: $0.01 per hour for every 50 units of Read Capacity

The listed pricing (as of 1/19/2012) offers a free-pricing tier as well:
Free Tier*
As part of AWS’s Free Usage Tier, AWS customers can get started with Amazon DynamoDB for free. DynamoDB customers get 100 MB of free storage, as well 5 writes/second and 10 reads/second of ongoing throughput capacity.
Final Thoughts:
... What's missing: From CTO Werner Vogels' DynamoDB blog post one comment from Faraz points out one of the key components missing from DynamoDB's initial offering: "missing critical database pillar of snapshot backup and fast recovery system". Vogel's response: "we have a philosophy of launching with a minimal feature set and then quickly iterating while prioritizing based on customer feedback. Backup/Restore for DynamoDB will have high priority". While true, I'm still waiting for micro in VPC!

... "pure local emulation is not available".  However, there is an open-source project to provide this emulation for SimpleDB: fakesdb (a fake version of Amazon's SimpleDB for local/integration testing).

... JSON is used for sending data and for responses, but it is not used as the native storage schema (from
Notes About Amazon DynamoDB).

... DynamoDB vs. Cassandra

Thursday, January 19, 2012

StructureMap Initialize (or Configure) with ChannelFactory

Trying to configure StructureMap to use the ServiceModel ChannelFactory should be pretty easy right? Unless.. it just isn't working.

The channel factory creates proxies, which StructureMap has a problem with. For instance, if you tried to create an instance of the proxy, and build the mapping:
       StructureMap.ObjectFactory.Initialize  
         (  
           z =  
           {  
             //service  
             var basicHttpBinding = new BasicHttpBinding();  
             var endpointAddress = new EndpointAddress("http://localhost:62049/SendMessage.svc");  
             var channelFactory = new ChannelFactory(basicHttpBinding, endpointAddress);  
             var proxy = channelFactory.CreateChannel();  
             z.For().Use(proxy);  
           }  
         );  

You would receive a StructureMapConfigurationException:
StructureMap configuration failures:
Error:  104
Source:  Registry:  StructureMap.Configuration.DSL.Registry, StructureMap, Version=2.6.3.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
Type Instance 'edc26816-1ec5-4f0d-b401-b4504e8fb537' (Object:  Instawares.Correspondence.Contract.ServiceContract.ISendMessage) cannot be plugged into type Instawares.Correspondence.Contract.ServiceContract.ISendMessage, Instawares.Correspondence.Contract, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
-----------------------------------------------------------------------------------------------
The resolution is much simpler, and provides more flexibility which is to allow StructureMap to handle creation of the object using the LambdaInstance Use(Func func):


 StructureMap.Configuration.DSL.Expressions.CreatePluginFamilyExpression
The function will handle the creation of the service proxy (and you can control the lifecycle of the object via the configuration as well).

Here's an example:

               StructureMap.ObjectFactory.Initialize
                (
                    z =
                    {
                        //service
                        var basicHttpBinding = new BasicHttpBinding();
                        var endpointAddress = new EndpointAddress("http://localhost:62049/SendMessage.svc");
                        var channelFactory = new ChannelFactory(basicHttpBinding, endpointAddress);
                        z.For().Use(channelFactory.CreateChannel);
                    }
                );

The object creation has been been transferred to the ChannelFactory, and StructureMap handles the creation of the proxy, and the life cycle of that proxy.

Resources
StructureMap
ChannelFactory(Of TChannel) Class

Tuesday, January 17, 2012

Best Practices for a .NET Extension or Plug-In


These are best practices that I created for writing a .NET Extension or Plug-In so some of the references and nomenclature are .NET specific, but the principals and ideas can be extended to any programming language or application design.

Best Practices for Writing a Good Extension or Plug-In:
  1. Any extension should have logging features (and ideally support verbose, error, warning messages). Most applications provide some sort of logging capabilities you can use or extend. This is preferred to designing your own simply because you would have to monitor two sets of logs not to mention the additional configuration, and maintenance of adding a second logging mechanism. If you do have to add your own logging, try to leverage an existing logging infrastructure: log4net, Microsoft Enterprise Library.
  2. If possible avoid any additional (and most importantly external) database or service calls. Aside from the performance hit this can add, your introducing potentially volatile resources that will affect the host systems ability to work properly.
  3. Settle on a single purpose or function for your extension. Don't try to make it do several things, and work in several different functions. It's hard to understand, maintain, and a nightmare when 'something' inside the extension/plug in goes bad.
  4. Limit external dependencies. If at all possible you want your extension/plug-in to be entirely self-sufficient. Any information or exchange should be provided by the host system via a state bag, or in the direct actionable items available.
  5. Document as much of the plug-in as possible within the code module. Include references, and be explicit. You should always document, but people tend to forget about extensions and plug-ins. You write them, they get deployed, and then days, months, years later... doom. The more that's in the code when you open it back up the quicker you can fix it.
  6. Handle and dispatch exceptions in almost all cases. If you can't handle the exception, and it needs to be raised to the host system, then make sure your logging (see #1 on this list) records the exception with as much information as possible. In my experience a host system most often reports very little useful information or (as in one case I remember) it reports absolutely nothing.
  7. Design the functionality of the plug in! So many times, people try to write everything inline for a plug-in rather than following the same coding practices that they would in any other application or feature. 
  8. Favor robust naming if your plug-in and if possible the methods, functions, and variables within it. Often when dealing with a plug-in it needs to be self explanitory to the infastructure dude, the end-user, and in almost all cases other people.
    1. Here's a quick example: 
      1. MyCompanyNamePlugIn - SeriouslyThat does that do? 
      2. MyCompanyUpdateCreatedDateOnNewRecordPlugIn - Ok, really simple. Want to guess?
  9. Consider interprocess communication if execution is lengthy, intensive, or will impact the performance of the host system. If the action is lengthy or is going to impact the host system, then consider having your plug-in use a queue or call a service for executing the action. If performance is going to be impacted in an extreme negative, consider external executing with a service-bus or queuing system.
References:
Best Practices for Developing Your First Ektron Site
Best Practices in Plugin Development (WordCamp Seattle)

Resources:
log4net
Microsoft Enterprise Library.

Monday, January 16, 2012

JsRender: Javascript Templating. An Introduction.

What is JsRender?
According to the demos index page JsRender is "Optimized for high-performance pure string-based rendering, without DOM or jQuery dependency".

In simple terms it's a way to apply templates to JSon data and render out content.  If you think about it, it's really a powerful feature, and valuable to anyone that is rendering content in a web browser.

It seems that JsRender became the solution for client side templating with the discontinuation of JQuery's templates, however JsRender is something... better. Aside from having no DOM depedency, it does not even have a JQuery dependency. From Borris Moore's presentation, JsRender is "~20 times faster than jquery.tmpl for simple templates".

At a high-level is supports:
Rendering templates and data into a string (as opposed to anything in the DOM)
Templates as strings, and within script tags
Template registration
Tags within templates, including conditional statements, loops

Here's a basic sample/example of JsRender in action: 
From http://borismoore.github.com/jsrender/demos/step-by-step/01_inserting-data.html












Requires Script references: jQuery, jsrender
Borris Moore offers a complete set of demos, which demonstrate much more: jsrender Demos.

What's JsRender's Future?

As a product, Borris Moore offered the following road-map for JsRender/JsView on OCTOBER 12, 2011 in this blog post: jQuery Templates and JsViews: The Roadmap

Roadmap summary:
  • jQuery templates
  • : Will remain at Beta1, and be superseded by JsRender templates, and JsViews.
  • JsRender
  • : Soon move to Beta – then on to V1
  • jQueryUI plan to use JsRender. (TBD whether it will migrate to jQuery project in GitHub...)
  • JsViews
  • : Move to Beta (after JsRender) and then on to V1 …
  • May also be used by jQueryUI

My Experiences

Most recently I've been using JsRender to render content from Ektron after designing a series of templates that would render content as JSon (see Designing Data Templates for Content Extraction with Ektron (JSon, XML) (Ektron Designing Content Delivery: Part 2) for more information).

In the past, I've also used it for rendering forms, pages, static content, large tables, dynamically loaded sections of my application. What can I say, it's easy, flexible and it works.

Resources:
jsrender on GitHub
jsrender Demos
JQuery Templates
Borris Moore "JsViews: Next-generation jQuery Templates and Data Link" Presentation
Designing Data Templates for Content Extraction with Ektron (JSon, XML) (Ektron Designing Content Delivery: Part 2

Friday, January 13, 2012

Determine your Ektron Version

Determing your Ektron product version should be simple

Within the website you can view a series of keys within the Web.config file that determines the product version:

<add key="ek_InstallBuild" value="8.0.2.035SP2.01"/>
<add key="ek_ServicePack" value="SP2"/>
<add key="ek_version" value="8.02"/>

You should also note that the Ektron version is available for the end-user to view it within the Ektron work area in the top left hand-corner (see screenshot below):

After searching, it does not appear that the product version is stored within the database for Ektron. There are version columns but this appears to be for versioning data within the application and not for determining the database (or Ektron product) version. This article on the forum seems to back-up these findings: Database Version(2).

Resources:
Database Version(2)

C# JSon Pretty-Print (A simple C# JSon Presentation Formatter)

When working on a recent project, I ran across a need to have the user paste into a textbox (in a WPF application) a snippet of  unformatted  JSon data. The data was then used with a template to render content for them to view. The hard part... the unformatted JSon data was almost impossible to read (let alone do anything with).

A quick search around on Google, and I couldn't find what I thought was pretty basic function in C#. I mean, someone, somewhere else in the world needed to be able to do this. The Javascript serializer would work if I had an object to start with, but in my case, there wasn't an object just a string. So I wrote a basic formatter for presentation of JSon data. It's not all that impressive, but it works, and creates presentable data that the user can view and work with.

I also added a quick method as well that would convert an object to a string, and then apply the same formatting algorithm in-case you have a need to present both and want to have the same presentation.

So instead of this:



You get this:












The Code:
        public class JSonPresentationFormatter
        {
            public string Format(object obj)
            {
                var serializer = new JavaScriptSerializer();
                var text = serializer.Serialize(obj);

                return Format(text);
            }

            public string Format(string text)
            {
                if (string.IsNullOrEmpty(text)) return string.Empty;
                text = text.Replace(System.Environment.NewLine, string.Empty).Replace("\t", string.Empty);

                var offset = 0;
                var output = new StringBuilder();
                Action tabs = (sb, pos) => { for (var i = 0; i &lt; pos; i++) { sb.Append("\t"); } };
                Func> previousNotEmpty = (s, i) =>
                {
                    if (string.IsNullOrEmpty(s) || i &lt;= 0) return null;

                    Nullable prev = null;

                    while (i > 0 && prev == null)
                    {
                        prev = s[i - 1];
                        if (prev.ToString() == " ") prev = null;
                        i--;
                    }

                    return prev;
                };
                Func> nextNotEmpty = (s, i) =>
                {
                    if (string.IsNullOrEmpty(s) || i >= (s.Length - 1)) return null;

                    Nullable next = null;
                    i++;

                    while (i &lt; (s.Length - 1) && next == null)
                    {
                        next = s[i++];
                        if (next.ToString() == " ") next = null;
                    }

                    return next;
                };

                for (var i = 0; i &lt; text.Length; i++)
                {
                    var chr = text[i];

                    if (chr.ToString() == "{")
                    {
                        offset++;
                        output.Append(chr);
                        output.Append(System.Environment.NewLine);
                        tabs(output, offset);
                    }
                    else if (chr.ToString() == "}")
                    {
                        offset--;
                        output.Append(System.Environment.NewLine);
                        tabs(output, offset);
                        output.Append(chr);

                    }
                    else if (chr.ToString() == ",")
                    {
                        output.Append(chr);
                        output.Append(System.Environment.NewLine);
                        tabs(output, offset);
                    }
                    else if (chr.ToString() == "[")
                    {
                        output.Append(chr);

                        var next = nextNotEmpty(text, i);

                        if (next != null && next.ToString() != "]")
                        {
                            offset++;
                            output.Append(System.Environment.NewLine);
                            tabs(output, offset);
                        }
                    }
                    else if (chr.ToString() == "]")
                    {
                        var prev = previousNotEmpty(text, i);

                        if (prev != null && prev.ToString() != "[")
                        {
                            offset--;
                            output.Append(System.Environment.NewLine);
                            tabs(output, offset);
                        }

                        output.Append(chr);
                    }
                    else
                        output.Append(chr);
                }

                return output.ToString().Trim();
            }
        }
You can download a copy of the source here: JSonPresentationFormatter
Example:
            //From object
            var employees = new[]
            {
                 new {Name= "John Vansteen",ID="1", Age="35"}
                ,new {Name = "Joe Yankin", ID = "2", Age = "44" }
                ,new {Name = "Larry Yon", ID = "3", Age = "33" }
            };
            Console.Write(new JSonPresentationFormatter().Format(employees));

            //From string
            var jSonAsString = "{\"glossary\": {\"title\": \"example glossary\",\"GlossDiv\": {\"title\": \"S\",\"GlossList\": {\"GlossEntry\": {\"ID\": \"SGML\",\"SortAs\": \"SGML\",\"GlossTerm\": \"Standard Generalized Markup Language\",\"Acronym\": \"SGML\",\"Abbrev\": \"ISO 8879:1986\",\"GlossDef\": {\"para\": \"A meta-markup language, used to create markup languages such as DocBook.\",\"GlossSeeAlso\": [\"GML\", \"XML\"]},\"GlossSee\": \"markup\"}}}}}";
            Console.Write(new JSonPresentationFormatter().Format(jSonAsString));
Key Words:
JSon C# Presentation Formatter

References:
IFormatProvider Interface

Source:
JSonPresentationFormatter

Wednesday, January 11, 2012

Designing a Generic Meta-Data Driven Transformation Template (Ektron Designing Content Delivery: Part 3)

In the previous Ektron Designing Content Delivery post (Designing Data Templates for Content Extraction with Ektron (JSon, XML) (Ektron Designing Content Delivery: Part 2)), I outlined the basics of creating a series of templates for data extraction.

While using Ektron content as data is useful (in particular SmartForm data), there is always a need to render transformed content (as Html, Text, Csv, or other formatted content). In this post, I will outline a method to create a simple Meta-Data driven Ektron template that can use used to transform your data for presentation.

What this is useful for:
  • SmartForm content that needs to be displayed as Html
  • SmartForm content that needs to be transformed (to other Xml, Html, Text, Csv, etc)
  • Any Ektron content that requires transformation before being rendered, and represents stand-alone content.
A high level overview of the rendering process:


Of note: I've also created an Ektron Widget control that follows the same pattern, so that I can render out this content as part of a page-builder page without any additional work by the end user. It pretty much works the same way with the template being a page-builder widget, and the end consumption being a Page-builder page.

Transformation Template:
System/Design Requirements:
1. Support Multiple Xsl/Xslt translations against the content set
2. When not configured properly then display a simple message back for the rendered content.
3. Support different separators/delimiters within the metadata in-case there is a conflict and it needs to be changed or updated.

ASPX:
       
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="MetaDataTransformation.aspx.cs" Inherits="ContentManagement.Site.Shared.Template.Content.SmartForm.MetaDataTransformation" %>
<%@ Register Assembly="Ektron.Cms.Controls" Namespace="Ektron.Cms.Controls" TagPrefix="CMS" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <CMS:ContentBlock ID="ContentBlock1" runat="server" />
    </div>
    </form>
</body>
</html>
       
 

Code-Behind:
using System;
using ContentManagement.Ektron.Content;
using ContentManagement.Ektron.Transformation.Xsl;
using ContentManagement.Site.Shared.Pages;
using MetaData = Ektron.Cms.API.Metadata;
using ContentManagement.Ektron.Transformation;

namespace ContentManagement.Site.Shared.Template.Content.SmartForm
{
    public partial class MetaDataTransformation : AbstractCmsPage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            this.ContentBlock1.SetDynamicParameterToIdAndFill(); //prep content block

            Response.Clear();

            WriteCmsDataToHeader(Response);
            Response.ContentType = ContentManagement.Web.ContentTypeDefinition.Html;

            var id = this.ContentBlock1.EkItem.Id; //retrieve the id from the item

var metaData = this.ContentBlock1.GetMetaData().GetItemByName("Transformation"); //Note: This should be content/error content from Ektron. Action badRender = () => Response.Write("Oppps. It looks like this content isn't ready yet. Try back again soon."); if (metaData == null) //meta data not located on this item, so we do not want it to render. badRender(); else { var data = new MetaData(); var metaDataType = data.GetMetadataType(long.Parse(metaData.ID)); var xsltTransformationFromMetaData = this.ContentBlock1.GetMetaData().GetItemByName("Transformation").Value.ToString().Split(new string[1] { metaDataType.Separater }, StringSplitOptions.RemoveEmptyEntries); var transform = XslTransformFactory.Get(); var input = this.ContentBlock1.EkItem.Html; if (xsltTransformationFromMetaData.Length <= 0) //no content, write out a simple error message. badRender(); else if (xsltTransformationFromMetaData.Length == 1) { //apply single transformation, and output directly to response stream transform.Transform( new XmlStringInput(input) , new XslFileInput(TransformationPath.MapPathAndValidate(this, xsltTransformationFromMetaData[0])) , Response.OutputStream , Response.Output.Encoding); } else { //apply multiple translations xsltTransformationFromMetaData.ForEach(trns => input = transform.Transform(new XmlStringInput(input), new XslFileInput(TransformationPath.MapPathAndValidate(this, trns)))); } } Response.End(); } } }


The steps for setup and configuration are pretty simple:
Ektron Configuration
  • Creation of the Meta-Data within Ektron
  • Sample Metadata Transformation within Ektron 8.0.2
  • Apply the Metadata to the Content Folder(s)
  • Metadata applied to the content folder
  • Add Meta-Data Driven Transformation Template to your content
Content Configuration
  • Add the MetaData to the Content
  • Meta Data Transformation on Content (Note: This has two transformations)
  • Set the Template for the Content to the Transformation Template
  • Add the correct meta data transformation template.


Notes
If your planning on using this with any type of volume you will want to cache the output of the transformation.

It's important to note that I am not referring to adding a custom style sheet to an Ektron web control (which you can do, and is extremely flexible) in particular this template will retrieve Ektron content, apply transformations and then write the result of the transformation to the response output. The application (and usage) of this for consumption of Ektron content is quite different. If your developing pages within Ektron, and want to transform the output of the control, using Ektron's own transformation infrastructure is a much better option. Here, I am transforming, and then providing content via a push/pull to other sites for content presentation (and providing an option to present that content fully rendered).

Q & A
Q: How is this different than adding a Cms control to the page and selecting a transformation? 
A: At the end of the day, it's not all that different, but I wanted a way to make this process completely configuration driven so I can set up additional content at will without having to create a page or a template for each set of content. 

In my next posts
1. Designing a wrapper-API for consumption, caching and rendering of Ektron data.
2. Now look what we can do: Render Ektron client-side with data templates and JsRender (for smoking fast response times).
3. My Ektron page templates

Resources:
Designing an Ektron Content Delivery and Provider Mechanism (Part 1)
Designing Data Templates for Content Extraction with Ektron (JSon, XML) (Ektron Designing Content Delivery: Part 2)
Guest Blogger-Cameron Jordan: No More XSLT?!