I will show in this post how to extend the basic Windows Azure Project Template and make the solution a bit more intelligent. I will do this using a new and upcoming .NET Framework 4 technology; Managed Extensibility Framework (MEF). MEF Preview 5 is targeting .NET Framework 3.5 and is even developed under MSPL license and so fits like a glove into Windows Azure.
Intro
My overall goal is to create a Windows Azure project template that:
- enables testability
- abstracts away storage
- is extensible and easy to evolve during development
I will work in the order or the three steps here outlined and I will use, as I said, one unifying technology to enable it all – MEF.
This post is the first step – making the Windows Azure template and the Windowz Azure SDK testable!
codeplex.com/azurecontrib
I am a developer (noopman) at the CodePlex Windows Azure Contrib project created by Steven Smith. Thank you Steven for taking me aboard as a developer. The project has the Apache License 2.0 (Apache). If this would cause a problem for anyone I will gladly put my code samples in any other project under any other license you wish. Just contact me through the site!
I have packaged it all as a lib assembly that you can download and run for yourself. There are also tests and a working template Windows Azure project.
You can download the solution in full here Testable Windows Azure Release and read the facts about it here Testable Windows Azure.
Since MEF is under MSPL I am not uploading that source or artifacts as part of my solution.
Versions
I am using the MEF Preview 5 bits for this project. MEF Preview 5 is available here on the the MEF codeplex site. In order not to have to bother with versions I am not uploading this binary or source code to the Windows Azure codeplex site. (Yeah I am also a coordinator on the MEF contrib project.)
The Windowz Azure SDK runs on .NET Framework 3.5. The two downloads you need for this are available on the Windows Azure site. (Click “Try it Now” and download and install Windows Azure Tools for Microsoft Visual Studio and Windows Azure SDK.)
Anecdotes
Before we begin let me sum up some experiences and impressions of the current state of Windows Azure affairs. (Scroll down for action!)
Working with Windows Azure you quickly realize that once again you’re back to basics. Windows Azure is in CTP and the SDK for it is small. The Visual Studio template is also very basic if not pretty stupid even. Stil the few lines of code in there are not exactly testable.
It sounds now as if I have something against Windows Azure and I don’t. Nothing could be further from the truth. I think Windows Azure shows HUGE promise and has that the architecture model and services planned and in operation for the Microsoft Cloud are IMMENSELY impressing! This is the reason I am now digging into it. Feedback is the best form of compliment I can offer.
Just to continue to bitch a little more I have one more thing to say about the Windows Azure CTP. Well two actually but the second point is quite petty:
1) Why, oh why, did you not develop the minimal SDK that is the Windows Azure SDK in a TDD fashion? Haven’t you realized by now that this is the FIRST thing the early adopters of Windows Azure will ask for/complain about? Really, I understand all about getting a CTP out of the door but still… Developing a throw away solution first and later replacing it with a tested version is NOT faster. I repeat; it is NOT faster to skip the tests at first and later add testability! All evidence points to it being SLOWER! Another point is that I, as an early adopter, have to code for testability on my own (which I do in this post). So does every other responsible developer out there even though this is not our responsibility. It is yours – Azure team! Please fix testability on Windows Azure as a high priority activity! If you like you can use my templates below- I will gladly give them to you. Because I live with and love what you do so much I am right here in your face requiring from you nothing less than I require of myself!
2) The Microsoft.ServiceHosting.ServiceRuntime.RoleManager class is not a static class but it has only static members. No instance members. This is of course a mistake which I’m sure will be corrected real soon. I propose you make the RoleManager services into a real instantiable service (not a static class) for increased testability. (Same as I do below.)
In all fairness the RoleManager does have an .IsRoleManagerRunning static property that answers false if the RoleManager is not running in the hosted “fabric”. But the following is a pattern I’d like to avoid in my code! In fact I still use that pattern but wrap it in a service so at least I don’t have to see it and code it over and over again.
if (RoleManager.IsRoleManagerRunning)
RoleManager.WriteToLog("Information", "my message to log");
OK – harsh comments done! ;~) Again; I love this work else I would not have bothered to comment! I just want to do my piece to influence the direction of Azure.
Enough talk :Let’s get crackin’!
This following will introduce a testable service for the Role Manager. It will enable MEF in the Windows Azure Web and Worker Role templates. Finally it will import the Azure service into the template through MEF.
Note: My sample solution will be very shallow and it is only the first step of three. I will update this post with new references as I go.
The IRoleManager service contract
In order to make the Windows Azure project template testable the first thing you have to do is create some wrappers around the Windows Azure SDK. Also I want to apply MEF to the code so that I can begin to resolve dependencies in a testable fashion.
Since the Microsoft.ServiceHosting.ServiceRuntime.RoleManager has static service like methods my initial idea was to try to export the methods of the RoleManager as service methods using MEF. Reason for this initial approach is that MEF Preview 5 currently supports the exporting of static methods.
Here is a simple sample that exports a static method from the nested static class to the test class that imports. The sample is very silly but shows that MEF actually does export static methods:
[TestClass]
public class MEFTests
{
[TestMethod]
public void MEF_can_import_static_methods()
{
var catalog = new TypeCatalog(typeof(InternalContainer));
CompositionContainer container = new CompositionContainer(catalog);
container.SatisfyImports(this);
Assert.AreEqual("Hello foo!", Hello("foo"));
}
public static class InternalContainer
{
[Export("MEFTests.Hello")]
public static string ExportHello(string name)
{
return string.Format("Hello {0}!", name);
}
}
[Import("MEFTests.Hello")]
public Func<string, string> Hello { get; set; }
}
The call to the extension method container.SatisfyImports(this); is a useful way to satisfy imports on a part without adding it to the MEF catalog.
Note: In MEF all classes that participate are simply called parts. Exports (ExportAttribute) are contracts a part offers to some one else. Imports (ImportAttribute) are contracts that you require. MEF is then the glue that creates exports and inject them into imports. (Read more on MEF at the MEF site.)
This is, I thought, exactly what I needed for my “Windows Azure with testable RoleManager” scenario. This turned out to be a blow in the air since it is quite tricky to actually export some part that is not mine to control. The Windows Azure SDK is not my assembly and the RoleManager is a compiled resource. I was stumped. Wasn’t MEF supposed to be über easy to use? Turns out it was my approach that was wrong. I had the opportunity to hang out for a few hours with my friend Glenn Block as he was passing by Sweden on his way from NDC to a User Group Tour in Poland. We spend the time we had taking a lunch together and also pairing up like the two geeks we in my dining room at home.
This is the skinny: MEF can export static methods but this is not the recommended approach. Instead the better way to do it is to create a service for the methods you need, implement a wrapper class that exports the interface as a contract and finally import this interface where you need it. I have seen Glenns logic in this and I have forgiven him for making me think. ;~)
I created a contract for the RoleManager services:
/// <summary>
/// Contract for a service of a testable <see cref="RoleManager"/>.
/// </summary>
[PartExportsInherited]
[Export(typeof(IRoleManager))]
public interface IRoleManager
{
bool IsRoleManagerRunning { get; }
void WriteToLog(EventLogName eventLogName, string message);
string GetConfigurationSetting(string roleSettingName);
ILocalResource GetLocalResource(string localResourceName);
void RegisterShutdownEvent(string shutDownEventName);
}
The awesome part here is that you can set the ExportAttribute on the contract and by using the additional attribute PartExportsInherited which “marks a class whose sub-classes will inherit its exports”. Please note this is a feature of MEF Preview 5 which will change in MEF Preview 6 and the later release. The same kind of functionality will be available but the mechanism will be different.
EventLogName
My IRoleManager contract is the same as the interface of the RoleManager class’ static methods. With one small difference. I have a slightly different version of WriteToLog method than does the RoleManager class. Reason for this is that the RoleManager takes a string eventLogName and I find this less uesful. Behind the scenes there are presently five possible values of eventLogName that you can use; "Information", "Warning", "Verbose", "Error" and "Critical". The last value “Critical” is special in that using it will actually cause a manual review by a Microsoft operator. (In order to understand this you have to read the SDK documentation.)
Note: Do NOT log to the "Critical" event log unless it is a very critical situation which puts the Azure hosting fabric at risk (or something of similar severity)!
My point is that the set of possible categories is a closed set of strings but it is possible to call the WriteToLog function with any string value. Writing to an arbitrary event log “foo” will throw a RoleException:
// The following throws RoleException "foo event does not exist."
Microsoft.ServiceHosting.ServiceRuntime.RoleManager.WriteToLog("foo", "bar");
My EventLogName class is a sealed class with a private constructor that only makes instances available as public static members on itself. The EventLogName class closed set. Furthermore it has an explicit conversion to string which makes it possible to use in calls to RoleManager.WriteToLog:
// The following works due to implicit conversion between EventLogName and string
Microsoft.ServiceHosting.ServiceRuntime.RoleManager.WriteToLog(EventLogName.Information, "bar");
The result is a strongly typed EventLogName class that specifies the only available event categories in the contract.
namespace AzureContrib.ServiceHosting.ServiceRuntime
{
/// <summary>
/// Event log names for available event logs in Windows Azure. This provides a type safe implementation of the possible event logs.
/// </summary>
public sealed class EventLogName : IEquatable<EventLogName>
{
/// <summary>
/// Write to the Information event log
/// </summary>
public static EventLogName Information = new EventLogName("Information");
/// <summary>
/// Write to the Warning event log
/// </summary>
public static EventLogName Warning = new EventLogName("Warning");
/// <summary>
/// Write to the Verbose event log
/// </summary>
public static EventLogName Verbose = new EventLogName("Verbose");
/// <summary>
/// Write to the Error event log
/// </summary>
public static EventLogName Error = new EventLogName("Error");
/// <summary>
/// Log entries marked with the Critical designation are also raised as alerts to the service via the alerting interface.
/// Because these alerts must be reviewed by an operator, Critical messages should be used only for urgent notifications.
/// </summary>
public static EventLogName Critical = new EventLogName("Critical");
private readonly string eventLogName;
private EventLogName(string eventLogName)
{
this.eventLogName = eventLogName;
}
/// <summary>
/// The <see cref="EventLogName"/> as a string
/// </summary>
/// <returns>a string</returns>
public override string ToString()
{
return eventLogName;
}
/// <summary>
/// Convert an <see cref="EventLogName"/> to a <see cref="string"/>
/// </summary>
/// <param name="eventLogName">To convert</param>
/// <returns>the <see cref="EventLogName"/> as a <see cref="string"/></returns>
public static implicit operator string(EventLogName eventLogName)
{
return eventLogName.ToString();
}
/// <summary>
/// Enumerate all the <see cref="eventLogName"/>s
/// </summary>
/// <returns>The available event log names</returns>
public static IEnumerable<EventLogName> EventLogNames
{
get
{
return (from field in typeof(EventLogName).GetFields()
where field.FieldType == typeof(EventLogName)
select (EventLogName)field.GetValue(null));
}
}
/// <summary>
/// Equality between two <see cref="EventLogName"/>
/// </summary>
/// <param name="other">The other one to compare with.</param>
/// <returns>True if the <see cref="EventLogName"/>s are equal</returns>
public bool Equals(EventLogName other)
{
return ToString() == other.ToString();
}
/// <summary>
/// Equality between an <see cref="object"/> and an <see cref="EventLogName"/>
/// </summary>
/// <param name="obj">The <see cref="object"/> to compare with.</param>
/// <returns>True if the <see cref="object"/> and the <see cref="EventLogName"/>are equal</returns>
public override bool Equals(object obj)
{
EventLogName o = obj as EventLogName;
return o != null && Equals(o);
}
/// <summary>
/// The hash code for an <see cref="EventLogName"/> is the hash code value of it's name
/// </summary>
/// <returns>The hash code</returns>
public override int GetHashCode()
{
return ToString().GetHashCode();
}
}
}
All in all I am pretty happy with my EventLogName solution! ;~)
OK – now let’s put IRoleManager to good use!
Enabling MEF in the Windows Azure Web Role and Worker Role
The Windows Azure Web Role in the CTP is not as open and available as a standard Web Application Project. Right now it affects us in the way that you cannot access your Web Application through an ASP.NET Application File (global.asax). You can configure your application through the web.config. So my first thought was to create a custom IHttpHandler or IHttpModule and start hooking up my MEF container through one of those methods. Then I realized it would be overkill for my small sample to introduce more concepts. I stayed with the simple “add a base class and create the container there”-approach. I would have wanted to create a MEF container once for the application and then call .SatisfyImports on each page request. Now I create a catalog for each page request. (Sorry - I applied the KISS principle.)
namespace AzureContrib.Web.UI
{
/// <summary>
/// A page that simply imports the <see cref="IRoleManager"/>
/// </summary>
public class Page : System.Web.UI.Page
{
/// <summary>
/// The <see cref="IRoleManager"/>
/// </summary>
[Import]
public IRoleManager RoleManager { get; set; }
/// <summary>
/// Set up the <see cref="IRoleManager"/>
/// </summary>
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
var catalog = new TypeCatalog(typeof(RoleManagerService));
var container = new CompositionContainer(catalog);
container.SatisfyImports(this);
RoleManager.WriteToLog(EventLogName.Information, "Initiated the page service.");
}
}
}
Now all I have to do is inherit this base page class and use my RoleManager service from the base property:
namespace SampleService_WebRole
{
public partial class _Default : AzureContrib.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
RoleManager.WriteToLog(EventLogName.Information, "Default page called");
}
}
}
For the WorkerRole a good likeness is to liken it with a Windows Service. The Service starts and runs it’s own course and reports it’s health status. Then it waits for incoming events (which in Windows Azure means polling a REST based Windows Azure Storage Queue – here is an excellent video from Mix 08 on Windows Azure Storage by Brad Calder Director/Architect). And processes message form said queues doing the work they are designed for.
Note: My ultimate goal here is to enable testability, persistence ignorance and extensibility for the Windows Azure Web And Worker Roles and now I have again touched upon this scenario. All of that will be revealed in future posts.
So in the CTP of Windows Azure the WorkerRole has a classic .Start() method and does a “wile true – work/sleep loop”. Enabling MEF in this scenario is even easier than in the Web Role above; the .Start() method is the application. Here it makes perfect sense to create an abstract base class and add the IRoleManager service:
namespace AzureContrib.ServiceHosting.ServiceRuntime
{
/// <summary>
/// A Worker Role that uses the <see cref="IRoleManager"/> service for testability
/// </summary>
public abstract class WorkerRole : RoleEntryPoint
{
/// <summary>
/// The <see cref="IRoleManager"/>
/// </summary>
[Import]
public IRoleManager RoleManager { get; set; }
/// <summary>
/// Run at start up of the role
/// </summary>
public override void Start()
{
var catalog = new TypeCatalog(typeof(RoleManagerService));
var container = new CompositionContainer(catalog);
container.SatisfyImports(this);
RoleManager.WriteToLog(EventLogName.Information, "Initiated the Worker Role.");
}
}
}
And the WorkerRole concrete class looks like this:
namespace SampleService_WorkerRole
{
public class WorkerRole : AzureContrib.ServiceHosting.ServiceRuntime.WorkerRole
{
public override void Start()
{
RoleManager.WriteToLog(EventLogName.Information, "Worker Process entry point called");
while (true)
{
Thread.Sleep(2000);
RoleManager.WriteToLog(EventLogName.Information, "Working");
}
}
public override RoleStatus GetHealthStatus()
{
// This is a sample worker implementation. Replace with your logic.
return RoleStatus.Healthy;
}
}
}
Where is the testable part?
The RoleManagerService is implemented with a default implementation; to use the classic RoleManager class with static methods; I have a nested wrapper for the default implementation. It has a public hook; if you know about it; where you can inject a mock version of the same service. Thus I can write tests that mock out the static method implementation and replace it with a null RoleManager if that suits my purpose. This means I can call RoleManager.WriteToLog all day from my tests without anything bad happening.
Just to make the picture more complete;
Anywhere in your code that you want to make your IRoleManager service available – all you have to do is call the container.SatisfyImports() method (or any other overloads like .GetExportedObject depending on your context).
Summary
This was a long post! ;~) I hope I have showed to your satisfaction 1) Windows Azure can be made testable with few lines of code. 2) Extending with MEF is a breeze. 3) Hooking up Windows Azure for testability, persistence ignorance and extensibility makes sense.
In my posts that follow this one I will add persistence ignorance and extensibility.
So Glenn Block and Steve Marx your two babies have now been married off to each other; Hope they are very happy together and that this will give birth to lots of beautiful (testable, persistence ignorant, extensible) babies!" ;~)
HTH - Cheers,
M.
posted @ Friday, July 03, 2009 8:21 AM