Sep 042015
 

AMQP 1.0 is “broker model independent” meaning there are no protocol requirements related to the internals of the broker. It complies with the International Standard ISO/IEC 19464. It is the only standard version of AMQP. As of last month, QPid Proton, SwftMQ, RabbitMQ, ActiveMQ, and MQLite are all supporting this relatively new standard.

Microsoft is currently working to retire their .NET-centric proprietary protocol, SBMP, over the next few months/ This will leave only the AMQP protocol when working with Azure Service Bus. The AMQP protocol backs the .NET SDK, which still has support for WCF with NetMessagingBinding. Developers may also choose to use AMQPNet Lite, which is an open source library for .NET and UWP (store apps). It is a lighter library exposing more of the primitives of AMQP. Another alternative is SBLite, which is a wrapper around AMQPNet Lite to abstract the low level details of AMQP.

Download Qpid Proton 0.10
https://qpid.apache.org/releases/qpid-proton-0.10

Download AMQPNet Lite
https://github.com/Azure/amqpnetlite

Download SBLite
https://github.com/ppatierno/azuresblite

image

Sep 152014
 

I had a great time at Jax Code Impact over the weekend. Many thanks to Bayer and Brandy for putting together a free, enjoyable, and educational Saturday conference. As a first year conference, it was impressive to see more than 300 people registered for six tracks of Microsoft-focused presentations. Kevin Wolf was a big hit with his quadcopters, 3-D printer, and oculus rift. I personally enjoyed two separate talks on Redis by Henry Lee and Steve Danielson.

I talked about the Windows Azure Service Bus offerings, and hit on topics such as Relays, Queues, Topics, WCF bindings, Pub/Sub, AMQP, and of course some Cloud Design Patterns. 

Here are the slides and code demos from my talk:

Code Impact Service Bus Presentation – Slides

Code Impact Service Bus Presentation – Demo Code

Mar 022011
 

Correlation is a priceless feature of WF4 that allows you to correlate WCF messages of your workflow based on some unique data in a subsequent message. From a calling client’s perspective, the need for correlation looks would look like this:

  1. Client creates workflow instance with WCF message
  2. Workflow (service) responds indicating that the workflow instance is running
  3. At some point in the future, client wants to resume the same, now waiting, workflow instance with another WCF message

To implement the resumption of the same workflow instance requires that the client have some sort of identifier to get back to the same instance they left earlier. WF4 uses CorrelationHandles to accomplish this. Using the CorrelationHandle class and some send/receive message properties, clients can share some unique ID to get back to the same instance. From a calling client’s perspective, the same scenario as before with correlation looks like this:

  1. Client creates workflow instance with WCF message
  2. Workflow creates a correlation handle on some unique ID
  3. Workflow (service) responds with the unique ID, for later correlation use, also indicating that the workflow instance is running
  4. At some point in the future, client wants to resume the same, now waiting, workflow instance with another WCF message
  5. Client sends another WCF message, including the unique ID in the message
  6. Workflow responds to the incoming message, reads the unique ID from the message, and looks up the correct instance by the correlation handle corresponding to that unique ID
  7. The same instance created earlier resumes

You may be asking yourself, “So how do I do all this correlation stuff?” The following procedure will detail the necessary steps. The download link at the bottom of this post contains a sample project that has this sample already implemented.

Procedure:

  1. Start with a workflow with two separate ReceiveAndSendReply sequences.
  2. If a unique ID is not available for correlation in your workflow already, you can use the WorkflowInstanceId. Put the following code in a new activity, and add the activity between the Receive and SendReply activities in your workflow.
    public sealed class GetWorkflowInstanceId : CodeActivity
    {
        public OutArgument<Guid> WorkflowInstanceId { get; set; }
    
        protected override void Execute(CodeActivityContext context)
        {
            context.SetValue<Guid>(this.WorkflowInstanceId, context.WorkflowInstanceId);
        }
    }
  3. Create a new variable named WorkflowInstanceId of type System.Guid. image
  4. Create a new variable named WorkflowInstanceCorrelationHandle of type CorrelationHandle.
  5. Set the WorkflowInstanceId out argument on the GetWorkflowInstanceId activity to the newly created variable WorkflowInstanceId.
  6. Modify the SendReply activity Content to include the WorkflowInstanceId guid. This will give the client the unique ID to use in subsequent messages.
  7. Open the SendReply activity CorrelationInitializers with the ellipsis button.
  8. Click Add initializer in the left pane of the Add Correlation Initializers window. Type the name of the newly created variable WorkflowInstanceCorrectionHandle.
  9. Verify that Query correlation initializer is selected in the combo box. Double-click to add a key under XPath Queries. Choose the WorkflowInstanceId item from the outbound message/parameters. In the attached example, this appears after the double-click as “Content : Guid” and appears as “sm:body()/xg0:guid” as an XPath query.
    image
  10. Click OK to close the Add Correlation Initializers window.
  11. For the subsequent Receive activities, modify the Content to include the WorkflowInstanceId guid as either a message or parameter. This will make it possible to lookup the correct workflow instance.
  12. On the same Receive activities, open CorrelatesOn with the ellipsis button.
  13. Type the name of the variable WorkflowInstanceCorrelationHandle in the CorrelatesWith field.
  14. Double-click to add a key under XPath Queries. Choose the WorkflowInstanceId item from the outbound message/parameters. In the attached example, this appears after the double-click as “Content : Guid” and appears as “sm:body()/xg0:guid” as an XPath query.
    image
  15. Test the interaction by creating a test client (included in the download below) that creates 2 or more instances and resumes them in a different order.
  16. The final workflow should look like the image to the right.

Download WfCorrelation.zip – 25.1 KB (25,727 bytes)
(Build the solution before opening the workflow to avoid activity errors)

 Posted by at 5:45 am
Dec 142010
 

I started seeing the following error in the AppFabric exceptions appearing during activation of my WF workflow.

Cannot create unknown type '{clr-namespace:MyActivityLibrary}MyCodeActivity'

The workflow completed all the steps of MyCodeActivity and the other code activities in my activity library. WF never complained; it was only an error visible in AppFabric. A Bing search resulted in the following suggestions:

  1. Create separate class libraries for activities and workflows (xamlx): already done.
  2. Place the activity library in the GAC: that should not be necessary so I skipped that one.
  3. Remove and add the activity from the workflow: again not necessary because I can see in the xaml that the namespace and the activity are fine and not appearing as a local assembly.

So it must be something else. Perhaps the unknown type error is not telling me the whole story and maybe it is failing to load/create another type referenced by my library? I went through my references and found a reference to a DLL that is not used by my activities. I removed the unnecessary reference and the errors did not return. Maybe the same thing is happening to you.

 Posted by at 9:26 pm
Dec 042010
 

An ACE (Application Control Engine) Appliance is commonly referred to as simply being a load balancer. An ACE appliance provides many more features that are commonly employed. One of the most desirable features is SSL off-loading. When properly configured for offloading, the ACE will serve as a load balancer, and also take on the responsibility of processing all SSL requests made to a web server farm. In this configuration web farm server’s IIS does not need to worry about SSL at all. SSL traffic ends at the ACE, and only unencrypted HTTP traffic exists between the ACE and IIS. This is desirable because it provides a hardware solution to SSL processing and alleviates some load and configuration on the web servers and IIS.

While these ACE features are wonderful, it creates a little headache for WCF servers hosted on IIS behind the ACE. The problem shows itself as one of the following error messages as seen on the client end:

System.ArgumentException: The provided URI scheme ‘http’ is invalid; expected ‘https’. Parameter name: via

System.ServiceModel.ServiceActivationException: The requested service, ‘https://sub.mydomain.com/MyService.svc’ could not be activated. See the server’s diagnostic trace logs for more information.

System.ServiceModel.ProtocolException: The content type text/html; charset=utf-8 of the response message does not match the content type of the binding (application/soap+msbin1). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly.

System.Net.WebException: The remote server returned an error: (500) Internal Server Error.

The clients have to call the service with a URL similar to https://sub.mydomain.com/MyService.svc. The ACE handles the SSL, converts the request to an unencrypted HTTP request and passes it onto the web server. When the request reaches IIS and the WCF dispatcher, a service cannot be found that matches the HTTPS To address in the header because only HTTP service endpoints are being exposed. To make it work, you need to trick WCF into thinking its HTTP services can support transport security without actual transport or message protection. You also have to configure the service to listen on one address (HTTP) and respond to requests from another (HTTPS).

Enter the professionals. Michele Leroux Bustamante, of IDesign, wrote a few important articles in DevProConnections in late 2008 (links at the bottom of this post) that discuss this in detail and provide a solution. I have modified her solution slightly to provide a slightly easier configuration experience.

AssertEncryptionHttpTransportElement.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.ServiceModel.Configuration;
using System.ServiceModel.Channels;

namespace WcfExtensions
{
    public class AssertEncryptionHttpTransportElement : HttpTransportElement
    {
        private bool m_RequireClientCertificate = false;
        ConfigurationPropertyCollection m_PropCol = null;

        public override Type BindingElementType
        {
            get
            {
                return typeof(AssertEncryptionHttpTransportBindingElement);
            }
        }

        protected override TransportBindingElement CreateDefaultBindingElement()
        {
            AssertEncryptionHttpTransportBindingElement retval =
                new AssertEncryptionHttpTransportBindingElement(m_RequireClientCertificate);
            return retval;
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                if (m_PropCol == null)
                {
                    m_PropCol = base.Properties;
                    m_PropCol.Add(
			new ConfigurationProperty("requireClientCertificate",
				typeof(bool), false, null, null, ConfigurationPropertyOptions.None));
                }
                return m_PropCol;
            }
        }

        protected override BindingElement CreateBindingElement()
        {
            return new AssertEncryptionHttpTransportBindingElement(this.RequireClientCertificate);
        }

        [ConfigurationProperty("requireClientCertificate", DefaultValue = false)]
        public bool RequireClientCertificate
        {
            get
            {
                bool retval = (bool)base["requireClientCertificate"];
                return retval;
            }
        }
    }
}

AssertEncryptionHttpTransportBindingElement.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Xml;
using System.Net.Security;

namespace WcfExtensions
{
    public class AssertEncryptionHttpTransportBindingElement : HttpTransportBindingElement, ITransportTokenAssertionProvider, IPolicyExportExtension
    {
        private bool m_RequireClientCertificate = false;

        public AssertEncryptionHttpTransportBindingElement() : base() { }

        public AssertEncryptionHttpTransportBindingElement(bool requireClientCertificate)
            : base()
        {
            m_RequireClientCertificate = requireClientCertificate;
        }

        public override BindingElement Clone()
        {
            AssertEncryptionHttpTransportBindingElement retval = new AssertEncryptionHttpTransportBindingElement(m_RequireClientCertificate);
            return retval;
        }

        public override T GetProperty<T>(BindingContext context)
        {
            if (typeof(T) == typeof(ISecurityCapabilities))
            {
                AssertEncryptionSecurityCapabilities retval = new AssertEncryptionSecurityCapabilities();
                return (T)(object)retval;
            }
            else
                return base.GetProperty<T>(context);
        }

        #region ITransportTokenAssertionProvider Members

        public XmlElement GetTransportTokenAssertion()
        {
            string secpolNS0 = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy";
            //string secpolNS = "http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702";
            XmlDocument xmldoc = new XmlDocument();
            XmlElement TransTokAssert = xmldoc.CreateElement("sp", "HttpsToken", secpolNS0);
            TransTokAssert.SetAttribute("RequireClientCertificate", m_RequireClientCertificate.ToString().ToLower());

            return TransTokAssert;
        }

        #endregion

        public bool RequireClientCertificate
        {
            get
            {
                return this.m_RequireClientCertificate;
            }
            set
            {
                this.m_RequireClientCertificate = value;
            }
        }

        #region IPolicyExportExtension Members

        void IPolicyExportExtension.ExportPolicy(MetadataExporter exporter, PolicyConversionContext context)
        {
            HttpsTransportBindingElement httpsTBE = new HttpsTransportBindingElement();
            httpsTBE.RequireClientCertificate = m_RequireClientCertificate;
            ((IPolicyExportExtension)httpsTBE).ExportPolicy(exporter, context);
        }

        #endregion
    }
}

AssertEncryptionSecurityCapabilities.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;
using System.Net.Security;

namespace WcfExtensions
{
    internal class AssertEncryptionSecurityCapabilities : ISecurityCapabilities
    {
        #region ISecurityCapabilities Members

        public ProtectionLevel SupportedRequestProtectionLevel
        {
            get { return ProtectionLevel.EncryptAndSign; }
        }

        public ProtectionLevel SupportedResponseProtectionLevel
        {
            get { return ProtectionLevel.EncryptAndSign; }
        }

        public bool SupportsClientAuthentication
        {
            get { return false; }
        }

        public bool SupportsClientWindowsIdentity
        {
            get { return false; }
        }

        public bool SupportsServerAuthentication
        {
            get { return true; }
        }

        #endregion
    }
}

Hosting application configuration file:

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

    <extensions>
      <bindingElementExtensions>
        <add name="assertEncryptionHttpTransportElement"
                type="WcfExtensions.AssertEncryptionHttpTransportElement, WcfExtensions" />
      </bindingElementExtensions>
    </extensions>

    <bindings>
      <customBinding>
        <binding name="MyService.customBinding0">
            <binaryMessageEncoding/>

            <!-- Replaces httpsTransport -->
            <assertEncryptionHttpTransportElement/>
        </binding>
      </customBinding>
    </bindings>

    <services>
      <service name="MyServices.MyService">
        <endpoint address="https://sub.mydomain.com/MyService.svc"
                  listenUri="http://myserver.mydomain.local/MyService.svc"
                  binding="customBinding"
                  bindingConfiguration="MyService.customBinding0"
                  contract="MyServices.IMyService" />
      </service>
    </services>
</system.serviceModel>

 

The server name in the “listenUri” must appear exactly as it does on the “You have created a service” page. This is typically the internal name of the server. The “address” must appear as the clients call the service (at the load balancer). The result of this configuration change is WCF responding to an unencrypted request sent to an HTTPS address while listening on an HTTP address. You can read about the internals of this solution in more detail in Michele’s articles referenced below.

You can download the project that compiles to give you the WcfExtensions DLL referenced above.

Resources:

 Posted by at 8:15 pm
Jun 162010
 

For the few MSMQ or NetMsmqBinding WCF users I’ve encountered, here is an error you may encounter in a highly secured environment.

0xC00E008F Binding to the forest root failed. This error usually indicates a problem in the DNS configuration. MQ_ERROR_DS_BIND_ROOT_FOREST

This is most likely another firewall problem. If port 3268 is not open, MSMQ cannot register or authenticate with the user’s certificate in AD. Here is the port description:

3268/TCP,UDP msft-gc, Microsoft Global Catalog (LDAP service which contains data from Active Directory forests)

 Posted by at 1:40 am
May 082010
 

Despite its pitiful adoption in the developer community, I am implementing Transactional NTFS (TxF) transactions using the Microsoft.KtmIntegration.TransactedFile class. This allows me to reap the benefits of TransactionScope and distributed transactions for file operations (e.g. creates, updates, deletes). This is the only missing piece for typical transactional business applications. With the “KTM” and “KtmRm for Distributed Transactions” services, available only on Vista, Windows 7, and Windows Server 2008, file operations will roll back if the TransactionScope is not completed.

There’s just one problem. Transactional NTFS does not work with file shares. I can?t remember the last time I put a “C:FileStore” reference in a config file. A friendly share like \serverFileStore is always preferred, especially since DFS came about. Attempting to use a share results in the following error message:

The remote server or share does not support transacted file operations

Don’t read this as “your remote server” or “your remote share”, but rather “all remote servers and shares”. As mentioned in this MSDN article, TxF is not supported by the CIFS/SMB protocols. The error was probably written with the expectation that one day some remote servers and shares would support TxF. I emailed Microsoft about it and received a response fairly quickly. The response was simply:

“We understand the need and have plans to eventually support TxF over SMB2, but we?re not there yet and are not ready to announce if or when this will be supported. When it is the documentation will be updated.”

I’m not getting my hopes up, but Windows Server 2011 looks to be our only hope before .NET changes beyond recognition and TxF is a distant memory. Until then, I wrapped up all of my TxF code in a WCF service and install that service on the server with the FileStore folder.

MSDN article – When to Use Transactional NTFS

      http://msdn.microsoft.com/en-us/library/aa365738(v=VS.85).aspx

TxF Sandbox – Sample Projects (including Microsoft.KtmIntegration.TransactedFile)

      TxFSandbox.zip

 Posted by at 3:32 pm
Apr 222010
 

The sole 2010 offering in the USA of IDesign‘s Architect’s Master Class conducted by the man himself, Juval Lowy, is only a few weeks away. I checked in at the IDesign web site, and found some updates the world needs to see.

If you want to learn something new every day, start at the top of the IDesign Code Library and step through one example each day. Be careful, you might need to re-write every line of code you’ve ever written.

 Posted by at 12:56 am
Mar 162010
 

In a previous post, I discussed solutions to the dreaded “The flowed transaction could not be unmarshaled” error commonly experienced when using MSDTC transactions with WCF, SQL, TxF, etc. I have once again experienced the un-trusted domain scenario, and can now report with certainty that adding hosts file entries on both machines will correct the problem. Testing this solution with DTCPing.exe between the two machines proves that making only the hosts file change acquaints the client and server and allows distributed transactions to occur.

You will find many blog and forum post non-solutions. Adding the hosts file entry or the equivalent domain redirects are the only solutions when working with two machines in disparate, un-trusted domains. Some of the non-solutions you’ll find go so far as to say to change your SQL connection string to prevent current (ambient) transaction enlistment. Not quite a complete solution as your first rollback unit test will fail.

 Posted by at 2:54 am
Feb 262010
 

If you have a WCF service exposing endpoints with the NetMsmqBinding, you may come across my old pal, error code 0xc00e002f when you have web application clients. If you’ve already had your required interactive login on the web server with your AppPool’s service account and have already registered your AppPool service account’s user certificate for message queuing, then you should be ok.

If you are using IIS 7 or 7.5, there is one more piece to the puzzle. Go into Advanced Settings on your Application Pool, and find “Load User Profile” under the Process Model section. “Load User Profile” on these latest versions of IIS needs to be true to get your service account’s user certificate passed to MSMQ. I fought this for a while before finally finding it. And now? 🙂

 Posted by at 3:07 am