Put Stuff in Your Windows Azure Junk Trunk – ASP.NET MVC Application

If you haven’t read Part 1 of this series (part 3 click here), you’ll need to in order to follow along with the JunkTrunk Repository.  Open the solution up if you haven’t already and navigate to the Models Folder within the ASP.NET MVC JunkTrunk Project.  In the folder add another class titled FileItemModel.cs and BlobModel.cs. Add the following properties to the FileItemModel.

[sourcecode language=”csharp”]
public class FileItemModel
{
public Guid ResourceId { get; set; }
public string ResourceLocation { get; set; }
public DateTime UploadedOn { get; set; }
}
[/sourcecode]

Add the following property to the BlobModel and inherit from the FileItemModel Class.

[sourcecode language=”csharp”]
public class BlobModel : FileItemModel
{
public Stream BlobFile { get; set; }
}
[/sourcecode]

Next add a new class file titled FileBlobManager.cs and add the following code to the class.

[sourcecode language=”csharp”]
public class FileBlobManager
{
public void PutFile(BlobModel blobModel)
{
var blobFileName = string.Format("{0}-{1}", DateTime.Now.ToString("yyyyMMdd"), blobModel.ResourceLocation);
var blobUri = Blob.PutBlob(blobModel.BlobFile, blobFileName);

Table.Add(
new BlobMeta
{
Date = DateTime.Now,
ResourceUri = blobUri,
RowKey = Guid.NewGuid().ToString()
});
}

public BlobModel GetFile(Guid key)
{
var blobMetaData = Table.GetMetaData(key);
var blobFileModel =
new BlobModel
{
UploadedOn = blobMetaData.Date,
BlobFile = Blob.GetBlob(blobMetaData.ResourceUri),
ResourceLocation = blobMetaData.ResourceUri
};
return blobFileModel;
}

public List GetBlobFileList()
{
var blobList = Table.GetAll();

return blobList.Select(
metaData => new FileItemModel
{
ResourceId = Guid.Parse(metaData.RowKey),
ResourceLocation = metaData.ResourceUri,
UploadedOn = metaData.Date
}).ToList();
}

public void Delete(string identifier)
{
Table.DeleteMetaDataAndBlob(Guid.Parse(identifier));
}
}
[/sourcecode]

Now that the repository, management, and models are all complete the focus can turn to the controller and the views of the application. At this point the break down of each data element within the data transfer object and the movement of the data back and forth becomes very important to the overall architecture. One of the things to remember is that the application should not pass back and forth data such as URIs or other long easy to hack strings. This is a good place to include Guids or if necessary integer values that identify the data that is getting created, updated, or deleted. This helps to simplify the UI and help decrease the chance of various injection attacks. The next step is to open up the HomeController and add code to complete each of the functional steps for the site.

[sourcecode language=”csharp”]
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to the Windows Azure Blob Storing ASP.NET MVC Web Application!";
var fileBlobManager = new FileBlobManager();
var fileItemModels = fileBlobManager.GetBlobFileList();
return View(fileItemModels);
}

public ActionResult About()
{
return View();
}

public ActionResult Upload()
{
return View();
}

public ActionResult UploadFile()
{
foreach (string inputTagName in Request.Files)
{
var file = Request.Files[inputTagName];

if (file.ContentLength > 0)
{
var blobFileModel =
new BlobModel
{
BlobFile = file.InputStream,
UploadedOn = DateTime.Now,
ResourceLocation = Path.GetFileName(file.FileName)
};

var fileBlobManager = new FileBlobManager();
fileBlobManager.PutFile(blobFileModel);
}
}

return RedirectToAction("Index", "Home");
}

public ActionResult Delete(string identifier)
{
var fileBlobManager = new FileBlobManager();
fileBlobManager.Delete(identifier);
return RedirectToAction("Index", "Home");
}
}
[/sourcecode]

The view hasn’t been created for the Upload just yet, so the method will cause a build error at this point. But before I add a view for this action, I’ll cover what has been created for the controller.

The Index Action I’ve changed moderately to have a list of the Blobs that are stored in the Windows Azure Blob Storage. This will be pulled from the manager class that we created earlier and passed into the view for rendering. I also, just for cosmetic reasons, changed the default display message passed into the ViewData so that the application would have something displayed more relevant to the application.

The About message I just left as is. The Upload action simply returns what will be a view we create.

The UploadFile Action checks for files within the request, builds up the model and then puts the model into storage via the manager.

The last method is the Delete Action that instantiates the manager and then calls a delete against the storage. This action then in turn traces back through, finds the Table & Blob Entities that are related to the specific blob and deletes both from the respective Windows Azure Storage Table and Blob Mediums.

The next step is to get the various views updated or added to enable the upload and deletion of the blob items.

Add a view titled Upload.aspx to the Home Folder of the Views within the JunkTrunk Project.

Upload View
Upload View

First change the inherits value for the view from System.Web.Mvc.ViewPage to System.Web.Mvc.ViewPage. After that add the following HTML to the view.

[sourcecode language=”html”]
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Upload an Image
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Upload</h2>
<% using (Html.BeginForm("UploadFile", "Home", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>

<div class="editor-label">
Select file to upload to Windows Azure Blob Storage:
</div>
<div class="editor-field">
<input type="file" id="fileUpload" name="fileUpload" />
</div>
<p>
<input type="submit" value="Upload" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
[/sourcecode]

After adding the HTML, then change the HTML in the Index.aspx View to have an action link for navigating to the upload page and for viewing the list of uploaded Blobs. Change the inherits first form System.Web.Mvc.ViewPage to System.Web.Mvc.ViewPage<IEnumerable>. The rest of the changes are listed below.

[sourcecode language=”html”]
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
<%: ViewData["Message"] %></h2>
<p>
<%: Html.ActionLink("Upload", "Upload", "Home") %>
a file to Windows Azure Blob Storage.
</p>
Existing Files:<br />
<table>
<tr>
<th>
</th>
<th>
FileName
</th>
<th>
DownloadedOn
</th>
</tr>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%: Html.ActionLink("Delete", "Delete",
new { identifier = item.ResourceId })%>
</td>
<td>
<%: item.ResourceLocation %>
</td>
<td>
<%: String.Format("{0:g}", item.UploadedOn) %>
</td>
</tr>
<% } %>
</table>
</asp:Content>
[/sourcecode]

Make sure the Windows Azure Project is set as the startup project and click on F5 to run the application. The following page should display first.

The Home Page o' Junk Trunk
The Home Page o' Junk Trunk

Click through on it to go to the upload page.

Selecting an Image to Put in The Junk Trunk
Selecting an Image to Put in The Junk Trunk

On the upload page select and image to upload and then click on upload. This will then upload the image and redirect appropriately to the home page.

The Image in the Junk Trunk
The Image in the Junk Trunk

On the home page the list should now have the uploaded blob image listed. Click delete to delete the image. When deleted the table and the blob itself will be removed from the Windows Azure Storage. To see that the data & image are being uploaded open up the Server Explorer within Visual Studio 2010.

Visual Studio 2010 Server Explorer
Visual Studio 2010 Server Explorer

View the data by opening up the Windows Azure Storage tree. Double click on either of the storage mediums to view table or blob data.

Windows Azure Storage
Windows Azure Storage

Put Stuff in Your Windows Azure Junk Trunk – Repository Base

Alright, so the title is rather stupid, but hey, it’s fun!  🙂

This project I setup to provide some basic functionality with Windows Azure Storage.  I wanted to use each of the three mediums;  Table, Blob, and Queue, and this example will cover each of these things.  The application will upload and store images, provide a listing, some worker processing, and deletion of the images & associated metadata.  This entry is part 1 of this series, with the following schedule for subsequent entries:

Title aside, schedule laid out, description of the project completed, I’ll dive right in!

Putting Stuff in Your Junk Trunk

Create a new Windows Azure Project called PutJunkInIt.  (Click any screenshot for the full size, and also note some of the text may be off – I had to recreate a number of these images)

Windows Azure PutJunkInIt
Windows Azure PutJunkInIt

Next select the ASP.NET MVC 2 Web Application and also a Worker Role and name the projects JunkTrunk and JunkTrunk.WorkerRole.

Choosing Windows Azure Projects
Choosing Windows Azure Projects

In the next dialog choose to create the unit test project and click OK.

Create Unit Test Project
Create Unit Test Project

After the project is created the following projects are setup within the PutJunkInIt Solution.  There should be a JunkTrunk, JunkTrunk.Worker, JunkTrunk Windows Azure Deployment Project, and a JunkTrunk.Tests Project.

Solution Explorer
Solution Explorer

Next add a Windows Class Library Project and title it JunkTrunk.Storage.

Windows Class Library
Windows Class Library

Add a reference to the Microsoft.WindowsAzure.ServiceRuntime and Microsoft.WindowsAzure.StorageClient assemblies to the JunkTrunk.Storage Project.  Rename the Class1.cs file and class to JunkTrunkBase.  Now open up the Class1.cs file in the JunkTrunk.Storage Project.  First add the following fields and constructor to the class.

[sourcecode language=”csharp”]
public const string QueueName = "metadataqueue";
public const string BlobContainerName = "photos";
public const string TableName = "MetaData";
static JunkTrunkBase()
{
CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
{
configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
RoleEnvironment.Changed
+= (sender, arg) =>
{
if (!arg.Changes.OfType()
.Any(change => (change.ConfigurationSettingName == configName)))
return;
if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
{
RoleEnvironment.RequestRecycle();
}
};
});
}
[/sourcecode]

After that add the following blob container and reference methods.

[sourcecode language=”csharp”]
protected static CloudBlobContainer Blob
{
get { return BlobClient.GetContainerReference(BlobContainerName); }
}
private static CloudBlobClient BlobClient
{
get
{
return Account.CreateCloudBlobClient();
}
}
[/sourcecode]

Now add code for the table & queue client and reference methods.

[sourcecode language=”csharp”]
protected static CloudQueue Queue
{
get { return QueueClient.GetQueueReference(QueueName); }
}
private static CloudQueueClient QueueClient
{
get { return Account.CreateCloudQueueClient(); }
}
protected static CloudTableClient Table
{
get { return Account.CreateCloudTableClient(); }
}
protected static CloudStorageAccount Account
{
get
{
return
CloudStorageAccount
.FromConfigurationSetting("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString");
}
}
[/sourcecode]

This class now provides the basic underpinnings needed to retrieve the appropriate information from the configuration.  This base class can then provide that connection information to connect to the table, queue, or blob mediums.

Next step is to create some initialization code to get the containers created if they don’t exist in Windows Azure.  Add a new class file to the PutJunkInIt Project.

JunkTrunkSetup
JunkTrunkSetup

[sourcecode language=”csharp”]
public class JunkTrunkSetup : JunkTrunkBase
{
public static void CreateContainersQueuesTables()
{
Blob.CreateIfNotExist();
Queue.CreateIfNotExist();
Table.CreateTableIfNotExist(TableName);
}
}
[/sourcecode]

Next add the System.Data.Services.Client Assembly to the project.  After adding the assembly add two new classes and name them BlobMeta.cs and Table.cs. Add the following code to the Table.cs Class.

[sourcecode language=”csharp”]
public class Table
{
public static string PartitionKey;
}
[/sourcecode]

Next add another class file and name it BlobMetaContext.cs and add the following code.

[sourcecode language=”csharp”]
public class BlobMetaContext : TableServiceContext
{
public BlobMetaContext(string baseAddress, StorageCredentials credentials)
: base(baseAddress, credentials)
{
IgnoreResourceNotFoundException = true;
}
public IQueryable Data
{
get { return CreateQuery(RepositoryBase.TableName); }
}
public void Add(BlobMeta data)
{
data.RowKey = data.RowKey.Replace("/", "_");
BlobMeta original = (from e in Data
where e.RowKey == data.RowKey
&& e.PartitionKey == Table.PartitionKey
select e).FirstOrDefault();
if (original != null)
{
Update(original, data);
}
else
{
AddObject(RepositoryBase.TableName, data);
}
SaveChanges();
}
public void Update(BlobMeta original, BlobMeta data)
{
original.Date = data.Date;
original.ResourceUri = data.ResourceUri;
UpdateObject(original);
SaveChanges();
}
}
[/sourcecode]

Now add the following code to the BlobMeta Class.

[sourcecode language=”csharp”]
public class BlobMeta : TableServiceEntity
{
public BlobMeta()
{
PartitionKey = Table.PartitionKey;
}
public DateTime Date { get; set; }
public string ResourceUri { get; set; }
}
[/sourcecode]

At this point, everything should build. Give it a go to be sure nothing got keyed in wrong (or copied in wrong). Once assured the build is still solid, add the Blob.cs Class to the project.

[sourcecode language=”csharp”]
public class Blob : JunkTrunkBase
{
public static string PutBlob(Stream stream, string fileName)
{
var blobRef = Blob.GetBlobReference(fileName);
blobRef.UploadFromStream(stream);
return blobRef.Uri.ToString();
}
public static Stream GetBlob(string blobAddress)
{
var stream = new MemoryStream();
Blob.GetBlobReference(blobAddress)
.DownloadToStream(stream);
return stream;
}
public static Dictionary<string, string> GetBlobList()
{
var blobs = Blob.ListBlobs();
var blobDictionary =
blobs.ToDictionary(
listBlobItem => listBlobItem.Uri.ToString(),
listBlobItem => listBlobItem.Uri.ToString());
return blobDictionary;
}
public static void DeleteBlob(string blobAddress)
{
Blob.GetBlobReference(blobAddress).DeleteIfExists();
}
}
[/sourcecode]

After that finalize the Table Class with the following changes and additions.

[sourcecode language=”csharp”]
public class Table : RepositoryBase
{
public const string PartitionKey = "BlobMeta";
public static void Add(BlobMeta data)
{
Context.Add(data);
}
public static BlobMeta GetMetaData(Guid key)
{
return (from e in Context.Data
where e.RowKey == key.ToString() &&
e.PartitionKey == PartitionKey
select e).SingleOrDefault();
}
public static void DeleteMetaDataAndBlob(Guid key)
{
var ctxt = new BlobMetaContext(
Account.TableEndpoint.AbsoluteUri,
Account.Credentials);
var entity = (from e in ctxt.Data
where e.RowKey == key.ToString() &&
e.PartitionKey == PartitionKey
select e).SingleOrDefault();
ctxt.DeleteObject(entity);
Repository.Blob.DeleteBlob(entity.ResourceUri);
ctxt.SaveChanges();
}
public static List<BlobMeta> GetAll()
{
return (from e in Context.Data
select e).ToList();
}
public static BlobMetaContext Context
{
get
{
return new BlobMetaContext(
Account.TableEndpoint.AbsoluteUri,
Account.Credentials);
}
}
}
[/sourcecode]

The final file to add is the Queue.cs Class File. Add that and then add the following code to the class.

[sourcecode language=”csharp”]
public class Queue : JunkTrunkBase
{
public static void Add(CloudQueueMessage msg)
{
Queue.AddMessage(msg);
}
public static CloudQueueMessage GetNextMessage()
{
return Queue.PeekMessage() != null ? Queue.GetMessage() : null;
}
public static List<CloudQueueMessage> GetAllMessages()
{
var count = Queue.RetrieveApproximateMessageCount();
return Queue.GetMessages(count).ToList();
}
public static void DeleteMessage(CloudQueueMessage msg)
{
Queue.DeleteMessage(msg);
}
}
[/sourcecode]

The now gives us a fully functional class that utilizes the Windows Azure SDK. In Part 2 I’ll start building on top of that using the ASP.NET MVC 2 Web Project. Part 2 will be published tomorrow, so stay tuned.

Free Windows Azure Images…

I created these, prospectively for use in presentations or whatever may call for something of this sort.  Since others may be talking about Windows Azure and the innards of the system also, I thought I’d contribute these for free use on the web.  I don’t even really care if you attribute me (even though it would be cool if you did).  I’ll be adding a few more over the next couple of weeks as I prepare for some pending presentations.  (click image to see full size)

Windows Azure
Windows Azure
Spline Architecture
Spline Architecture

Overloaded to Death, “Cloud” Computing is Dead

I’ve gotten to a point where I won’t argue it anymore.  The word Cloud as defined on Wikipedia

“location-independent computing, whereby shared servers provide resources, software, and data tocomputers and other devices on demand, as with the electricity grid. Cloud computing is a natural evolution of the widespread adoption of virtualization, service-oriented architecture and utility computing. Details are abstracted from consumers, who no longer have need for expertise in, or control over, the technology infrastructure “in the cloud” that supports them.“

has been overloaded to such a large degree that it doesn’t actually mean anything anymore.  Cloud computing has become a casualty of media hype.  Utility computing, service oriented architecture, virtualization, and other things still have meaning, but cloud computing really is lost.  We have cloud services in Office 365, which isn’t particularly stored or operated from the cloud.  We have vertically distributed, geographically dependent databases & other services with are called cloud, but also don’t meet the basic definition above.  We have private cloud computing, which also is a perversion of the definition above.  Basically the term cloud has been stuck onto anything technology related.  Your phone, your website, your car, your everything is now all of a sudden supposed to be provided by the cloud.

In laymen’s terms, “The cloud is bullshit.”

I feared this would happen at some point, because the underlying technology of the original ideal around cloud computing is still very intact, very valuable, is drastically disruptive, and marks a massive change in the technology industry.  Virualization, horizontal architectures (hardware & software, PaaS, IaaS, and SaaS are already and will continue to change the enterprise, startup, and worldwide business landscape.

Why am I rambling on about this?  What is my frustration?

When things are redefined that means we, the practitioners, programmers, and users of actual utility, virtualization, and other computing that we had defined as cloud computing will have to find new language to use in defining this revolutionary shift in capabilities.  The specifics that are now needed in each conversation becomes required, the understanding now desperately needs to increase.  Let me provide an example of why.

Imagine a CTO or a CIO that is working with his or her team to identify a way to utilize cloud computing to enhance their services, increase their uptime, and generally utilize this new technology within their enterprise in which they work.  Say they’ve been turned onto the idea of a private cloud.  Not a virtual private cloud that’s hosted in the public cloud (like AWS’s offering), but just a big huge box that sits in their data center.

  • Does it provide location independent computing?  Nope.
  • Does it provide auto-scaling capabilities like AWS, Azure, Rackspace, or others?  Nope.
  • Does it offer the assumes virtualization, service orientation, or other features?  Questionable.
  • Does it offer the resiliency of connectivity like a geographically dispersed utility computing service?  Nope.

But that CTO or CIO will be told over and over that it is a cloud, a private cloud, one of their very own that they can take and hold and hug and love.  But it isn’t cloud computing by the definition on Wikipedia.  It isn’t cloud computing by definition of those hard core developers, web application experts, and others that have been using cloud computing for years now.  It isn’t for those marketers that have been bringing the message of utility, virtualization, and other features.  It’s a misrepresentation by the sales people and media who just want to put stickers with little clouds on everything and shove it out the door to clients who will shell out big money for things that say cloud.

I don’t have much of a problem with that, I’m just perturbed that now I have to explain even more about what cloud computing really is versus what it isn’t.  Now every time I talk to someone new in the field – in addition to the fear & misinformation about security in the cloud (It’s secure already, so drop it – it’s more secure than your data center you can be sure), I now have to explain to people that Office 365 is a SaaS Service, that isn’t particularly in the cloud yet (last I checked it isn’t running on Windows Azure, but it will or should be in the coming future).  I have to explain that Compute & storage that is limited to one geographic area stuck in your data center is called a “computer”, or maybe at best referred to as “High Performance Computer”, but it doesn’t meet the cloud computing definition or the generally accepted understanding of what entail cloud computing.  The number of explanations to keep conversations honest have just drastically increased.

Thanks media, you’ve just made my job harder.  For those still learning in the industry, beware of the misinformation out there, it’s become rampant!

…got that out of my system.  Now on to actual development with utility computing, on virtualized images, within a highly distributed, node based, geographically dispersed computing network system.  <-  How do ya like those apples?  Specific enough?  🙂

Gritty Technical Info on Windows Azure Worker Roles

In the last blog entry, “Gritty Technical Info on Windows Azure Web Roles“, I covered the creation and startup of a web role within the Windows Azure Development Fabric and observing the web role with the Windows Azure Compute Emulator.  In this blog entry I’ll cover the worker role.

Open the Windows Azure Web Role Sample Solution.  Right click on the Windows Azure and select New Worker Role Project.

New Worker Role Project...
New Worker Role Project...

Once the worker role project SampleWorkerRole is added the solution explorer will display the project just like the web role, albeit fewer files.

Solution Explorer
Solution Explorer

Next right click on the SampleWorkerRole instance in the Windows Azure Web Role Sample and select properties.  Now set the instance count to 2 and the VM size to extra large.

SampleWorkerRole Properties
SampleWorkerRole Properties

Click on F5 to run the application.  Now when the application executes the 6 web role instances will start and the 2 worker role instances will start.

Windows Azure Compute Emulator
Windows Azure Compute Emulator

Examine the first worker role instance.

SampleWorkerRole Instance Status
SampleWorkerRole Instance Status

The worker role instance displays a number of new diagnostic messages in a similar way to the web role.  The first half of the trace diagnostics are configuration and instance messages.  The second half of the trace diagnostics are status messages that are printed from the worker role running.

Open up the code in the WorkerRole.cs file in the SampleWorkerRole Project.  As a comparison open the WebRole.cs file in the SampleWebRole Project.

[sourcecode language=”csharp”]
using System.Diagnostics;
using System.Net;
using System.Threading;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace SampleWorkerRole
{
public class WorkerRole : RoleEntryPoint
{
public override void Run()
{
Trace.WriteLine("SampleWorkerRole entry point called", "Information");

while (true)
{
Thread.Sleep(10000);
Trace.WriteLine("Working", "Information");
}
}

public override bool OnStart()
{
ServicePointManager.DefaultConnectionLimit = 12;
return base.OnStart();
}
}
}
[/sourcecode]

In the WorkerRole.cs file the code inherites from the RoleEntryPoint for the WorkerRole. In the WorkerRole Class the Run and OnStart Methods are overridden to provide some basic trace information and set the default connection limit.

The Run method has a basic while loop that updates every 10000 milliseconds, which displays on the Windows Azure Compute Emulator as “Information: Working”.

[sourcecode language=”csharp”]
using Microsoft.WindowsAzure.ServiceRuntime;

namespace SampleWebRole
{
public class WebRole : RoleEntryPoint
{
public override bool OnStart()
{
return base.OnStart();
}
}
}
[/sourcecode]

In the code for the WebRole.cs file there is very little actually going on.  Take a closer look at the OnStart method override.  Technically this code doesn’t even need to be in the generated file and can be deleted, but provides a good starting point to add any other code needed in the start of the web role.

Next I’ll add some code in the worker role to provide a telnet prompt that responds with worker role information.  To work through this exercise completely download a telnet client like Putty (http://www.chiark.greenend.org.uk/~sgtatham/putty/).

If Visual Studio 2010 is no longer open, launch it and open the Windows Azure Web Role Sample Solution.  Right click on the SampleWorkRole Role in the Windows Azure Web Role Sample Project.  Click on the Endpoints tab of the properties window and click on Add Endpoint and call it TelnetServiceEndpoint.

Endpoint
Endpoint

Add a private member and create a run method with the following code.

[sourcecode language=”csharp”]
private readonly AutoResetEvent _connectionWait = new AutoResetEvent(false);

public override void Run()
{
Trace.WriteLine("Starting Telnet Service…", "Information");

TcpListener listener;
try
{
listener = new TcpListener(
RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["TelnetServiceEndpoint"].IPEndpoint) { ExclusiveAddressUse = false };
listener.Start();

Trace.WriteLine("Started Telnet Service.", "Information");
}
catch (SocketException se)
{
Trace.Write("Telnet Service could not start.", "Error");
return;
}

while (true)
{
listener.BeginAcceptTcpClient(HandleAsyncConnection, listener);
_connectionWait.WaitOne();
}
}
[/sourcecode]

After adding this code, add the following code for the role information to write to a stream.

[sourcecode language=”csharp”]
private static void WriteRoleInformation(Guid clientId, StreamWriter writer)
{
writer.WriteLine("— Current Client ID, Date & Time —-");
writer.WriteLine("Current date: " + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString());
writer.WriteLine("Connection ID: " + clientId);
writer.WriteLine();

writer.WriteLine("— Current Role Instance Information —-");
writer.WriteLine("Role ID: " + RoleEnvironment.CurrentRoleInstance.Id);
writer.WriteLine("Role Count: " + RoleEnvironment.Roles.Count);
writer.WriteLine("Deployment ID: " + RoleEnvironment.DeploymentId);
writer.WriteLine();

writer.WriteLine("— Instance Endpoints —-");

foreach (KeyValuePair<string, RoleInstanceEndpoint> instanceEndpoint in RoleEnvironment.CurrentRoleInstance.InstanceEndpoints)
{
writer.WriteLine("Instance Endpoint Key: " + instanceEndpoint.Key);

RoleInstanceEndpoint roleInstanceEndpoint = instanceEndpoint.Value;

writer.WriteLine("Instance Endpoint IP: " + roleInstanceEndpoint.IPEndpoint);
writer.WriteLine("Instance Endpoint Protocol: " + roleInstanceEndpoint.Protocol);
writer.WriteLine("Instance Endpoint Type: " + roleInstanceEndpoint);
writer.WriteLine();
}
}
[/sourcecode]

Now add a handle method for the asynchronous call.

[sourcecode language=”csharp”]
private void HandleAsyncConnection(IAsyncResult result)
{
var listener = (TcpListener)result.AsyncState;
var client = listener.EndAcceptTcpClient(result);
_connectionWait.Set();

var clientId = Guid.NewGuid();
Trace.WriteLine("Connection ID: " + clientId, "Information");

var netStream = client.GetStream();
var reader = new StreamReader(netStream);
var writer = new StreamWriter(netStream);
writer.AutoFlush = true;

var input = string.Empty;
while (input != "3")
{
writer.WriteLine(" 1) Display Worker Role Information");
writer.WriteLine(" 2) Recycle");
writer.WriteLine(" 3) Quit");
writer.Write("Enter your choice: ");

input = reader.ReadLine();
writer.WriteLine();

switch (input)
{
case "1":
WriteRoleInformation(clientId, writer);
break;
case "2":
RoleEnvironment.RequestRecycle();
break;
}

writer.WriteLine();
}

client.Close();
}
[/sourcecode]

Finally override the OnStart() method and setup the RoleEnvironmentChanging Event.

[sourcecode language=”csharp”]
public override bool OnStart()
{
ServicePointManager.DefaultConnectionLimit = 12;

DiagnosticMonitor.Start("DiagnosticsConnectionString");

RoleEnvironment.Changing += RoleEnvironmentChanging;

return base.OnStart();
}

private static void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
{
if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
{
e.Cancel = true;
}
}
[/sourcecode]

Now run the role by hitting F5. When the application runs open up the Windows Azure Compute Emulator to check for the end point and verify the instances of the role are running.

Endpoint displayed in the Windows Azure Compute Emulator
Endpoint displayed in the Windows Azure Compute Emulator

The Service Name should be Windows_Azure_Web_Role, with an Interface Type of SampleWorkerRole running on the tcp://*:1234 URL and the IP is 127.0.0.1P:1234.

SampleWorkerRole
SampleWorkerRole

Click on one of the instances, which should be green, and assure that each has started up appropriately.

PuTTY
PuTTY

Startup a telnet application, such as Putty and enter the information in as shown in screenshot above.

Telnet
Telnet

Start the telnet prompt connecting to the Windows Azure Worker Role. The prompt with the three choices will display. Click to recycle and then display worker role information a few times, just to make sure all the information is available and the worker role telnet application service is working. Select 3 to exit out and the prompt should close while the role continues to run on the development fabric.