Azure .NET Services – A Twitter Service Bus

In this post I’ll demonstrate how to use the Microsoft .NET Services Service Bus and the .NET Service Bus NetTcpRelayBinding. The post based on the Echo sample in Azure SDK for .NET Services (March 2009 CTP).

What is a .NET Service Bus? it provides a relay service that makes it possible to establish connectivity through the cloud across organizational boundaries. This crucial infrastructure allows cloud-based workflows to communicate with on-premise applications by traversing firewalls and NAT devices.

The sample shows a simple service and client that communicate through the Service Bus. When the service application is started, it asks for your .NET Services Solution Credentials and opens an endpoint on the Service Bus. Once opened, this endpoint has a well-known URI on the Service Bus and is, irrespective of whether your machine is residing behind a Firewall or Network Address Translation, reachable from anywhere.

Configuring the Service

You configure the .NET Access Control Service through the administration portal provided within a particular solution. This is where you configure the rules that determine how it will issue claims for the users it knows about or as a result of transforming claims from other providers.

When using the .NET Service Bus, you’ll configure endpoints that use this Service Bus scope as the base URI (yours will be customized to your account, of course). Every solution comes preconfigured with a set of access control rules for both the .NET Service Bus and the .NET Workflow Service.

 

The Contract

Our service implements very simple contract that works as a Twitter wrapper. Currently there is only one method – CheckIfUserFollowMe that checks if a user follows you. (You can download the code and add other operations to the contract).

[ServiceContract(Name = "ITwitterContract", Namespace = "http://samples.maordavid.com/ServiceModel/Relay/")]
public interface ITwitterContract
{
    [OperationContract]
    bool CheckIfUserFollowMe(string user,string password,string friendScreenName);
}
public interface ITwitterChannel : ITwitterContract, IClientChannel { }

The Service

The service configuration file:

<configuration>
  <system.serviceModel>
    <services>
      <!-- Application Service -->
      <service name="Blog.MaorDavid.Samples.TwitterService">
        <endpoint contract="Blog.MaorDavid.Samples.ITwitterContract"
                  binding="netTcpRelayBinding" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

The configuration file is just like any other WCF configuration file with the exception that configured service endpoint refers a "netTcpRelayBinding", which isn’t part of the .NET Framework. The NetTcpRelayBinding is one of the new bindings introduced with the Service Bus.

In order to hook a listening service into the Service Bus, the Service Bus service needs to be able to verify that the owner of the listening service is authorized to do so.

Authentication and authorization are both performed by the Microsoft .NET Services Access Control Service. In order to make these steps simple, the Microsoft.ServiceBus Framework contains a set of transport client credential helpers that automatically deal acquiring required security tokens.

// create the endpoint address in the solution's namespace
Uri address = ServiceBusEnvironment.CreateServiceUri("sb", solutionName, "TwitterService");

// create the credentials object for the endpoint
TransportClientEndpointBehavior userNamePasswordServiceBusCredential = new TransportClientEndpointBehavior();
userNamePasswordServiceBusCredential.CredentialType = TransportClientCredentialType.UserNamePassword;
userNamePasswordServiceBusCredential.Credentials.UserName.UserName = solutionName;
userNamePasswordServiceBusCredential.Credentials.UserName.Password = solutionPassword;

// create the service host reading the configuration
ServiceHost host = new ServiceHost(typeof(TwitterService), address);

// add the Service Bus credentials to all endpoints specified in configuration
foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
{
    endpoint.Behaviors.Add(userNamePasswordServiceBusCredential);
}

// open the service
host.Open();

Console.WriteLine("Service address: " + address);
Console.WriteLine("Press [Enter] to exit");
Console.ReadLine();

// close the service
host.Close();

The credential used in this example is a simple Username/Password credential that is backed by the Solution credentials you set up when signing up for Microsoft .NET Services. Each Microsoft .NET Services solution automatically owns a branch of the Service Bus global namespace. "Your" Solution namespace branch is rooted at

[scheme]://solution-name.servicebus.windows.net/

whereby [scheme] is either "sb" (as in this example) or "http" or "https". The namespace owner can subdivide that namespace and organize services onto that namespace as needed and define rules in the Access Control Service to guard access to branches of the namespace.

When I run the host application, the WCF hosting infrastructure automatically authenticates with the .NET Access Control Service, acquires claims, and then opens an outbound port to the .NET Service Bus and registers a listener on the specified address. At that point, the locally hosted service will be ready to receive messages relayed through the .NET Service Bus on the specified address. I can also browse to the base address of the solution at this point to access the service registry feed and any active listener endpoints will appear in the feed.

The Client

The client configuration file will need to look just like the host configuration file. Only the endpoint definition will need to appear within the <client> section.

<configuration>
  <system.serviceModel>
    <client>
      <!-- Application Endpoint -->
      <endpoint name="RelayEndpoint"
                contract="Blog.MaorDavid.Samples.ITwitterContract"
                binding="netTcpRelayBinding"/>
    </client>

  </system.serviceModel>
</configuration>

When started, it asks for the Solution credentials, creates a channel to the Service and sends requests to the service.

// create the service URI based on the solution name
Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", solutionName, "TwitterService");

// create the credentials object for the endpoint
TransportClientEndpointBehavior userNamePasswordServiceBusCredential = new TransportClientEndpointBehavior();
userNamePasswordServiceBusCredential.CredentialType = TransportClientCredentialType.UserNamePassword;
userNamePasswordServiceBusCredential.Credentials.UserName.UserName = solutionName;
userNamePasswordServiceBusCredential.Credentials.UserName.Password = solutionPassword;

// create the channel factory loading the configuration
ChannelFactory<ITwitterChannel> channelFactory = new ChannelFactory<ITwitterChannel>("RelayEndpoint", new EndpointAddress(serviceUri));

// apply the Service Bus credentials
channelFactory.Endpoint.Behaviors.Add(userNamePasswordServiceBusCredential);

// create and open the client channel
ITwitterChannel channel = channelFactory.CreateChannel();
channel.Open();


Console.WriteLine("Welcome to the Twitter Client. To check if your friend is following you, please follow the instructions below;");
Console.WriteLine("Enter your Twitter user name:");
string twitterUserName = Console.ReadLine();
Console.WriteLine("Enter your Twitter password:");
string twitterPassword = MicrosoftSDKUtilities.ReadPassword();
Console.WriteLine("Enter your friend's Twitter user name:");
string friendTwitterUserName = Console.ReadLine();
try
{
    bool follow = channel.CheckIfUserFollowMe(twitterUserName, twitterPassword,friendTwitterUserName);
    if(follow)
        Console.WriteLine("Yes! You & {0} are friends!", friendTwitterUserName);
    else
        Console.WriteLine("Sorry...{0} doesn't follow you -:(", friendTwitterUserName);

}
catch (Exception e)
{
    Console.WriteLine("Error: " + e.Message);
}
Console.ReadLine();

channel.Close();
channelFactory.Close();

You can download the sample:

And this is what we get:

twitterSB

Enjoy!

Share this post :