he Web Storage System (WSS) that was introduced in MicrosoftÂ® Exchange 2000 is a Web-accessible database that can store any type of data: e-mail, contacts, appointments, threaded discussions, multimedia files, and so on without the strict structural requirements of typical relational database systems. The WSS can store any piece of data right out of the box. When your application requires a loosely coupled and loosely structured database for storage, the WSS is the way to go.
Exchange 2000 is currently the only product shipping with the WSS, but there is another product in development that also plans to incorporate it. The Microsoft knowledge management product called SharePoint Portal Server (formerly codenamed "Tahoe") will be built on top of the WSS.
After I describe WSS and its schema for properties and classes, I'll show you how to access data items and their properties programmatically. I'll explain how to view data and properties, add custom data to them, create your own content classes and schema, use your custom content classes to store data, and create your own forms to view your customized classes.
To understand the WSS and its loose structure you have to change your thinking about Web development. Traditionally, client/server and Web application development entailed a database back end with a user interface. The typical Web app used the user interface to retrieve data and display it. This method of development is interface-centric.
Conversely, the WSS stores its data as individual data items. When those items are requested, the WSS renders HTML to view them. Instead of being interface-centric, the WSS is data-centric. You simply request the data item you want to view, and the WSS takes care of rendering it for you.
The WSS is based on Internet standards for almost all of its services and storage. HTTP, SMTP, XML, MIME, and Web Document Authoring and Versioning (WebDAV) are just some of the technologies the system uses for transmitting and storing data. At the heart of the WSS is the data store and the forms rendering engine. The data store looks much like a file system folder list. Figure 1 shows the data store for the sample application I'll discuss later. At the top is the root folder and underneath are data items and subfolders that nest into a tree-like hierarchy.
Figure 1 Data Hierarchy
The forms rendering engine handles how the data items are rendered for the browser. For a data item to be rendered, the rendering engine detects the browser capabilities. Next, it determines which form to render the item with, and delivers the data item rendered to the browser using the appropriate encoding (XML, XSL, HTML 4.0, HTML 3.2, and so on).
Accessing the Data
Because the WSS is data-centric, everything within the data store is URL-accessible. Each mailbox, folder, e-mail message, and calendar appointment is accessible from your browser's address bar. For example, this URL opens my personal outbox and renders its contents.
The following URL opens an appointment from my calendar and renders the details in the browser.
Notice the .EML extension in the URL. The WSS stores all data items with an .EML extension (unless you specify another extension).
In previous versions of Exchange, there were only two ways to access data in the information store: the Messaging API (MAPI) and a COM interface called Collaborative Data Objects (CDO). With the WSS, Microsoft has added a myriad of additional tools that can access it, while still being backward-compatible with MAPI and earlier versions of CDO. You can now access all items in the data store through an Exchange OLE DB provider. This means that you can take your knowledge of ADO and put it to work right away.
The data store comes with an installable file system driver (IFS), which means you can access it through a drive mapping. After you install Exchange 2000, you will notice that you have an M: drive on your server (assuming M: isn't already used, in which case Exchange will choose the next letter available). This is the default drive mapping for accessing data in the WSS through the Exchange IFS.
The WSS is accessible through XML and WebDAV. This interface works over HTTP and allows you not only to retrieve data from the data store, but to create, modify, update, and delete data from the data store all over HTTP. This interface is particularly useful if your application has to work across firewalls. Simply open port 80 to your WSS server and you have all the access to your data that you need.
Web Storage System Schema
WSS is a loosely structured database, but no database would work without some type of structure. Enter the WSS schema. Much like the schema that defines Active Directoryâ„¢, the WSS defines a global set of content class types and properties that can be assigned to data items. The content class helps define the type of data that is stored within the data item and thus what properties will be assigned to the data item. The content class of an item also helps define which form will be used to render the data item for the Web.
As with Active Directory, you can extend the default schema with your own content classes and properties. The default schema that comes with the WSS is quite extensive, and in most cases will contain all the data properties your application requires. If you do need to create schema extensions, you can inherit existing content classes and their properties, so you don't have to reinvent the wheel. This technique also allows large development teams to reuse custom schema extensions for company-specific data needs and prevents the duplication of work in different areas.
Figure 2 The Web Storage System Explorer
Let's look at some examples. When I view the messages in my mailbox, I look at the schema content class for each e-mail and see that they are of content class urn:content-classes:message. You can see this for yourself by downloading the WSS SDK and using the WSS Explorer (see Figure 2). This tool allows you to browse any folder in the WSS that you have rights to view and shows you the content classes and properties of any data item.
Figure 3 Viewing Appointments
When you look at my calendar, you will see that each appointment is of content class urn:content-classes:appointment (see Figure 3). You may wonder why a lengthy name is necessary to simply define a message or an appointment. The section before the property name is called the namespace. The namespace allows unique identifiers for different properties that share similar names or attributes. Namespaces make it possible to have uniquely defined content classes and properties across many systems with many data items. That way, a message defined on one system will work if it is moved to another system that follows the same standards. When defining your own content classes and namespaces, you should make it a habit to follow these standards. I will talk more about this when I discuss extending the WSS schema.
Properties and Class Inheritance
As you can see in Figure 4, the WSS allows you to define a set of properties for each data item. These properties are associated with a data item when the content class of the item is defined. For instance, a message data item will have different properties than an appointment data item. You will also notice that not every property is set because not every data item of a content class will use every property.
Figure 4 Set Properties
You will also notice that properties are named using the same namespace rules that govern content classes. For instance, almost all properties for an e-mail message (content class urn:contentclasses:message) are stored using the urn:schemas:httpmail namespace. This allows data to be standardized so it will function across multiple systems.
As I mentioned earlier, you can inherit your custom content class from an existing content class. To understand this, compare an appointment item to a message item. An appointment item has many of the same properties that a message has (subject, text description, date received), but it has some extra ones for appointment start time, end time, location, and so on. The appointment item inherits from the message class to reuse its properties in the appointment object.
All of these content classes and properties are defined for each data store in the global schema folder. This folder is located in the /non_ipm_subtree/schema/ subfolder located at the top of every data store. Exchange 2000 automatically creates this folder when a data store is created. When browsing the schema folder, you will see that the definitions for these content classes and properties are themselves data items. The data items of content class definitions and property definitions have the content class urn:content-classes:contentclassdef and urn:content-classes:propertydef, respectively. The data store looks in the schema folder for items of this type to define what content classes and properties exist in this data store. This folder will come into play later when I discuss extending the schema for your own data needs.
Accessing WSS Data Programmatically
Let's look at how to access data items and their properties programmatically. The Visual BasicÂ®-based code in Figure 5 opens up a folder (my inbox) using ADO, then traverses its contents using the ADODB.Connection, ADODB.Recordset, and ADODB.Record objects. For more information on the syntax to query the Exchange OLE DB provider, see the WSS SDK.
Once you know how to view items and their properties, you will want to assign custom data to them. It's not always appropriate to extend the global schema for a data store to meet the needs of a single data item. For instance, if you have e-mail that you want to tag with a custom property, you don't have to go through the hassle of extending the schema. Instead, you can simply create a one-off custom property for the data item. Once added, this property will follow the data item wherever it goes, but it does not affect the definition of other items of the same content class. The following lines add the calendar property of "start time" to a data item to show how to add a one-off custom property using ADO.
recADO.Open sItemURL, connADO
recADO.Fields.Append "urn:schemas:calendar:dtstart", adDate, , , Now()
When deciding whether to extend the schema or simply use one-off custom properties, there are many things to take into consideration. One-off fields are quick and easy to implement, but having multiple pages with one-off field definitions can result in inconsistencies and mistakes. In contrast, a custom schema is created once and stored in a central location. If you are working with many custom properties, then creating a customized schema may save you time over constantly adding custom fields. Custom content classes also make it easy to filter and search for items of your customized class type, allowing you to share folders for data items.
Creating a Custom Schema
Now let's look at how to extend the WSS schema to create a custom schema for specific data needs. For this example, I created my own content class and properties to store articles within the data store. The custom content class I created is called articleitem. The articleitem custom class inherits properties from the message content class. When extending the WSS schema, avoid putting your extensions into the global schema folder directly; create your own schema folder instead. By doing so, you can inherit the schema definitions from the global folder thus avoiding the problems associated with adding and deleting items in an important system folder. When you create the folder, set the properties to tell it where to inherit its schema (for instance, the global schema folder).
After creating the schema folder and setting up its inheritance with the global schema folder, you can start creating your custom schema extensions. You do this by creating data items to represent the custom content classes and properties in the custom schema folder. These new data items will be of content class urn:content-classes:contentclassdef or urn:content-classes:propertydef, depending on what type of extension you are creating.
Once you have successfully created the schema folder and the schema items, you can start using your schema extensions. By default, when a new folder object is created, that property is set to the default schema folder, which is the global schema folder. When new data folders are created in the data store, you need to set their schema definition location to the custom schema folder. By defining and customizing this property and telling the data folder to get its schema definition from the custom schema folder, the data folder gets the custom definitions and inherits the global definitions. By creating the custom folder separately, you can keep your definitions separate, but get the benefits of inheriting from the global folder.
In Figure 6 I created a custom schema folder that inherits the global schema definitions, a custom content class called articleitem, custom property definitions, and a new data folder whose data definition is set to the newly created custom schema folder.
As you will see, in the custom content schema code I used a custom namespace. For the purpose of this article, I used the namespace prefix of http://ecollege.com/. When sharing data across systems, it's best to use the standardized namespaces so information is uniform and standardized. However, when creating custom content classes and property definitions, you should use your Internet domain name as your namespace prefix. If two systems should happen to use the same namespace, someone's data would get stepped all over. Since you control your Internet domain name, you can be sure it's unique.
When using the WSS Explorer, you will see certain properties that are specific to Exchange 2000, not to generic mail systems. These properties usually have the namespace prefix http://schemas.microsoft.com/ to keep them unique across systems. Microsoft followed this principle, its domain name is its namespace prefix. Remember, namespaces are always case-sensitive.
Storing Data in Custom Classes
Once you have custom content classes defined with their custom properties, you can start to use them to store data. Figure 7 shows how to create a custom data item called articleitem and populate its inherited properties and its custom properties.
I used a CDO object message to create the custom item. While I was not really creating a message object, it is still good to use the CDO objects whenever possible. They offer quick access to properties and methods that make life easier than accessing all properties through ADO and the long namespace. For instance, you can use the CDO message object "from" property to set the "urn:schemas:httpmail:from" property in ADO. Using CDO objects allows you to use all of the built-in capabilities of the CDO objects (like the datasource property for saving and opening an object) yet allows you to access all of your custom properties and non-built-in properties through the fields collection.
When using a CDO object to create and modify your custom data item, it is best to use an object that your custom data item inherits from. In this instance, I used the CDO message object to create the articleitem since the articleitem inherits from the message object. If you create a custom data item such as vacationitem, which stores data about when your users will take vacation, then your custom data item will likely inherit from the appointment object. In this case, you would use the CDO.Appointment object to create your vacationitem custom data items. Now that I've explained custom content classes and properties and talked about creating data items that utilize schema extensions, let's look at how to view the new data.
Viewing Data with Web Storage Forms
The WSS uses the concept of a form to view data. A form is registered to a content class type. When a data item of that content class type is requested, the WSS uses that form to render the data item into HTML. Exchange 2000 comes with a set of default forms that are used to render the default content classes defined for each data store. This default set of forms is commonly known as OutlookÂ® Web Access (OWA). If you have used Exchange 5.5 from the Web, you know that Outlook Web Access was used for accessing your mailbox and viewing its contents over HTTP. This older version was built using the typical interface-centric format.
The upgraded version of OWA in Exchange 2000 is data-centric. It enables Exchange 2000 to render any data item within the WSS into an HTML or XML/XSL format, depending on the browser. For instance, when I run the code to create a custom articleitem in public folders, this item is viewable right away through OWA. Simply type the URL of the item directly into your browser or browse to it using the OWA interface to public folders. You will find that custom data items are rendered as e-mail with none of the custom properties viewable. Since the forms registry does not have a registration for displaying the custom content class, it simply displays it using the default form for a message object.
OWA is more than simply the default interface for data stored within the data store. The Outlook Web Access interface can be reused outside of the traditional OWA shell. You can reuse OWA to put views of WSS data into any Web page. You do this using the <IFRAME> tag. For instance, you can create a welcome page on your intranet that also contains each user's inbox. You can reuse discussion threads that are created in public folders and use OWA to display them on the Internet.
Since everything in the data store has a URL, you simply point the IFRAME source to the URL of the data item or folder that you want to render. And because this is based on the WSS forms rendering engine, the rendering of the data is automatically scaled up or down to the browser's capabilities. Put the following code into an HTML page and access it from different browsers to see the power of the WSS rendering.
<IFRAME name='MAIN', width = 90%, height = 70%,
You can customize the way that OWA renders folder data (like discussion threads or press releases) when reusing OWA in your Web application. For instance, you can change the view from the default folder listing to a threaded discussion that has messages grouped by their thread topic. You do this by sending special parameters to OWA through the query string. By specifying a CMD parameter, you can control the way that folder data is rendered, as you can see in this example:
<!-- Create View of Threaded discussions grouped by thread topic -->
<IFRAME name='MAIN', width = 90%, height = 70%, SRC=
Creating Customized Forms
Now let's look at how to create customized forms that can be used to render custom content classes. Customized forms allow you to render customized properties instead of seeing the default OWA message form for custom data. The first step in this process is to register the form within the forms registry. You do this in the same way you define custom content classes and custom propertiesâ€"by creating a data item within the custom schema folder. This data item will have the content class of urn:schemas-microsoft-com:office:forms#registration which tells the WSS that this is a form registration. Next, you need to define which content class this form registration is for. In my example, it is the articleitem custom content class.
At this point, you need to define when to use this form. Since the WSS can sniff out the client browser, you can create form registrations for different client needs. For instance, Microsoft Internet Explorer 5.5 users have access to features that those using Internet Explorer for the Handheld PC do not. The ability to register certain forms for certain browsers has tremendous advantages when you are developing your application for the Internet because you can create customized interfaces for the devices accessing your application. By using these registration properties, you can specify a form based on the operating system, browser version, the state your data is in (useful for workflow applications), the language, and many other variables (see Figure 8).
As you can see, this code creates a form registry entry for /Articles/ArticleItem.asp to be used when any client, from any OS, using any language accesses the articleitem data item. Form registrations work like custom content classes. When you create a new folder, you specify where that folder can find its default schema. When you point that folder's schema definition into your custom schema folder, it automatically inherits the global schema folder and its default form registrations (OWA).
If you don't specify where to get the schema definition when a folder is created or if you point it to the global schema folder, the data items in this folder will use the default form registrations of OWA not your customized form registrations.
To register the /ArticleApplication/ArticleItemJP.asp to be used when an articleitem data item is requested from a Japanese browser running WindowsÂ® CE 3.0, change the registry entry like so:
Next, you simply need to create an /ArticleApplication/ArticleItem.asp file and put in your customized code to render articleitem.
Let's look at what happens when a custom data item is requested so you can see how a form can process any data item of a particular content class. First, the browser requests a data item. Second, the WSS reads the data item and determines what form will render it based on the content class and the requesting client browser capabilities. Then the form is instantiated and the URL of the data item requested along with the form URL are sent as parameters in the query string. With the URL of the data item, the form can now access the requested data item via ADO and render the custom data and properties stored with it. Your ADO or CDO code simply retrieves the data item requested through ADO by the URL sent in the query string. Figure 9 shows how to display the custom articleitem properties.
Now that you have a custom content class and a data item in the data store of this content class, you can display it using the custom form registration by typing the item directly into your browser or by browsing to it using the OWA interface to public folders (see Figure 10).
Figure 10 Rendering a Data Item
You don't always have to use ASP and ADO/CDO for your custom forms. The Web Storage System SDK has a special ISAPI filter that allows you to create your custom forms in HTML. The HTML will contain customized tags that will be processed by the ISAPI filter and replaced with the data stored within your data item. There is also a set of Microsoft FrontPageÂ® design-time controls that help you create these custom HTML tags in a nice visual environment so you don't have to get your hands dirty. Finally, there is a tool for you to register your forms, so you don't have to do that programmatically using ADO. Look in the Web Storage System SDK for further information on the FrontPage tools for the WSS and for the Web Storage forms renderer.
A Sample Application
I created a Web application that stores all of its data in the data store. This application stores articles and allows users to rate the articles on a scale of 1 to 5 and discuss the articles through threaded discussions, similar to the functionality on MSDN Online. This application was built in the default public folders data store for demonstration purposes only, since putting applications in the public folder isn't recommended. For a live application, you should definitely create a new data store and install it outside of the public folder tree, although keep in mind there is a limit of five per server. Placing this demo application in the public folder tree allows you to see from Outlook how the custom schema folder is createdâ€"again, for demonstration purposes only.
To create the application, I created three custom content classes: articleitem, threaditem, and ratingitem. Articleitems contains the data for each article. Threaditems stores the data for the discussion threads created for the articleitems. Ratingitems stores the specific ratings that users give to each of the articles. You may notice that I did not reuse the OWA-threaded discussion features in this application. I wanted to create an individual discussion forum for each article. To accomplish this by reusing OWA discussion threads, I would need a separate discussion folder for each article. Having a large number of articles would result in too many folders. By creating a custom content class that stores the discussion thread an article is associated with, I can store all thread items in a single folder.
In the download for this article, you will find a setup program and some ASP pages (see the link at the top of this article). The setup program creates the folders in the public folder tree, the content class definitions, the form registrations, and so on. The ASP pages render the article data, thread data, and ratings data.
Accessing WSS Data with XML and WebDAV
I mentioned earlier that CDO and ADO were not the only technologies you could use to access the WSS. Microsoft Internet Information Services (IIS) 5.0 and the WSS now support WebDAV. This technology defines methods for accessing data on a Web server and securely requesting and modifying its contents over HTTP. WebDAV transforms HTTP from a read-only client/server protocol to a robust read/write vehicle, all through port 80.
Because IIS and the WSS now support the WebDAV technology, you can construct a request from any HTTP client to create and query for resources on the server. The requests for WebDAV are generated in XML using the new command extensions to HTTP. Figure 11 shows how to use the XMLHTTP COM component to transmit a request and retrieve the contents of a mailbox using the WebDAV syntax.
The code in Figure 12 creates an articleitem for the application directly from the client using WebDAV and the XMLHTTP COM object. This code uses the XMLHTTPRequest interface of the MSXML DOM and sends an expanded HTML command (CREATE) to build an item in the data store. You can see that I send the properties of the data item as XML to the CREATE command so that it not only builds the data item, but populates properties for the data item as well.
Also included with the download for this article is an extensive sample Visual Basic-based form application that uses the XMLHTTP COM object for creating article items, looking up existing data items, and displaying their properties, all over HTTP and WebDAV. This form client can be used to access data on the server from any computer that has the XMLHTTP COM object installed on it.
Content Indexing and Searching
The WSS also has the ability to index all of its data much like IIS can index your Web site. Out of the box, you get the benefits of quicker searches and queries from Windows 2000 directory searches of the M: drive by content indexing your data store. With the advent of Outlook 2002, user queries from the e-mail client will be much quicker as well. You can also access this index from ADO, so you can process programmatic searches and filters on your data store.
By default, the WSS will index the common fields that exist in the default content classes. Subject, From, To, and TextBody are some of the fields that are automatically indexed for message objects. By reusing these fields on custom content classes that inherit from these objects (as I did with the articleitem content class), you get out-of-the-box functionality for content indexing. You can set up the content index engine to index your custom fields as well.
Let's look at an example of how to use ADO to utilize a free text query of a data store public folder.
rsADO.Open "SELECT* FROM scope('shallow traversal of """ _
& "file://./backofficestorage/MyDomain.Com" _
& "/Public Folders/Articles/" _
& """') WHERE (" & chr(34) & "DAV:contentclass" _
& chr(34) & " = '" & sCustomSchema _
& "content-classes:articleitem'" & " AND FREETEXT _
(*, ' & chr(34) & "Sean McCormick" & _
Chr(34) & "') " & ") ORDER BY " & _
chr(34) & "urn:schemas:httpmail:fromname" & chr(34) ,connADO
As you can see, the ADO code uses the FREETEXT keyword to specify that the index created by the content indexing engine is the index to use. This query will search out all of the articlesitems written by Sean McCormick. For my sample application, I utilized the content indexing features to filter the list of available articles. My articleitem content class reuses the message fields of from, subject, and textbody from the message object for author, title, and article text, respectively. All of these fields are automatically indexed when you turn on content indexing for the data store to allow the easy addition of a programmatic filter to the article list.
The WSS is a new technology based on Internet standards that allows you to loosely store any type of data you need for your application. By using customized content classes and properties, you can extend the data store to meet your custom data needs as well. Content indexing and WebDAV support also extend the capabilities of your data store application with little work. With the promise of any data, anywhere on any device, it's easy to see that the WSS will play an important role in the Microsoft .NET Framework. With other products that plan to use and extend the WSS such as Digital Dashboard 2.1, SharePoint Portal Server, and Office XP in development or shipping, expect to hear more about the Web Storage System.