Enable your applications for Windows Azure CTP - Live Mesh

Using the Live Framework

Article written by Peter Himschoot, Lieven Iliano and Wim Uyttersprot.

Abstract

Live Mesh, which is part of the Windows Azure CTP, allows you to share data with your devices and others. In this article you will learn about Mesh and how to enable your application for Mesh. As an example we will add Mesh support to the U2U Email Snippets for Outlook 2007 plug-in, which can be found here:

http://www.u2u.be/res/Tools/EMailSnippets.aspx

What is Live Mesh?

In today’s digital lifestyle we all have several devices connected to the internet. Live Mesh is Microsoft’s solution to synchronize files and other data between all your devices and contacts. For example: if you take a picture with your mobile phone, you can use Mesh to sync all pictures automatically to your portable, or to one of your friend’s desktop PC. So with Mesh you can synchronize folders and files between devices and users. In the future we can imagine most of your digital devices will connect to the internet, such as your digital camera, GPS, etc… If you’d like to know what has happened to your data, you can subscribe to notifications. And if you need some data from one of your other devices, you can use Live Mesh to connect to the desktop of this device and access it as if you were sitting in front of it. And even when you don’t have access to one of your devices, you can access your data on the internet because Live Mesh also keeps data synchronized with www.mesh.com. All you need is a Microsoft Live ID. Live Mesh tackles complex problems like synchronization, authentication and authorization…

Mesh Enabling your application

In the Windows Azure CTP Mesh can be extended by either building a web site that uses Mesh (and can be used as part of the Mesh desktop), or you can add Mesh capabilities to an existing application. In this case the Mesh desktop will normally not display your data, but you can always build a Silverlight application to show your data on the Mesh desktop. In this article we will extend an existing application: the U2U Email Snippets for Outlook 2007 plug-in. To do this we need to use the Live Framework.

What is the Live Framework?

The Microsoft Live Framework is the way to program against Mesh, from any platform, programming language, application and device. Currently in CTP, it provides us with a simple and uniform programming model to talk to Mesh using REST (and AtomPub). To use it, you will first need to have access to a developer token, which you can apply for at http://dev.live.com/liveframework/sdk/. Once you have your token you can download the API and the Mesh client. The API’s come with a number of flavors, like .NET, JavaScript, etc… If your flavor isn’t available you still can use ‘bare’ REST to talk to Mesh, so you’re not limited to any technology, as long as it can use HTTP and XML.

For simplicity we’ll be using the .NET libraries to talk to Mesh.

The Live Framework also includes a Mesh client, which is required if you want Mesh to synchronize to your local machine. For the current CTP it is only available for Windows, but in the future you’ll be able to use it for Mobile devices, Apple computers, and so on. In this case you can work offline and go to http://localhost:2048.

The Mesh Client will also install a Windows Service called MOE. This service takes care of synchronization with the cloud and other devices. So once you’re online again MOE will synchronize automatically. You can choose which folders and files to share with your devices and other Mesh users.

Adding Mesh Capabilities to the U2U Email Snippets for Outlook 2007

In this article we want to show you how to Mesh enable your application. By enabling Mesh  your application gets the advantages that you can easily share data on several of your devices, or with other people, without having to build and deploy your own services on the Web. You don't need to worry about authentication, security, etc... As an example we have added Mesh capabilities to our U2U Email Snippets for Outlook 2007. With this add-in you can save your email message, or a part of the message, as a snippet. When composing a new email message you can reuse this ready-made snippet by inserting it in your new email message.

 

Here you see the Outlook editor with the Email Snippets pane enabled. To the right you see a bunch of snippets, which you can drag and drop to your email, inserting standard clauses quite easily.

This utility can store these snippets in a SharePoint list, or in Outlook folders, or in a SQL Server database. By clicking on the Settings link you can choose which provider you’d like:

In this article we’re going to look at how you can store these snippets in Mesh, and its advantages.

Live Framework Resource Browser

While developing with Mesh and the Live Framework SDK you will want to see the result of what you’ve done on Mesh. For this, Microsoft built a tool that allows you to browse Mesh in the cloud and locally. It’s the Live Framework Resource Browser and you will find it in the Tools directory of where you installed the SDK.

With this tool you can look at all the objects stored in the Live Operating Environment. For example if you click on Devices you can see the list of all your devices stored in Mesh. This tool is very handy for debugging and we will  use it to illustrate some of the things we need to do.

Creating the Mesh Provider

The U2U Email snippets for Outlook has a plug-in model where you basically need to create a couple of classes. The main classes are a connection, a snippet service and some UI. Let’s start with the snippet service. This class need to inherit from SnippetService, a class with a number of abstract methods like GetSnippetsFromStoreList, CreateSnippet, UpdateSnippet, etc… You get the idea.

LiveOperatingEnvironment

Live Mesh’s single point of contact is represented through the LiveOperatingEnvironment (LOE) class. You use this class to access your Mesh objects, contacts, etc… The LOE gives us a common way for storing data, will synchronize this data for us on devices and in the cloud, and uses a very simple common programming model using HTTP. Each object stored in the LOE is accessible through its id, which is an Uri.

Through it we first authenticate and then use Mesh. For example:

LiveOperatingEnvironment env = new LiveOperatingEnvironment();

LiveItemAccessOptions accessOptions = new LiveItemAccessOptions(true);

try

{

    String token = new NetworkCredential(Username, Password, Environment)

                    .GetWindowsLiveAuthenticationToken();

    Uri environmentUri = new Uri(Environment);

    env.Connect(token, AuthenticationTokenType.UserToken,

                environmentUri, accessOptions);

}

catch

{

    env.ConnectLocal(accessOptions);

}

 

In this code example, we first create an instance of the LiveOperatingEnvironment class. When we connect we need to pass it a LiveItemAccessOptions instance, which we also create. This is used to automatically load relations, and whether or not we want to receive notifications.

Next we need an authentication token, which we can create using a NetworkCredential instance and GetWindowsLiveAuthenticationToken extension method. We also need the Uri to the Mesh environment, which is currently https://user-ctp.windows.net/.  

We’ve wrapped this code in a try catch block in case there is no network access. In that case we connect to the local environment using the ConnectLocal method. This method doesn’t require credentials because the user should authenticate through the Mesh client.

LOE_Resources.jpg

Figure 1: The Mesh object model

 

Because later on Microsoft could add more features to the LiveOperatingEnvironment Mesh is just one part of the LOE.

For example, you can use the LOE to access all your contacts which we will discuss next.

Inviting contacts

Mesh also makes it very easy to synchronize MeshObjects with other users of Mesh. For this we have an “Invite members to this list…” which opens the Invite Members dialog:

You can type any contact’s e-mail address here, but to simplify things we also add a contacts dialog, showing all your contacts from Windows Live:

Contacts in the list have been blurred for privacy reasons…

Getting the list of contacts from the Live Operating Environment is very easy:

public class MeshContact

{

  public string DisplayName { get; set; }

 

  public string Email { get; set; }

 

  public static List<MeshContact> GetListOfContactsFromMesh(

                                    LiveOperatingEnvironment env )

  {

    if (env == null)

        throw new ArgumentNullException(

          "LiveOperatingEnvironment is required");

    // Ensure contacts have been loaded

    if (!env.Contacts.IsLoaded)

        env.Contacts.Load();

    // Get all contacts

    var contactsQuery = from contact in env.CreateQuery<Contact>()

                        select contact;

    // Convert these contacts to MeshContacts

    List<MeshContact> contactsList = new List<MeshContact>();

    foreach (var contact in contactsQuery.ToList())

    {

      if (contact.Resource.Emails.Count > 0)

      {

        string name = contact.Resource.FormattedName;

        string email = contact.Resource.Emails[0].Value;

        if (string.IsNullOrEmpty(name))

          name = email;

        contactsList.Add(new MeshContact()

        {

          DisplayName = name,

          Email = email

        });

      }

    }

    return contactsList;

  }

}

 

The GetListOfContactsFromMesh method first does a couple of checks, for example if the Contacts have been loaded. If not, it explicitly does the Load. Then we create a query with LINQ to search for all contacts:

var contactsQuery = from contact in env.CreateQuery<Contact>()

                    select contact;

 

Then we execute the query and convert each Contact into a MeshContact (our own type). The query itself is interesting, because it uses LINQ syntax, which is converted by the Live Framework into an appropriate REST call (similar to how a LINQ to SQL query is converted into SQL). This call then executes the query on the server side, which is very efficient.

Using Collections vs. Queries

In the part where we retrieve the list of contacts, we used a query with the CreateQuery<T> method. In this case we didn’t really need a query because we retrieve all contacts anyway. We could have retrieved contacts using the Contacts.Entries collection. So what is the difference? In Live Mesh queries are executed server side, so if there are a lot on entries, a server side query will limit the number of objects sent over the wire. Using the collection we will do a client-side query, requiring Mesh to send over all objects. So, when possible, we would advise using queries.

Storing Data: Mesh Objects, Data Feeds and Data Entries

Live Mesh stores its folders and files as MeshObject instances, and so can you. Actually, each MeshObject is identified through a hyperlink (as an Uri), has data stored as a resource and describes relationships to other resources. Because relations are represented through hyperlinks, we can directly access these relationships without going through the base object, but most of the time we will do this as it is simpler. All other objects in Mesh follow this same pattern, except the data is stored in another resource type. MeshObjects are used as the root for synchronization between devices and the cloud and for sharing with other users.

Each Mesh object has a number of data feeds, and each data feed contains a number of data entries.

Where the MeshObject level is used to define sharing and mapping to devices, the DataFeed is used for synchronization and security. Each DataFeed has security set on it, and will be used to notify your application when synchronization collisions occur. DataEntries are used to keep track of data.

For example, on Mesh each Folder is represented as a MeshObject, and that is why we can share and map each folder individually.  Each Folder MeshObject contains a single DataFeed, and each file in this folder is represented as a DataEntry. When Mesh synchronizes a folder on two devices, any collisions will be at the data entry level, and should normally be resolved automatically through Mesh. This synchronization mechanism will not be explained in this article.

Each of these objects wraps a resource, which is a collection of properties. For example a DataFeed instance has a DataFeedResource instance containing properties like the DataEntriesLink (a collection of Uri’s to link to the data entries). A data entry can store any data you want, and it has special methods to make it really easy to do. For example, any class that is a Windows Communication Foundation DataContract can be stored like this:

DataFeed list = …

DataEntry de = new DataEntry(snippet.Title);

snippet.Id = LastId;

de.Resource.Title = snippet.Id.ToString();

de.Resource.Type = MeshTypes.Snippet;

de.Resource.SetUserData<EmailSnippet>(snippet);

list.DataEntries.Add(ref de);

 

For example, the DataContract used here looks like:

[DataContract(Name = "snippet", Namespace = "urn://www.u2u.be")]

public class EmailSnippet

{

  [DataMember(Name = "text", Order = 0)]

  public string Text

  {

    get;

    set;

  }

  …

}

 

To store some data which is a data contract, we first create a DataEntry instance. To keep track of what is in the data entry we can set the Title and Type properties of the DataEntryResource. The Title and Type can then be used to find all snippets using a query. And then to serialize the data itself, we use the SetUserData generic method.

To retrieve the data we first look for all the wanted data entries using a query (using the CreateQuery method), and then use the GetUserData method to deserialize the object:

DataFeed list = …

var query = from de in list.CreateQuery<DataEntry>()

            where de.Resource.Type == MeshTypes.Snippet

            select de;

foreach (var de in query)

{

  EmailSnippet snippet = de.Resource.GetUserData<EmailSnippet>();

  result.Add(snippet);

}

Creating Snippet Lists

But how can we store our snippets in Mesh? Snippets are grouped into SnippetLists, and we decided to store these in a single MeshObject. MeshObjects can be synchronized to any device and shared between users, ideal since this is what we want to do with SnippetLists. Each SnippetList has a number of snippets, and we’ve decided to store this in a single DataFeed, and make each snippet a DataEntry.

When you create a MeshObject you can specify a title and a type, and this is ideal to separate our MeshObject from anyone else’s. So the code to create a SnippetList:

public override void CreateSnippetList<T>(string listName, out T createdList)

{

  MeshObject newList = new MeshObject(listName);

  newList.Resource.Type = MeshTypes.Root;

  newList.Resource.Title = listName;

  Mesh.MeshObjects.Add(ref newList);

 

  Connection.SnippetListName = listName;

  Connection.SnippetListId =

    new Guid(newList.Resource.Id.ToGuid().ToString());

 

  // Each MeshObject requires at least one DataFeed

  DataFeed list = new DataFeed(Snippets.DefaultList);

  list.Resource.Type = MeshTypes.List;

  list.Resource.Title = Connection.SnippetListName;

  newList.DataFeeds.Add(ref list);

 

  createdList = (T)(object)new MeshSnippetList()

                {

                  Title = listName,

                  Id = newList.Resource.Id.ToGuid()

                };

}

 

In our method we first create a new MeshObject and set its Type and Title. Then we add it to Mesh, simply by adding to a collection. The .NET API’s will create our MeshObject using REST but this is an implementation detail.

The Snippet plugin also uses the concept of a Connection, and when we create a new list we make it the default list by storing its name in the connection. We also store its ID which is easily converted from the GUID uses as the id in Mesh.

Each MeshObject can have a number of DataFeed instances, and we need one. So we create a DataFeed instance and add it to our MeshObject DataFeeds collection. Again this is very simple with the API’s.

Finally we return an empty MeshSnippetList object because this is required by the snippet plugin.

 

Devices

Live Mesh can synchronize any data to any device, but you need to install the local client. Mesh keeps track of all of your registered devices in the Device Ring, which you can see here:

 

In this case you see my mobile and my portable. The mobile phone has the Mesh local client installed while my portable doesn’t. If you want to install the local client, simply click on Add Device:

This will make your device available in the Devices collection. For each device you can discover its name, if it is the local device, if it is connected, etc…

However, creating a MeshObject and MeshDevice is not enough to automatically synchronize it to your device. You need to create a mapping to do this, which is a special kind of relation. So to understand mappings we first need to understand relations:

Setting up relations

Every object in Mesh has an Uri as its unique identifier. Relations use these Uri’s to represent a relation from one object to another. For example, using the Live Framework Resource Browser you can look at one of the Mesh objects:

<entry>

    <id>urn:uuid:7c2365c6-657c-4108-9b1f-74139f95d371</id>

    <title type="text">EmailSnippets</title>

    <published>2009-01-05T22:11:49Z</published>

    <updated>2009-01-05T22:11:49Z</updated>

    <author>

      <name>U2U Trainer</name>

    </author>

    <link rel="LiveFX/DataFeeds" title="LiveFX/DataFeeds"

          href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE/DataFeeds" />

    <link rel="LiveFX/Members" title="LiveFX/Members"

          href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE/Members" />

    <link rel="LiveFX/Mappings" title="LiveFX/Mappings"

          href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE/Mappings" />

    <link rel="LiveFX/Activities" title="LiveFX/Activities"

          href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE/Activities" />

    <link rel="LiveFX/NewsItems" title="LiveFX/NewsItems"

          href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE/News" />

    <link rel="LiveFX/Subscription" title="LiveFX/Subscription"

          href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE/Subscriptions" />

    <link rel="self" title="self" href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE" />

    <link rel="edit" title="edit" href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE" />

    <link rel="LiveFX/AuthorProfile" title="LiveFX/AuthorProfile"

          href="email-info@u2u.be/Profiles" />

    <category term="Snippets_Root_Type" label="Snippets_Root_Type"

              scheme="http://user.windows.net/MeshObject" />

    <category term="MeshObject" label="MeshObject"

              scheme="http://user.windows.net/Resource" />

  </entry>

 

Here we can see that there are relations (<link>) to for example DataFeeds, Members, etc… The handy thing about these Uri's is that they can be used to access these objects directly through REST. So to create a relation we need to get the link to the destination and add it to the relation. Let’s do this with a mapping.

Mapping devices

The snippets utility will store all snippets in the cloud, but if you want, you can have it synchronize to any device in your device ring. Doing this will make the snippets available offline, and on other devices. This makes it easy to share your snippets on your different machines (we don’t have support for our snippets yet on other devices like a mobile phone). To do this we added a “Map to Devices…” button to the configuration dialog:

Clicking this button will open a dialog listing all devices in your device ring and which are currently mapped to your snippet list:

With it you can now map any device so the snippet list is synchronized locally.

Mappings

Each MeshObject has a collection of mappings, which will setup synchronization between the MeshObject and the device. Once you create a MeshObject, you need to use code like this so Mesh will sync the MeshObject to a device:

MeshObject root = …

string deviceName = (MeshDeviceResource)(device.Resource).Title;

Mapping entry = new Mapping(deviceName);

entry.Resource.DeviceLink = device.Resource.SelfLink;

root.Mappings.Add(ref entry);

root.Update();

 

In this code example, we first retrieve the MeshObject somehow. Then we create a Mapping instance passing the name of the device. Next we setup a relation between the mapping and the device. And then we add this to the collections of mappings. Next time Mesh synchronizes, it will put this MeshObject on that device for local access.

This will then add a relation as a link in the mappings object:

<entry>

    <id>urn:uuid:b04330f0-14d9-4bfe-b86c-6b9d21289e44</id>

    <title type="text">U2U-PC</title>

    <published>2009-01-13T13:51:01Z</published>

    <updated>2009-01-13T13:51:01Z</updated>

    <author>

      <name>U2U Trainer</name>

    </author>

    <link rel="LiveFX/MeshObject" title="LiveFX/MeshObject"

          href="Mesh/MeshObjects/YZSSG7D4MUEEDGY7OQJZ7FOTOE" />

    <link rel="LiveFX/Device" title="LiveFX/Device"

          href="Mesh/Devices/4A3SOOKHK64UJHICRS2MV7R2QQ-QVSQREPBQKIE7JFE3JGZ6I65OM" />

    …

  </entry>

 

Members

If you want to, you can share a MeshObject between different users. For this to work we need to add a Member in the MeshObject Members collection, and send an invite. Mesh will always send an invite, but we can customize this invite to a certain extent. For example we can change to expiry date for the invite.

To do this, first we need a MeshObject (root) and a role (roleType).

RoleType roleType = …

MeshObject root = …

 

Next we check if the root’s Members have been loaded, and load them if needed:

if (!root.Members.IsLoaded)

    root.Members.Load();

 

Then we create a MemberResource to represent the user to share with:

 

MemberResource pendingMemberResource = new MemberResource();

pendingMemberResource.Title = name;

pendingMemberResource.Email = email;

pendingMemberResource.Role = roleType;

 

And we create an invitation with an expiry date of 7 days:

     

Invitation invitation = new Invitation();

invitation.Email = email;

DateTimeOffset expires = DateTimeOffset.UtcNow.Add(new TimeSpan(7, 0, 0, 0));

invitation.Expires = expires;

 

Then we create the Member, and add it to the Members collection of our Mesh object:

 

Member shareWith = new Member(pendingMemberResource);

shareWith.Resource.PendingInvitation = invitation;

 

root.Members.Add(ref shareWith);

root.Update();

 

Getting all SnippetLists from Mesh

Creating a SnippetList is one thing, but later we will need to get the list of all the SnippetLists stored in Mesh. Again this is simple; with the API we can build a query which will be executed at the server side – so very efficient – to retrieve all MeshObjects with the correct type:

var query = from mo in Mesh.CreateQuery<MeshObject>()

            where mo.Resource.Type == MeshTypes.Root

            select mo;

 

Because the snippet tool doesn’t understand MeshObjects we need to convert each one to one of our own types. You cannot do this immediately, because the IQueryable implementation doesn’t support this. To we execute our prior query using ToList() and then convert the MeshObjects into MeshSnippetList instances.

var toList = from mo in query.ToList()

             select new MeshSnippetList()

             {

               Title = mo.Resource.Title,

               Id = new Guid(mo.Resource.Id.Substring(9))

             };

 

Getting the Snippets from the SnippetList

Once the uses selects one of the lists, we need to retrieve all snippets stored there. This is very similar: we create a query to search for the right DataEntry instances and convert them to our own representation:

private EmailSnippetCollection CollectMeshSnippets()

{

  EmailSnippetCollection result = new EmailSnippetCollection();

  DataFeed list = GetCurrentSnippetListNameDataFeed();

  var query = from de in list.CreateQuery<DataEntry>()

              where de.Resource.Type == MeshTypes.Snippet

              select de;

  foreach (var de in query)

  {

    EmailSnippet snippet = de.Resource.GetUserData<EmailSnippet>();

    CheckLastId(snippet.Id);

    result.Add(snippet);

  }

  return result;

}



 



Copyright © 1999-2010 by U2U