Silverlight in the Mesh and the “Cloud” – Silverlight Mesh Enabled Web Application and Azure Services (Part 4) - DevCorner

Silverlight in the Mesh and the “Cloud” – Silverlight Mesh Enabled Web Application and Azure Services (Part 4)

Hello,

In previous parts (Part 1, Part 2, Part 3) we have seen how to create Azure Hosted Service, How to Access Azure Data Services Blob data, how to create SMEWA-like application which uses those services.

While creating Silverlight application I’ve faced some problems – I couldn’t use Live Framework along with WebServices which returns non primitive types. Then I solved it by creating and hosting standard (non SMEWA) Silverlight application in the Mesh. This time I want to build “Cloud Store” client application which will use data stored in blob by Management application (created here) and provide UI to browse blob contents, “purchase” blob content and save it in Mesh Live Desktop folder. Once this Live Desktop folder will be synchronized with real folder at clients machine (with Live Mesh client installed on user machine) the purchases will arrive to the client’s machine.

This time I will not be able to use the “cheat” I’ve used in previous part – this time I really need Live Framework services to have an access to Live Desktop folders, to be able to interact with items there. From the other side I still want to use my Azure hosted services. So, let’s start…

My UI will look like this:

image

Well, build this UI is pretty easy if not take to account the fact, that I cannot receive the List<ContentEntry> I used in Part 2. I decided to do the following trick: serialize the web method result (with SOAP serializer) by myself and will use LINQ to XML to read it at the second side (close to what any web service call and automatically generated proxy does). The my function which returns me the files list turned to be like follows:

[WebMethod]
public string GetFiles()
{
  // Get the configuration from the cscfg file
  StorageAccountInfo accountInfo = StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration();
 
  // Container names have the same restrictions as DNS names
  BlobStorage blobStorage = BlobStorage.Create(accountInfo);
  BlobContainer _Container = blobStorage.GetBlobContainer(RoleManager.GetConfigurationSetting("ContainerName"));
 
  IEnumerable<object> blobs = _Container.ListBlobs(string.Empty, false);
  List<ContentEntry> filesList = new List<ContentEntry>();
 
  foreach (object o in blobs)
  {
    BlobProperties bp = o as BlobProperties;
    if (bp != null)
    {
      BlobProperties p = _Container.GetBlobProperties(bp.Name);
      NameValueCollection fileEntryProperties = p.Metadata;
      filesList.Add(new ContentEntry() { BlobName = p.Name, FileUri = bp.Uri.ToString(), FileName = fileEntryProperties["FileName"], Submitter = "ALEX" });
    }
  }
 
  //WORKAROUND - SEEMS THERE IS NO WAY TO USE LIVE MESH ALONG WITH COLLECTIONS (?)
  ContentEntry[] files = filesList.ToArray<ContentEntry>();
  XmlSerializer serializer = new XmlSerializer(filesList.GetType());
  MemoryStream stream = new MemoryStream();
  serializer.Serialize(stream, filesList);
  string sRes = "";
 
  using (StreamReader reader = new StreamReader(stream))
  {
    stream.Position = 0;
    sRes = reader.ReadToEnd();
  }
 
  stream.Close();
  stream.Dispose();
 
  return sRes;
}

And in client UI SMEWA i created corresponding ContentEntry class and used LINQ to XML to process the response as follows:

private void LoadMeshItems()
{
  storeBL.ContentHandlerSoapClient client = new ContentDownloaderSilverlight.storeBL.ContentHandlerSoapClient();
  client.GetFilesCompleted += (s, e) =>
    {
      if (null == e.Error)
      {
        //Get the string result
        string res = e.Result;
 
        //Load in and use LINQ to XML to create the list of ConentEntry items
        XDocument xmlTopics = XDocument.Parse(res);
        var files = from file in xmlTopics.Descendants("ContentEntry")
                    select new ContentEntry
                    {
                      BlobName = (string)file.Element("BlobName"),
                      FileName = (string)file.Element("FileName"),
                      FileUri = (string)file.Element("FileUri"),
                      Submitter = (string)file.Element("Submitter")
                    };
 
        lst.ItemsSource = files;
      }
      else
      {
        //Take care of exception received from server
      }
    };
 
  client.GetFilesAsync();
}

Now I had the list of files being displayed.

Then I created the function, which iterates over Live Mesh Folders (I didn’t create some recursive mechanism, because I wanted to use only a single level folder hierarchy in this simple application) and adds them as a nodes in my TreeView control. The I’ve also added the items in folder as a leaves of this TreeView Node(s).

//Get Mesh application and the environment
MeshApplicationService meshApp = Application.Current.GetMeshApplicationService();
MeshObjectsCollection = meshApp.LiveOperatingEnvironment.Mesh;
 
//Get all root level live mesh folders, which this application could "see"
var folders = from a in MeshObjectsCollection.MeshObjects.Entries
              where a.Resource.Type == MeshConstants.LIVE_MESH_FOLDER
              select a;
 
foreach (var folder in folders)
{
  //Add them to the tree
  TreeViewNode node = new TreeViewNode();
  node.EnableCheckboxes = true;
  node.Text = folder.Resource.Title;
  node.ID = folder.Resource.Id;
  node.Icon = "images/Folder-Closed-16x16.png";
  node.IconExpanded = "images/Folder-Open-16x16.png";
  node.Tag = folder.Resource.Type;
  treeFolders.TreeViewNodes.Add(node);
 
  //Get root level folder data feeds
  var fileFeed = from a in folder.DataFeeds.Entries
                 where a.Resource.Type == MeshConstants.LIVE_MESH_FILES
                 select a;
    
  //Iterate oved files in the DataFeed
  var files = from a in fileFeed.First().DataEntries.Entries
              where a.Resource.Type == MeshConstants.File
              select a;
 
  foreach (var file in files)
  {
    //Add files to the current folder as a child
    TreeViewNode filenode = new TreeViewNode();
    filenode.EnableCheckboxes = false;
    filenode.Text = file.Resource.Title;
    filenode.ID = file.Resource.Id;
    filenode.Icon = "images/Document-16x16.png";
    filenode.Tag = file.Resource.Type;
    node.TreeViewNodes.Add(filenode);
  }
}

Now when I have the list of files I’d like to download one and save it in selected Live Desktop folder. First of all to save data in live desktop we need to do the following steps:

1. Create MeshObject (if still not exists). The type of this object should be “LiveMeshFolder”. This will be the Root folder, which will have all the downloaded files:

//Get current active Mesh Application Services
MeshApplicationService meshApp = Application.Current.GetMeshApplicationService();
 
//Get the Mesh object from Live Environment
Mesh mesh = meshApp.LiveOperatingEnvironment.Mesh;
 
// Create folder object. title is a name of new folder.
MeshObject meshObject = new MeshObject(title);
 
// It is a mesh folder
meshObject.Resource.Type = MeshConstants.LIVE_MESH_FOLDER;
 
// Add folder to collection of mesh objects. mesh is from Mesh type.
mesh.MeshObjects.Add(ref meshObject);

2.Create DataFeed which will accept our data (files). In my case Type of the DataFeed will be “LiveMeshFiles”, and Handler “FileSystem”:

// Create feed for files (required)
DataFeed fileDataFeed = new DataFeed(MeshConstants.LIVE_MESH_FILES);
 
// Set type and handler type (required)
fileDataFeed.Resource.Type = MeshConstants.LIVE_MESH_FILES;
fileDataFeed.Resource.HandlerType = MeshConstants.FILE_HANDLER_TYPE;
 
// Add new data feeds to collection
meshObject.DataFeeds.Add(ref fileDataFeed);

3. Now I can add data to my DataFeed.

//Instantiate a new DataEntry and set it's values
fileDataFeed.DataEntries.Add(...);

This time I had to stop to evaluate my options. DataEntries.Add() has couple overloads methods. All of them have only 3 different ways to get data – another parameters are metadata of the DataEntry.

First and not relevant for my case is accepts another DataEntry. Second (and most obvious option) to use in my case was overload which receives URI object. This is something I had in my hands, from ContentEntry.FileUri of selected List Item. When trying to use it I’ve got exception:

"Access is denied"

It was strange, because the very same FileUri was used as a source for preview Image on the page:

ImageSource source = new BitmapImage(new Uri((lst.SelectedItem as ContentDownloaderSilverlight.ContentEntry).FileUri));
imgPreview.Source = source;

Then I tried to use third type of overloads, which get stream. I tried to get it  via WebClient.OpenReadAsync(FileUri). This time  I’ve got SecurityException because I have no clientaccesspolicy.xml at http://devcorner.cloudapp.net and I cannot put it there because it is actually generated domain to provide endpoint to my Azure Data Services… But how ImageSource gets data from there?

Well, after “playing” a little bit with tools like Fiddler 2 and Silverlight Spy I had to conclude, that ImageSource, BitmapImage, probably MultiScaleImage and others seems to make an actual call through the browser's Javascript network stack using XMLHttpRequest. Not fair, but that’s what it is… :(

So how I solved my problem?

I created ASP.NET generic handler (*.ashx) and add it to my Azure Hosted Services. This solved me the problem with security exception and also gave me a way to return byte[] of my blob file stream:

[WebService(Namespace = "http://devcorner.cloudapp.net/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Images : IHttpHandler
{
  public void ProcessRequest(HttpContext context)
  {
    string FileName;
 
    FileName = HttpContext.Current.Request.QueryString["fileName"];
 
    // Get the configuration from the cscfg file
    StorageAccountInfo accountInfo = StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration();
 
    // Container names have the same restrictions as DNS names
    BlobStorage blobStorage = BlobStorage.Create(accountInfo);
    BlobContainer _Container = blobStorage.GetBlobContainer(RoleManager.GetConfigurationSetting("ContainerName"));
 
    if (_Container.DoesBlobExist(FileName))
    {
      //Get Blob data to memory stream
      MemoryStream stream = new MemoryStream();
      BlobContents contents = new BlobContents(stream);
      BlobProperties props = _Container.GetBlob(FileName, contents, false);
 
      if (null != contents)
      {
        if (stream.Length > 0)
        {
          //write stream bytes back to output stream and mark content typa as JPEG image
          context.Response.ContentType = "image/jpeg";
          context.Response.OutputStream.Write(stream.ToArray(), 0, (int)stream.Length);
        }
 
        stream.Close();
        stream.Dispose();
      }
    }
  }
 
  public bool IsReusable
  {
    get
    {
      return false;
    }
  }
}

At the UI SMEWA side i used the WebClient to access generic handler and save the stream to Live Desktop when it will arrive:

string fileUri = "http://????????????.cloudapp.net/Images.ashx?fileName=" + (lst.SelectedItem as ContentEntry).BlobName;
WebClient downloader = new WebClient();
downloader.OpenReadCompleted += (s, args) =>
{
  if (null == args.Error)
  {
    Stream str = args.Result;
 
    //If got stream from fileHandler save it to the mesh folder
    if (null != str)
    {
      //Save logic here....           
    }
  }
};
downloader.OpenReadAsync(new Uri(fileUri, UriKind.Absolute));

That’s it – mission accomplished! File arrives from the Blob (via Hosted Services) and could be stored in  Mesh Live Desktop Folder!

With the SMEWA from Part 3 and Hosted Services from Part 2 it makes the complete cycle I promised in Part 1:

Local file from any client machine uploaded to the Cloud Storage with SMEWA by user with access to Management UI SMEWA, then at different machine it was downloaded from the Cloud Storage (Blob) with different SMEWA and saved in Live Desktop of user with access to this Client SMEWA. If Live Desktop folder is synchronized with real folder on client machine (via Live Mesh Client installed there) it will be synchronized like any other Live Mesh conetent.

Client SMEWA application execution video is here:

Cloud Store Clinet SMEWA - Demo

 

Enjoy,

Alex

Published Friday, January 23, 2009 9:54 AM by Alex Golesh

Comments

# Domain Talk &raquo; Blog Archive &raquo; Silverlight in the Mesh and the ???Cloud??? ??? Silverlight Mesh Enabled Web Application and Azure Services (Part 4)

Pingback from  Domain Talk  &raquo; Blog Archive   &raquo; Silverlight in the Mesh and the ???Cloud??? ??? Silverlight Mesh Enabled Web Application and Azure Services (Part 4)

# Silverlight Cream for January 24, 2009 -- #496

In this issue: Jonathan van de Veen, David Justice, Andrej Tozon, Patrick Keating, Timmy Kokke, Shawn

Saturday, January 24, 2009 11:10 PM by Community Blogs

# re: Silverlight in the Mesh and the “Cloud” – Silverlight Mesh Enabled Web Application and Azure Services (Part 4)

Very Nice =)

Thursday, February 19, 2009 2:50 PM by Qasim

# re: Silverlight in the Mesh and the “Cloud” – Silverlight Mesh Enabled Web Application and Azure Services (Part 4)

Interessante Informationen.

Friday, March 06, 2009 6:04 PM by ...

# re: Silverlight in the Mesh and the “Cloud” – Silverlight Mesh Enabled Web Application and Azure Services (Part 4)

Alex this is quiet interesting...Can you please send me this sample end to end??

Thursday, May 14, 2009 7:02 PM by VJ

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above: