Software in the cloud: Cloud Workflow

As promised in my last posting let's take a look at the new "Workflow in the cloud" functionality offered in the July drop of the BizTalk Services

This cloud based workflow system leverages Windows Workflow and is hosted in our datacenter and extended as appropriate through "cloud-scale" runtime services and infrastructure.  The idea being that you can run your workflows on our infrastructure which will scale to your throughput needs - subject to appropriate payments.   These workflows will be durable and resilient to any failures - all of this will be transparent to you, simply deploy your workflows and away you go.

As it currently stands you only get one workflow type to use, a sequential workflow which is of course well suited to straight-forward predictable machine workflows.  Within this workflow you get to use Activities but you can't however use the complete palette of WF activities that you get in the .NET framework.

Your restricted to a number of new cloud activities provided which are as follows: CloudHttpSend, CloudHttpReceive, CloudIfElse, CloudSequence, CloudServiceBusSend, CloudDelay and CloudWhile.

All of these do exactly what they say on the tin, the CloudHttpSend activity enables you to send an HTTP GET or POST to a remote URI, the CloudHttpReceive enables something to send data to a running workflow instance via an HTTP POST.

The CloudServiceBusSend activity may be a new concept to you as it refers to invoking a Service exposed via the Internet Service Bus using the relay service which I covered in this posting. This means you can have a WCF service hosted within your corporate network but still enable it to be invoked from parties outside of your corporate boundary provided they have the appropriate permissions.  In the case of a cloud based workflow, it can invoke services hosted within a corporate boundary from a Microsoft data-center without having to punch new holes in your firewall but still maintain security.

So, let's take a look at how you can create your own workflow, deploy and execute it using the BizTalk Services release that you can all use.  To start you'll need to create an account at https://biztalk.net and download/install the BizTalk Services SDK.

The first step is to create a new Sequential Workflow, all cloud workflows must be Sequential Workflows and XOML only.  There is no way to have "code behind" files with these workflows, everything has to be expressed using a workflow, activities and as appropriate rules.  Once this has been created you should consider deleting the automatically created .cs file to avoid you accidently adding code which can't be used.

image

Once you've done this you should see the new cloud based activities in your toolbox as shown below, if not you may need to right click the toolbox, choose "Choose Items.." and then ensure that the cloud prefixed workflow activities are selected.  These new activities are held within the System.ServiceBus.Workflow assembly under the %PROGRAMFILES%\Microsoft BizTalk Services SDK\Assemblies directory.

 image

 

For the purposes of this simple Hello World cloud workflow we'll just use one activity.  The CloudHttpReceive activity will be used to demonstrate how we can communicate with a running workflow instance.  You shoud have a workflow as shown below

image

The usual exclamation marks indicate that we have some configuration to do on this activity, for the cloudHttpReceive the only configuration we need to supply is a response that that will be returned as part of the remote HTTP post to our workflow, you can also configure the HTTP Status code to return as required.  For the purposes of this workflow and the code you'll see in a bit, set the name of this activity to helloWorldHttpReceive and supply a response body of your choosing.

Now that our simple workflow is done let's deploy it to the Cloud Workflow infrastructure hosted by Microsoft, right now there isn't a Deploy addin to Visual Studio although there is an underlying Web Service.  For now we need to perform a simple copy/paste deployment model.

Firstly we need to get the XOML representation of the workflow, a quick way of doing this is to right Click the workflow in Solution Explorer, Chose "Open With.." and then choose "XMl Editor", you can now copy the XOML representation into the clipboard.

We now need to deploy the workflow via the BizTalk Services website, browse to https://workflow.biztalk.net/ and supply your BizTalk Services credentials to view the workflow management portal as shown below.

image

Click "Manage Types" on the right hand side to manage the workflow types under your account, then click Add New to access the Workflow Deployment page which is shown below.  For the purposes of this demo we'll call our Workflow "Hello World" and you can now paste the XOML representation into the supplied box.

image

Click Save changes and your cloud workflow is now ready to go!  Note the "Rules" tab in the previous screenshot, this is where you can also provide any rules associated with your cloud workflow.

Now our workflow is created how can we go about kicking it off within the Microsoft datacenter?  It's pretty straight forward once you know how to do it and the BizTalk Services SDK has a sample to help you out.   Those of you familiar with BizTalk Orchestrations you'd expect that we could now perform a simple HTTP POST at a special URI and have a workflow instance created automagically?  Not right now, I'm afraid - you have to manually crete an instance, then start it before you can submit an HTTP post, this as I understand it will be changed as the product teams progress.

The code to create, start and then HTTP POST to a workflow instance is shown below, it's all pretty straight forward and a lot of it is demonstrated in the SDK sample.  The key thing in the code is how to target your HTTP POST of a running workflow instance.  The URI is made up as follows:

https://workflow.biztalk.net/servicesHttp/**YOURBIZTALKSERVICESUSERNAME**/workflows/**YOURWORKFLOWNAME**/instances/**YOURWORKFLOWINSTANCEID**/**HTTPRECEIVEACTIVITYNAME**

And an example for my scenario here is as follows:

https://workflow.biztalk.net/servicesHttp/darrenj/workflows/HelloWorld/instances/83734f1c-d08f-4cc9-b396-b76ebdb60979/helloWorldHttpReceive

You'll need to add assembly references to your project for System.ServiceBus.dll, System.ServiceBus.Workflow.dll and System.ServiceModel.dll

[TestMethod]
public void TestMethod1()
{
    // Create a new Workflow Instance in the cloud, we then get the InstanceID back
string instanceId = CreateWorkflowInstanceInTheCloud();

    // The Workflow Type
string workflowTypeName = "HelloWorld";

    // The PlaceOrder Workflow starts with a cloudHttpReceive activity called "placeOrderHttpReceive"
// We must target this directly when posting our Order information to the cloud workflow
string httpActivityName = "helloWorldHttpReceive";

    // Build up the URL to directly target our newly created Workflow Instance and to post XML directly to the
// waiting cloudHttpReceive activity

Uri serviceUri = new Uri(
string.Format(WorkflowClientConfig.HttpUri + "{1}/workflows/{2}/instances/{3}/{4}",
WorkflowClientConfig.WorkflowHostName, _bizTalkServicesUsername, workflowTypeName, instanceId, httpActivityName));

string httpPostBody = "<HelloWorldWorkflow><Message>Hello World</Message></HelloWorldWorkflow>";

    // Prepare to perform an HTTP POST to the waiting workflow
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceUri);
request.Method = "POST";
request.ContentType = "text/xml";
request.ContentLength = httpPostBody.Length;
    // Add the special header which contains our STS token retrieved from the cloud
request.Headers.Add("X-MS-Identity-Token", GetTokenFromCloudSts());

using(StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
        // Write the OrderInformation XML representation into the stream
writer.Write(httpPostBody);
writer.Flush();

        // Wait for a response from the Cloud Workflow, the cloudHttpReceive activity is configured to respond with a fixed
// Response if succesful;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

using(StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string returnMessage = reader.ReadToEnd();

Trace.WriteLine("Response message from the cloud is: " + returnMessage);

            // For some reason all responses from the cloudHttpReceive activity are wrapped into a <string> element
Assert.IsTrue(returnMessage.Contains("Hello World Response"));
}
}
}

 private string GetTokenFromCloudSts()
{
    // Using a tokenised URL let's build up a valid URL given the supplied BizTalk Services credentials
    // This URL when invoked will return an authentication token we can then use to perform administrative operations on
    // Our Workflow Configuration (Create, Start, etc.)
    string tokenUrl = string.Format(
            "https://{0}/issuetoken.aspx?u={1}&p={2}",
            WorkflowClientConfig.StsHostName,
            _bizTalkServicesUsername,
            _bizTalkServicesPassword);

    // Create an HttpWebRequest
    HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenUrl);
    Trace.WriteLine(tokenRequest.RequestUri);

    // No Request to send, just invoke the URL and get the authentication token
    HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();

    // Get the token from the Response Stream
    byte[] tokenBody = new byte[500];
    int tokenBodyLength = tokenResponse.GetResponseStream().Read(tokenBody, 0, 500);

    // Convert to a UTF8 representation which we can then use in subsequent requests
    string authenticationToken = Encoding.UTF8.GetString(tokenBody, 0, tokenBodyLength);
    Trace.WriteLine(String.Format("Authentication Token: {0}", authenticationToken));

    return authenticationToken;
}
 private string CreateWorkflowInstanceInTheCloud()
{
    // New up an instance of the provided System.ServiceBus.Workflow.WorkflowClient which provides a wrapper
    // around the cloud workflow SOAP Web Servics        
    WorkflowClient workflowClient = new WorkflowClient();

    // Provide BizTalk Services credentials
    workflowClient.CredentialType = TransportClientCredentialType.UserNamePassword;
    workflowClient.Credentials.UserName.UserName = _bizTalkServicesUsername;
    workflowClient.Credentials.UserName.Password = _bizTalkServicesPassword;

    workflowClient.Open();

    // Parameters can't be supplied in R12 of Cloud Services so we this empty
    // A shame as it would enable us to pass the OrderInformatoin directly
    Dictionary<string, object> parameters = new Dictionary<string, object>();

    // Create a workflow within the BizTalk Services username namespace 
    // The Workflow Type name is "HelloWorld"
    // No parameters in R12
    // Retrieve an authentication token from the BizTalk Services Cloud STS
    string instanceId = workflowClient.CreateWorkflowInstance(_bizTalkServicesUsername, "HelloWorld", parameters, GetTokenFromCloudSts());
    Trace.WriteLine("Workflow Instance Created:" + instanceId);

    // Once a workflow is created it must be explicitly started otherwise it won't active any activity shapes
    // In R12 there's no ability to create/start a workflow based on an external activity action such as an Http Send
    // The workflow instance was created within the BizTalk Services username namespace
    // The workflow Type name is "HelloWorld"
    // The instanceId was returned by the previous CreateWorkflowInstance call
    workflowClient.StartWorkflowInstance(_bizTalkServicesUsername, "HelloWorld", instanceId);
    Trace.WriteLine("Workflow Instance Started:" + instanceId);

    // Our work is done, close the WorkflowClient
    workflowClient.Close();

    // Return the InstanceId so we can identify the WorkflowInstance we've just created when we perform an HTTP post to it next
    return instanceId;
}

All pretty straight forward, and it's a bit more complex then it should be due to the requirement to create and start a workflow instance before being able to perform an HTTP post but this should come in time.

If you navigate back to the https://workflow.biztalk.net site then you can review running workflow instances and check they've completed successfully.

That's it for this post, this post has got rather large so I'll do another post on calling ServiceBus services via the cloudServiceBusSend activity and also detail some limitations in this release of Cloud Workflow (it's the first release for the team so don't expect everything to be there straight away!).  I'll also cover a handy little ISBTraceTool that I put together to aid with visualizing cloud workflow activity.

More soon!