Azure Applications with Delphi XE

In Delphi XE there is native and specific support for three of the Microsoft's Azure cloud services: Queue, Tables, and Blobs. This article provides a short introduction to these components and the underlying classes, and shows a practical demo of how to use the Azure service components to publish some database data on a hosted database, and also consume that data.

This article is based on the last section of my new Delphi XE Handbook, and the source code is also available from the book Subversion repository at code.marcocantu.com.

Delphi XE Components for Azure

Delphi XE includes some components for Azure, but also and moreover a number of lower level classes, which is where the real power is. These classes provide ready-to-use methods for performing the various operations and deal with the complicated security requirements of the Azure platform.
The core non-visual component is the one used to configure the connection to the service, called TAzureConnectionString. Although this component has a few quirks in its user interface (it messes up the Object Inspector in the IDE1) it is a handy starting point in which to enter the user name and password of your account.
Other visual components offer direct navigation and management of the respective data structures:
·         TAzureTableManagement is for NoSQL tables2
·         TAzureBlobManagement is for binary data (that is, files, images, documents...)
·         TAzureQueueManagement is for managing queuing services
These management components are very nice to learn about these services and examine their status, but aren't generally a good end user tool, safe for the real power users. In practical terms, you'll generally use the low-level classes behind these components rather than the visual “management” TreeView controls. The three core classes, defined in the DSAzure unit, are:
·         The TAzureTableService class
·         The TAzureBlobService class
·         The TAzureQueueService class

Building an introductory Demo, Azure101

In the Azure101 project I introduce both the visual components and the low level classes, focusing on the Table and Blob services. The programs loads the account settings from an INI file.
There are two Azure visual controls hooked to this configuration component, as you can see in Listing 1:
 
object AzureTableManagement1: TAzureTableManagement
 ConnectionInfo = AzureConnectionString1
 Active = False
end
 
object AzureBlobManagement1: TAzureBlobManagement
 ConnectionInfo = AzureConnectionString1
 Active = False
end
Listing 1: The simple properties of the Azure Management components of the demo
 
As with a database component, you won't generally activate them at design time. Given they might take some time for the setup, you won't probably even turn them on in the OnCreate event handler. In this demo I do it when a button is pressed, turning the Active property of both management components to True.
At this point you can use the two controls for browsing the current Tables and Blob containers of your account. You can double click on each of them to open a dialog box with more details. In Figure 1 you can see the information displayed by the TreeViews.
 
Figure 1: The Azure101 demo lets you browse your Azure Tables and Blobs
 
As you can see in the figure, the information about tables is limited, and you'll have to double click on a table to open a dialog and see more. For the Blobs, instead, you can drill into the list of elements in each container and also see the list of the properties, the meta data, and the access control list right into the main TreeView. For the Blobs you can also use this view to load new files and set their permissions.
The button on the side will copy to the clipboard the main URL for your azure data, which in my case is:
http://marcocantu.blob.core.windows.net
You can combine this URL, the container name, and the object name to figure out its public URL, like (if you look again at the image above):
http://marcocantu.blob.core.windows.net/mydata/euro.jpg
This can be properly seen in a browser only because I set its Content-Type property to a format browsers should recognize (see the details in the image below). Using a URL like the one above anyone can see the image I uploaded with a Delphi program. In Figure 2 you can see the set of properties for the object:
 
Figure 2: The properties of an Azure Blob object in the AzureBlobManagement component
 
In terms of the tables, you need to open the details of one to see its structure, as displayed in Figure 3. Differently from what you might expect, the table data is not displayed in a grid. In fact, this is not a relational database table, but rather a NoSQL table structure, in which each row can have different fields or, to be more precise, named attributes.
 
Figure 3: The detailed view of an Azure Table in a dialog displayed by the AzureTableManagement component
 
Now what is more interesting, of course, is to interact with this table in your code. For example, you can add a new row to a table using the InsertEntity method of the TAzureTableService class. The row information is passed in JSON format, so you can use Delphi's TJSONObject class (from the DBXJSON unit) to define the proper data structure. This must have the RowKey and PartitionKey attributes, defined in Delphi with two constants called XML_ROWKEY and XML_PARTITION.
 
The other element is that some rows can have extra fields, which doesn't mean that other rows have those fields empty, but that they won't have them. In fact there is no schema or definition of a table: the table structure is defined by the contents of the records you add to it. In listing 2 there is the code used to add a new row to the table.
 
procedure TAzure101Form.btnAddRowClick(Sender: TObject);
var
 TableService: TAzureTableService;
 RowObj: TJSONObject;
begin
 TableService := TAzureTableService.Create(
    AzureConnectionString1);
 try
    // prepare the “row” data
    RowObj := TJSONObject.Create;
    RowObj.AddPair(XML_ROWKEY, TJSONString.Create(
      'ID' + IntToStr (Random (100000))));
    RowObj.AddPair(XML_PARTITION, TJSONString.Create(
      TimeToStr (now)));
    RowObj.AddPair('name', TJSONString.Create('marco'));
    RowObj.AddPair('site', TJSONString.Create(
      'www.marcocantu.com'));
    if CheckBox1.Checked then
      RowObj.AddPair('twitter', TJSONString.Create('marcocantu'));
 
    // now add the row to the table
    Log (TableService.InsertEntity('marco1', RowObj));
 finally
    TableService.Free;
 end;
end;
Listing 2: The code used to add new rows to an Azure Table
 
The other operation done by the program is to query the table for the list of records. This is accomplished by calling the QueryEntities method of a TableService object. The result of this call, however, uses an XML format (basically an ATOM format) that the program logs without parsing it.
 
The interesting element of this approach is that once we have created this data structure (or table), it can be used by anyone with the proper permissions and an Internet connection. There is a cost associated, of course, but quite minimal compared to alternative hosting solutions.
The Azure Publish and Consume Demo
The Azure101 demo can get you started using Delphi's Azure support, but it certainly falls short in terms of demonstrating its usefulness. The “management” views are quite nice, but that's hardly the interface you'll want to present to an average end user. That's why I decided to add a second demo, made of two programs:
·         The first is a publishing application that pulls data from a database and makes it available in an online Azure Table, while the content of graphical fields is published in an Azure Blob.
·         The second is a browsing application that pulls the data from the Azure cloud storage and shows it to the end user. Notice that this way the end user doesn't require a connection to the database and can benefit from the high bandwidth offered by Azure.
Publishing the Data to Azure
To stick to a classic Delphi theme, the database in question is the Biolife table from the FishFact database. In this case I'm using the ClientDataSet version for simplicity.
Let's look at the CloudPublish application first. The main form of this program has two fields referring to the services we are going to use.
These are initialized in the OnCreate event handler after loading the account settings from the INI file, like in the previous example. Before publishing the data, you have to create the corresponding containers.
To publish the data of a single record, we have to create a JSON data structure, include a row key (in this case the record id) and a partition key (in this case a time stamp), and also post a stream with the image using as name the same record id, as you can see in Listing 4 (which incldues also the method used transform the original bitmap image in a JPEG, using a temporary memory stream).
 
procedure TFormCloudPublish.btnPostOneClick(Sender: TObject);
var
 strTable: string;
 RowObj: TJSONObject;
 content: TBytes;
begin
 RowObj := TJSONObject.Create;
 RowObj.AddPair (XML_ROWKEY,
    TJSONString.Create (cdsBioSpeciesNo.AsString));
 RowObj.AddPair (XML_PARTITION,
    TJSONString.Create (TimeToStr (now)));
 
 RowObj.AddPair('category',
    TJSONString.Create(cdsBioCategory.AsString));
 RowObj.AddPair('commonname',
    TJSONString.Create(cdsBioCommon_Name.AsString));
 RowObj.AddPair('speciesname',
    TJSONString.Create(cdsBioSpeciesName.AsString));
 RowObj.AddPair('length',
    TJSONString.Create(cdsBioLengthcm.AsString));
 RowObj.AddPair('notes',
    TJSONString.Create(cdsBioNotes.AsString));
 
 TableService.InsertEntity(tablename, RowObj);
 BlobService.PutBlockBlob(tablename,
    cdsBioSpeciesNo.AsString + '.jpg',
    JpegContentOf,
    '', nil, 'image/jpeg');
end;
 
function TFormCloudPublish.JpegContentOf: TBytes;
var
 jpgImg: TJPEGImage;
 aStream: TMemoryStream;
begin
 Image1.Picture.Assign(cdsBioGraphic);
 
 jpgImg := TJPEGImage.Create;
 aStream := TMemoryStream.Create;
 try
    jpgImg.Assign(Image1.Picture.Graphic);
    jpgImg.SaveToStream (aStream);
    aStream.Position := 0;
    SetLength(Result, aStream.Size);
    aStream.ReadBuffer(Result[0], aStream.Size);
 finally
    jpgImg.Free;
    aStream.Free;
 end;
end;
Listing 3: The code used to publish the database data to Azure
 
Another button let's you scan the database table and post each record. I haven't told much about the user interface of this program because it is very bare bones, as you can see in Figure 4.
 
Figure 4: The simple user interface of the CloudPublish example
 
Now let's look at the browsing application, called CloudBrowse. When this form is created, it does an initialization similar to the other Azure programs. For this demo, I'm using a single account, but you can set up Azure to have a separate user with read-only rights.
The first specific operation of the browsing demo is a call to QueryEntities to get a list of the records as XML. This return string is processed using an XML-mapper generated interface.
However, to parse the actual content of each node we cannot use the interface, as the presence of a nested XML name space causes the interface to fail whilst reading the information (as requests return empty strings in case of a name space mismatch).
The code that parses the XML will add to a list the row and partition keys, which are used later to retrieve the details as a user double clicks on a list item, with the code in Listing 4, and display the information like shown in Figure 5.
 
procedure TFormCloudBrowse.ListBox1DblClick(Sender: TObject);
var
 strRowKey: string;
 strXml: string;
 memStream: TMemoryStream;
 strPartKey: string;
begin
 strRowKey := ListBox1.Items.Names [
    ListBox1.ItemIndex];
 strPartKey := ListBox1.Items.ValueFromIndex [
    ListBox1.ItemIndex];
 strXml := TableService.QueryEntities(tablename,
    TIDUri.ParamsEncode(strPartKey), strRowKey);
 memo1.Lines.Text := strXml;
 
 memStream := TMemoryStream.Create;
 try
    BlobService.GetBlob(tablename,
      strRowKey + '.jpg', memStream);
      memStream.Position := 0;
    Image1.Picture.Graphic.LoadFromStream(memStream);
 finally
    MemStream.Free;
 end;
end;
Listing 4: The code used to retrieve the data and image of a specific record
 
Figure 5: The user interface of the CloudBrowse example, showing both record data and images
 
Again, notice that this browsing application doesn't require a connection to the original database or to a custom web server, but relies only on the tables and images provided by Azure cloud storage, which has almost infinite bandwidth.
Conclusion
It is hard to tell how much cloud computing will be a temporary fad and how much of our computing power will reside in web farms owned by a few companies in a few years time, but what is relevant to notice here is that Delphi can be used in this scenario and that there is no “single preferred vendor” (like in other architectures) but rather the ability to use services of multiple companies.
The net result is that in many cases you can extend your native Windows application with a new reach to the Internet, without having to rewrite them as Web applications.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar