Monday, September 24, 2012

Packt Publishing reaches 1000th book milestone

Over the past couple years I have had the chance to work with Packt on two BizTalk titles:
During this time Packt has been very active in producing high quality BizTalk books but that is not all.  They are about to release their 1000th book which is definitely an impressive milestone.  As part of this celebration, Packt has a special offer that they have extended to the public.  Please click on the image below for more information.

BizTalk 2010 R2 CTP: Azure Service Bus Integration–Part 3 Sending message to Service Bus Queue

This is the third post  in a series where I discuss integrating BizTalk 2010 R2 CTP with the Azure Service Bus.  In Part 1 I discussed BizTalk receiving a message from a Service Bus Queue.  In Part 2 I expanded on the scenario that I described in Part 1 but added support for Brokered Messaging Properties.  In this post I am going to switch gears and have BizTalk send messages to a Service Bus Queue.

Scenario
In my previous posts, I have focused on Power Outage scenarios as this is an industry that I am very familiar with.  In Post 2, I discussed situations where we may have critical customers and we want Power Outage incident tickets, when critical customers are involved, to have a higher priority. Whenever you are dealing with Critical Customers you generally have dire circumstances for getting their power restored as quickly as possible.  Whether you are dealing with Hospital,s where people could die, or Oil and Gas operations where being down may result in revenue losses in the hundreds of thousands of dollars per hour (or more).  Often times, Power Delivery organizations will have Major Account Representatives (or MAR for short).  These people are responsible for maintaining business relationships with these types of customers to ensure service levels and expectations are being met.

In Part 2, we left off delivering messages to a Work Order Management system that can dispatch these trouble events to field operations staff to address.  We created two separate paths: one for regular customers and one for critical customers.  The intention of this post is to have the Work Order Management system provide a message back to BizTalk that BizTalk can push to an “Estimated Time of Restore” queue.  A MAR account application can then subscribe to these events.  Having this information at the MAR’s fingertips will allow them to contact the customer to give them the bad, or good, news.

Service Bus Client
  • A new C# console application has been added to our solution from the previous posts called BrokeredMessageFromBizTalk.
image
  • Next we are going to add a class called EstimatedTimeToRestore.  It looks an awful lot like our PowerOut class from our previous scenario with the major difference being the RestoreTime property.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BrokeredMessageFromBizTalk
{
    public class EstimatedTimeToRestore
    {
        public string CustomerName;
        public string PhoneNumber;
        public string Address;
        public DateTime RestoreTime;
    }
}
  • Below is the code that I have placed in Program.cs. I do have a section of code commented out that will allow us to write out an instance of EstimatedTimeToRestore to disk. We will want a copy of this file so that we can use it within BizTalk to generate an XSD schema.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Messaging;

namespace BrokeredMessageFromBizTalk
{
    class Receiver
    {
        const string QueueName = "<queue_name>";
        static string ServiceNamespace = "<your_namespace>";
        static string IssuerName = "owner";
        static string IssuerKey = "<your_key>";

        static void Main(string[] args)
        {

            //Uncomment this code if you want to write an instance of your data class to disk
            //EstimatedTimeToRestore etr = new EstimatedTimeToRestore();
            //etr.Address = "1 Calgary Way";
            //etr.CustomerName = "General Hospital";
            //etr.PhoneNumber = "403-1234567";
            //etr.RestoreTime = DateTime.Now;
            //using (FileStream writer = new FileStream("c:/temp/ETRfile.xml",FileMode.Create, FileAccess.Write))
            //{
            //    DataContractSerializer ser = new DataContractSerializer(typeof(EstimatedTimeToRestore));
            //    ser.WriteObject(writer, etr);
            //}
          
         //Create instance of tokenProvider using our credentials
            TokenProvider tokenProvider = TokenProvider.CreateSharedSecretTokenProvider(
                Receiver.IssuerName, Receiver.IssuerKey);
            Uri uri = ServiceBusEnvironment.CreateServiceUri("sb", Receiver.ServiceNamespace, string.Empty);
            MessagingFactory messagingFactory = MessagingFactory.Create(uri, tokenProvider);
            QueueClient qc =  messagingFactory.CreateQueueClient(Receiver.QueueName, ReceiveMode.ReceiveAndDelete);
            BrokeredMessage bm;
            while ((bm = qc.Receive(new TimeSpan(hours: 0, minutes: 0, seconds: 5))) != null)
            {
                var data = bm.GetBody<EstimatedTimeToRestore>(new DataContractSerializer(typeof(EstimatedTimeToRestore)));
                Console.WriteLine(String.Format("An estimated time of restore {0} has been received for {1}", data.RestoreTime, data.CustomerName));
            }
        }
    }
}
  • Most of this code is nothing that you haven’t seen before as part of SDKs or other Blog posts out there.  The one line that you do need to be aware where we serialize the message that was pulled off of the Queue into an instance of our EstimatedTimeOfResponse class.  This allows us to use a typed response.  You may recall from Post 1 and 2 that we were using this same Serializer when pushing messages to the Queue so that BizTalk will be able to receive typed messages from the Queue.  The process is no different on the receive side.
var data = bm.GetBody<EstimatedTimeToRestore>(new DataContractSerializer(typeof(EstimatedTimeToRestore)));

Creating PowerRestoreQueue
In our previous examples, we had the client application ensure that our Queue existed prior to putting a message in it.  This time around we are going to use the Azure Service Bus Portal to perform this operation.

As of this writing, all Service Bus configuration occurs within the “old” portal.  To add a queue, select your namespace and then click on the New Queue button.  You will then need to provide a name and configure optional properties, if so desired, and then click the OK button.  You will then see the queue has been added successfully.  For the purpose of this example we are going to use a name of powerrestore.

image

Modifying BizTalk Solution
  • Once again we are going to want to generate a typed XSD schema based upon the sample file that was generated by our Queue Client Code.    We can do so by:
    • Right mouse clicking on BizTalk project (PowerOutage) - Add - Add Generated Items.
    • When prompted, click on the Generate Schemas label and then click the Add button.
    • Select Well-Formed XML from the Document type dropdown and then we need to provide the name of our sample file.  Click OK to proceed.
  • We now need to re-deploy our BizTalk application by right mouse clicking on our Solution and clicking Deploy Solution.
  • Within the BizTalk Administration Console we need to now add a Receive Port and Receive location.  There isn’t anything super special about this Receive Location.  We will use the FILE Adapter, the XML Receive Pipeline and will use a local file system URI.  Do make a note of the Receive Port Name as we will use it as a filter in our Send Port that we are about to create.
image
  • Create a new Send Port using the new SB-Messaging Adapter and specify the PassThruTransmit Send pipeline.
image
  • Click the Configure button.  We now need to specify our Destination URL including our Namespace(underlined in green) and the name of our queue (underlined in red).
image
  • Next, click the Authentication tab and modify the Access Control STS URI. We need to provide our namespace (underlined in green).  Be sure to leave the –sb string after the namespace.
image

  • Lastly, we need to create a Filter so that when BizTalk receives a message from our Receive Port, that we previously configured, that we send it to the Azure Service Bus queue.  To do this we need to click on Filters and then select the BTS.ReceivePortName  property while specifying  our Receive Port Value Name.
image

Testing our Solution
In order to test our application, we will focus on the BizTalk aspects first. 
  • We will want to drop our sample XML file, that we previously generated from our Queue client, and drop it in our BizTalk receive location folder:
image
  • BizTalk will now pick up this file and deliver it to our Service Bus Queue.
  • Next, we can start up an instance of our Queue Client.  We will soon discover that our message has been dequeued and that our console application has been updated informing us that an updated Estimated time of restore has been provided for us.
image


Conclusion
Once again, a very seemless experience when taking your existing BizTalk skills and using them to create Cloud or Hybrid solutions.  The only real area that you need to be concerned with is the Serializing of messages that you are putting or pulling from the queue.

This series will continue, I have a few other areas that I plan on exploring related to Azure Service Bus and BizTalk integration. Stay tuned...


Saturday, September 15, 2012

BizTalk 2010 R2 CTP: Azure Service Bus Integration–Part 2 Brokered Message Properties


In my last post I provided a walkthrough that allows you to send a typed Brokered Message from a Console application to a Service Bus Queue,  have BizTalk retrieve this message and then write it to disk.  I am now going to expand upon that scenario and describe how we can leverage Brokered Message properties within BizTalk to route the message to different locations using BizTalk’s promoted properties.
What is a Brokered Message Property?
In many ways a Brokered Message Property is very similar to a Promoted Property within BizTalk.  These properties can be used to capture meta-data outside the body of the message.  We can then use these properties for routing within the Service Bus when delivering messages to different Service Bus Topics. It is important to note that we don’t have to use these properties for just routing.  We can also use them as part of business logic in downstream systems if we so desire.
Why is this important for BizTalk?
As I mentioned in the previous paragraph we can use Promoted Properties within BizTalk to route messages and we can also use it to capture meta data if we want (although you should look at distinguished fields instead if that is your intent).  In the BizTalk 2010 R2 CTP there is now support for transitioning Brokered Messages Properties from Service Bus Queue clients to BizTalk promoted properties.  BizTalk applications themselves do not understand a Brokered Message property, but BizTalk will convert these Brokered Message Properties into BizTalk Promoted Properties where they can be used to route messages.
Scenario Walkthrough
In my previous blog post I used a Power Outage scenario.  My client application would pass along customer information to a Service Bus Queue and then BizTalk would pick that message up and write it to disk.  In a ‘real life’ scenario I would have routed that message to a Customer Information System (CIS) or a Work Order Management (WOM) system so that a field operations team could address the power outage.  In this walkthrough I am going to build upon that scenario.  This difference this time around is that I am going to introduce a Brokered Message Property called isCriticalCustomer.  I hate to publicly admit it but not all customers are treated equally when it comes to delivering power.  An example of a Critical Customer may be a hospital.  It is more important for a Power company to get their power on before yours.  A patient’s respirator is more important that someone watching the latest American Idol episode. 
Within my Console application this isCriticalCustomer property will be set as a Brokered Message Property.  When this message is retrieved by BizTalk this property will be converted into a Promoted Property and BizTalk will then use that Promoted Property to route the message to a different destination.
Note: A person with a lot of Service Bus experience may say why don’t you just use Topics?  I could have a Topic for regular customers and a Topic for Critical Customers.  This is also a valid pattern but for the purposes of demonstrating BizTalk capabilities I will leave the routing to BizTalk.
Modifying Queue Client
I am not going to display all of the code required for this client to work.  I am going to be adopting the code I listed in my previous post.  So please refer back to that post for the starting point.  I will include any areas within this post where I have made changes.
In the code below I am going to create and send two messages.  In red you will discover that I am setting a Brokered Message Property called isCriticalCustomer. In the first message I am indicating that this is not a critical customer (aka a regular customer).  In the second message I am saying that it will be a Critical Customer.  Once we get to the BizTalk section you will see how we can use this property to route the message within BizTalk.
              //Create new instance of PowerOut object
              //This customer will not be a Critical Customer
              PowerOut po = new PowerOut();
              po.CustomerName = "Stephen Harper";
              po.PhoneNumber = "613-123-4567";
              po.Address = "24 Sussex Drive";

              BrokeredMessage message = new BrokeredMessage(po, new DataContractSerializer(typeof(PowerOut)));
              message.Properties.Add("isCriticalCustomer", false);
              myQueueClient.Send(message);

              //Create new instance of PowerOut object
              //This customer will  be a Critical Customer
              po = new PowerOut();
              po.CustomerName = "Calgary General Hospital";
              po.PhoneNumber = "403-123-4567";
              po.Address = "1 Red Mile Drive";

              message = new BrokeredMessage(po, new DataContractSerializer(typeof(PowerOut)));
              message.Properties.Add("isCriticalCustomer", true);
              myQueueClient.Send(message);

BizTalk Modifications
You may recall from my previous post that my BizTalk solution was very simple as I only had a schema that represented this Customer message being sent from my client application.  So in order to support our new scenario I only need to add one more artifact to my solution and that is a Property Schema.  The reason why I need to add this schema is that I need an artifact within my BizTalk application to “hold” these values as they are being populated when BizTalk receives the message.  This is no different than when you want take a value from one of your “regular” BizTalk schemas and turn it into a Promoted Property.
Within our BizTalk solution we need to do the following:
  • Add a PropertySchema to our project.  Once it has been added there will be a default property that we will rename to isCriticalCustomer and change the data type to be a boolean.
image
  • We now need to re-deploy our application.
  • Open up the ReceiveBMfromServiceBus Receive Location, click the Configure button.  Now click on the Properties tab.  Within this tab we are going to specify our namespace for our PropertySchema.  If you are unsure where you can get this namespace from, look in the image above and notice the value of the targetNamespace matches the value that I have put in this text box.  We also need to ensure that the Promote Brokered Message Properties checkbox is checked on.
image
  • Next we are going to remove our previous Send Port and create two new Send Ports.  One send port will be for Regular Customers and the other will be created for Critical Customers. 
  • Below is the Send Port for regular customers.  Notice that a separate sub-folder called RegularCustomers has been created for these files.
image
  • Click on the Filters label and then add a new Property.  You will notice that within the dropdown list you will find the property that we created in our PropertySchema called isCriticalCustomer.  We need to select this value and then set the value to false.
image
Note: When you pull down the Property drop down you will also discover the Out of the Box Service Bus Brokered Message properties.  These properties are out of the scope of this post but it is something that may be beneficial down the road. 
  • We now want to perform similar actions to our other send Port that will be use to send our CriticalCustomer messages.
image
  • Once again we are going to click on the Filters label.  We will use the isCriticalCustomer property again but this time we will set the Value to true.
image
  • We can now bounce any affected Host Instance(s) and start our application.
Testing our Application
As you my recall, we modified our Console application so that it will send two messages to the same PowerOutage queue.  In the first message, we set the isCriticalCustomer Brokered Message property to false.  In the second message, for the hospital, we set it to true.  The end result is that we should receive one message in our Regular Customers folder and one in our Critical Customers folder.
  • As promised when I run the application I will find one message in each corresponding folder:
image
  • If I open the files I will discover that the right message was delivered to the correct folder:
image
Conclusion
Overall it is a pretty slick, and seamless, experience.  I think the BizTalk product team has done a great job in bridging the Service Bus Brokered Messaging Property with BizTalk’s Promoted Property.  In my opinion, the Azure Service Bus and BizTalk Server really complement each other by providing robust Hybrid solutions.  It is great to see smooth interoperability between these two technology sets.
This concludes this blog post.  I am not done yet with this series as I have still just scratched the surface.  I plan on writing more about my experience with Sending messages to Service Bus Queues/Topics from BizTalk and perhaps dive into some different messaging patterns.

Friday, September 14, 2012

BizTalk 2010 R2 CTP: Azure Service Bus Integration–Part 1

Back in June 2012, I had the opportunity to attend TechEd North America.  At this event the BizTalk team gave us a glimpse into the next version of BizTalk and went over the Product Road map.  You can read more about this Roadmap session here.

One of the areas that Microsoft wanted to address was better/seamless integration with Azure and more specifically with Service Bus Queues and Topics.  The BizTalk team released a feature pack back in October 2010 that better enabled BizTalk to leverage the Service Bus Relay capabilities.  This feature pack does work well but did not allow for connectivity to Service Bus Queues and Topics since they weren’t even available back then.

In the fall of 2011, the talented Paolo Salvatori wrote a very detailed article on how you can integrate BizTalk 2010 with Service Bus Queues and Topics.  While Paolo’s solution does work it does require some additional effort and some people may be a little overwhelmed by the solution.  But I do give credit to Microsoft and Paolo for coming up with a solution considering BizTalk 2010 was released much before Service Bus Queues and Topics were commercially available.  Their solution just validates why BizTalk leveraging WCF is a good idea.  When investments are made to WCF, BizTalk usually benefits. All in all, it was a good stop-gap for anyone desperate to integration BizTalk 2010 with Azure.

Fast forward to July 2012 when Microsoft released this BizTalk 2010 R2 CTP.  Microsoft has delivered on making integration with Service Bus Queues and Topics very simple.  The BizTalk team recently released a blog post which provides an overview of some of these new features.  I thought it would be beneficial to provide a walk through for anyone interested in more details than what Microsoft included in that post.

Scenario

The scenario that we are about to explore includes a client application that will publish a typed Brokered message from a Console application to a Service Bus Queue.  BizTalk will then use the new SB-Messaging adapter to retrieve the message and simply write it to the file system.  As an experienced BizTalk guy, I like strongly typed messages and I am not afraid to admit it.  So as part of this solution I am going to include a strongly typed BizTalk schema that I am going to deploy.  For this walkthrough I am not going to transform this message but for anyone familiar with BizTalk they will be able to take this solution adapt it for their needs.

Client Application

  • Launch Visual Studio 2012 and create a C# Console application.  I called my application BrokeredMessageToBizTalk

image

  • Next I will use the Nuget Package manager and installing the Windows Azure Service Bus package.  You can access Nuget by clicking the following within Visual Studio: Tools - Library Package Manager - Manage Nuget Packages for Solution.

image

  • Since I want deal with typed messages I am going to create a class called PowerOut.  Since I work in the Power Industry I will over-simplify a use case that involves a customer whose power is out.  They will send a message from a client application (it could be a web page, mobile phone app etc) to a Service Bus Queue.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BrokeredMessageToBizTalk
{
    public class PowerOut
    {
        public string CustomerName;
        public string PhoneNumber;
        public string Address;
       
    }
}

  • Within our Program.cs file we want to include the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Messaging;
using System.Runtime.Serialization;
using System.IO;

namespace BrokeredMessageToBizTalk
{
    class Sender
    {
   
        const string QueueName = "PowerOutageQueue";
        static string ServiceNamespace = "YOUR_NAMESPACE";
        static string IssuerName ="owner";
        static string IssuerKey = "YOUR_KEY”;

        static void Main(string[] args)
        {
            //*****************************************************************************************************
            //                                   Get Credentials
            //*****************************************************************************************************          
            TokenProvider credentials = TokenProvider.CreateSharedSecretTokenProvider(Sender.IssuerName, Sender.IssuerKey);
            Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", Sender.ServiceNamespace, string.Empty);

            MessagingFactory factory = null;

            try
            {
                //***************************************************************************************************
                //                                   Management Operations
                //***************************************************************************************************       
                NamespaceManager namespaceClient = new NamespaceManager(serviceUri, credentials);
                if (namespaceClient == null)
                {
                    Console.WriteLine("\nUnexpected Error: NamespaceManager is NULL");
                    return;
                }

                Console.WriteLine("\nCreating Queue '{0}'...", Sender.QueueName);

                // Delete if exists
                if (namespaceClient.QueueExists(Sender.QueueName))
                {
                    namespaceClient.DeleteQueue(Sender.QueueName);
                }

                namespaceClient.CreateQueue(Sender.QueueName);

                //***************************************************************************************************
                //                                   Runtime Operations
                //***************************************************************************************************
                factory = MessagingFactory.Create(serviceUri, credentials);

                QueueClient myQueueClient = factory.CreateQueueClient(Sender.QueueName);

                //***************************************************************************************************
                //                                   Sending messages to a Queue
                //***************************************************************************************************
               

                Console.WriteLine("\nSending messages to Queue...");

                //Create new instance of PowerOut object
                PowerOut po = new PowerOut();
                po.CustomerName = "Stephen Harper";
                po.PhoneNumber = "613-123-4567";
                po.Address = "24 Sussex Drive";

                BrokeredMessage message = new BrokeredMessage(po, new DataContractSerializer(typeof(PowerOut)));
              
                myQueueClient.Send(message);
             

                //Uncomment this code if you want to write a sample file to disk

                //using (FileStream writer = new FileStream("c:/temp/file.xml",FileMode.Create, FileAccess.Write))
                //{
                //    DataContractSerializer ser = new DataContractSerializer(typeof(PowerOut));
                //    ser.WriteObject(writer, po);
                //}

                Console.WriteLine("\nAfter running the entire sample, press ENTER to exit.");
                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine("Unexpected exception {0}", e.ToString());
                throw;
            }
            finally
            {
                // Closing factory close all entities created from the factory.
                if(factory != null)
                    factory.Close();
            }
           
        }

    }
}

Of the code above I want to highlight a couple different lines:

  • The first one deals with the DataContractSerializer as seen below.        

BrokeredMessage message = new BrokeredMessage(po, new DataContractSerializer(typeof(PowerOut)));

If you do not use a DataContractSerializer you can expect undesirable results when BizTalk retrieves the message from the queue.  As mentioned in the recent BizTalk team blog post: “Brokered Message .NET API uses Binary encoding. To avoid this issue, you will need to use Text by explicitly provide your own serializer, instead of the default serializer.”

  • The next deals with the few lines that have been commented out.  Since I want to use typed messages within BizTalk, I can generate a sample XML message using the code below.  This will allow me to generate a BizTalk schema using tools provided within Visual Studio.

    //using (FileStream writer = new FileStream("c:/temp/file.xml",FileMode.Create, FileAccess.Write))
                //{
                //    DataContractSerializer ser = new DataContractSerializer(typeof(PowerOut));
                //    ser.WriteObject(writer, po);
                //}

*As a side note – wouldn’t it be nice if BizTalk supported native .Net Classes (from a messaging perspective) - hint, hint *

BizTalk Application

We can now create a BizTalk application.  Since we are using the new BizTalk 2010 R2 CTP we can also use the latest version of Visual Studio 2012.  As I mentioned earlier I want to process typed messages so our BizTalk solution will be very simple.  It will only include a Schema.  We will deploy this message to BizTalk so that when an instance of this message is published to the MessageBox that we will have a known schema deployed that will match this message type.

  • We can now create a new BizTalk application. I have called mine PowerOutage and I have also added a Strong Name Key called PowerOutage.snk.

image

  • Next I want to create a new Schema based upon the sample file that we previously generated.  I can create this new schema by right mouse clicking on BizTalk project (PowerOutage) - Add - Add Generated Items.
  • When prompted, click on the Generate Schemas label and then click the Add button.

image

  • Select Well-Formed XML from the Document type dropdown and then we need to provide the name of our sample file.  Click OK to proceed.

image

  • We will now have a schema added to our solution that represents our PowerOutage class.

image

  • Deploy our BizTalk Application
  • When we launch the BizTalk Admin Console we will discover our PowerOutage application.
  • We now need to create a Receive Port and corresponding Receive Location.  In this situation we are going to use the SB-Messaging Adapter.

image

  • When we click the Configure button we will have a few more properties to fill out including our URL.  Our URL is going to include our Namespace (highlighted in Green) and our QueueName (highlighted in Orange)

image

  • Next we need to click on the Authentication tab.  Within this tab we will provide our Namespace as it relates to the Access Control Servers (ACS), an our Issuer Name and Key.

image

  • The Properties tab is not used in this example.  I will further examine it in a later post.
  • With our Receive Port and Receive Location created we can no move on to our Send Port.  For this example we are simply going to create a File Drop where we can write out the file that we have received from the Service Bus Queue.

image

  • Since we do not have any Orchestrations we do need to wire up a subscription for our inbound message.  In order to do this we will simply create a “Send Port Subscription” by setting filter.

image

  • We can now Start our BizTalk application and bounce our Host Instance(if applicable)

Testing our scenario

  • Next, launch our Console Application and we will discover that our message has been sent to our Queue.

image

  • If we check the File Drop that was specified in our Send Port we should see a newly created file.  When we open this file we should recognize the content that we populated in our Console application.  Since we now have typed data within BizTalk it will be easy to transform it into other message types so that we can exchange data with other systems such as Line of Business (LOB) systems.

image

Conclusion

Now that wasn’t so bad was it?  For experienced BizTalk people this process should be a breeze.  The only area that initially hung me up was the DataContractSerialzer that is specified in our console application.  The other good news is that we are just scratching the surface in this blog post.  Look for more posts related to BizTalk and Service Bus integration using the new BizTalk 2010 R2 CTP.

Monday, September 10, 2012

Packt MCTS BizTalk certification e-copy winners

This is a follow-up post to the Win a e-copy of the Packt MCTS BizTalk certification book post.  Thank-you to all that entered.  I enjoyed reading why you were interested in pursuing certification. The following people have won an e-copy of the book:
  • Johan Älverdal
  • Kevin Molloy
  • Donie Treadaway
I have forwarded your email addresses to the publisher and they will be in touch.