Showing posts with label SNIA. Show all posts
Showing posts with label SNIA. Show all posts

RSS Ingester For XAM Reference VIM

Recently the SNIA XAM SDK TWG (Software Development Kit Technical Working Group) released a new version (version 0.7) of the XAM SDK with lots of new features. Included in this SDK was the 5th code drop for the Reference VIM. Here is a summary of the major new functionality included in this code drop.

  • Support for XSet hold and release
  • Support for XSet autodelete and shred policies
  • Support for simple where clauses in level 1 queries
  • Support for retention policies including XSet import and export
  • Support for asynchronous methods
  • Support for level 2 queries

Also recently released were version 1.01 of the XAM technical positions (AKA specifications). If you are familiar with version 1.0 of these three documents you may just want to read the errata for each document which is also available on the website.

I decided to test the Reference VIM by writing a small Java application to ingest the RSS feed from my blog, display some information about each post and store this information in the Reference VIM database. This application uses an XML configuration file to store a list of the RSS feeds to be ingested and the popular ROME Java library to ingest and parse the individual RSS feeds. Rather then re-invent the world, it also uses a modified version of the Java source file ExampleBase.java which is supplied as part of the latest XAM SDK to create and authenticate the connection to the XAM library and Reference VIM.

Here is RSS2XAM..java which is meat of the application. As you can see, RSS2XAM extends ExampleBase. This is a useful paradigm for writing applications which use the Reference VIM.


//
// RSS2XAM - Ingest RSS feed and add to XAM Storage System
// using SNIA XAM SDK V0.7 reference VIM
//

import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Calendar;
import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;

import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndImageImpl;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;
import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndCategory;

import org.snia.xam.XSet;
import org.snia.xam.XSystem;
import org.snia.xam.XUID;

public class RSS2XAM
extends ExampleBase
{

private static void iterateRSSFeed(String rssFeedUrl, XSystem xsystem)
throws Exception {

URLConnection feedUrl = new URL(rssFeedUrl).openConnection();
SyndFeedInput input = new SyndFeedInput();
SyndFeed feed = input.build(new XmlReader(feedUrl));

System.out.println("RSS feed: " + rssFeedUrl + " (" + feed.getFeedType() + ")");

// ingest the feed, display info and create the XSets
List list = feed.getEntries();
for (int i = 0 ; i < list.size(); i++)
{
SyndEntry entry = (SyndEntry)list.get(i);

String display = "Entry: " + i;
display += "\n Title: " + entry.getTitle();
display += "\n Link: " + entry.getLink();
display += "\n Author: " + entry.getAuthor();
display += "\n Date Published: " + entry.getPublishedDate();

// get the list of categories
List catList = entry.getCategories();
String categories = "";
for (int j = 0 ; j < catList.size(); j++)
{
if (j > 0 ) categories += ", ";
SyndCategory cat = (SyndCategory)catList.get(j);
categories += cat.getName();
}

display += "\n Categories: " + categories;
System.out.println(display);

XSet xset = xsystem.createXSet(XSet.MODE_UNRESTRICTED);
xset.createProperty("com.fpmurphy.rss.title", false, entry.getTitle());
xset.createProperty("com.fpmurphy.rss.link", false, entry.getLink());
xset.createProperty("com.fpmurphy.rss.author", false, entry.getAuthor());
xset.createProperty("com.fpmurphy.rss.categories", false, categories);
Calendar cal=Calendar.getInstance();
cal.setTime(entry.getPublishedDate());
xset.createProperty("com.fpmurphy.rss.published_date", false, cal);

XUID xuid = xset.commit();
System.out.println(" XSet XUID ==> " + xuid.toString() + "\n");
xset.close();
}
}

public static void main(String argv[]) {

try {
File file = new File("rss.xml");

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(file);
document.getDocumentElement().normalize();

// init, load Reference VIM, and authenticate
System.out.println("Connecting to Reference VIM");
initLibrary();
XSystem xsystem = connectToVIM(s_xri);
authenticate(xsystem);

// parse the rss.xml
NodeList nodeList = document.getElementsByTagName("feed");
int totalFeed = nodeList.getLength();
System.out.println("Number of Feeds: " + totalFeed);

for (int i = 0; i < nodeList.getLength(); i++) {
Node feedNode = nodeList.item(i);
if (feedNode instanceof Element) {
Element child = (Element) feedNode;
String check = child.getAttribute("check");
String rssFeedUrl = child.getTextContent();

// hand off to do the work
iterateRSSFeed(rssFeedUrl, xsystem);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}


Here is a copy of the modified exampleBase.java. If you compare it to the original source code in the XAM SDK (...//Java_Reference_VIM/examples/ExampleBase.java) you will see that most of the changes are minor simplifications.


/*
* Copyright (c) 2009, Sun Microsystems, Inc.
* Copyright (c) 2009, The Storage Networking Industry Association.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of The Storage Networking Industry Association (SNIA) nor
* the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Properties;
import org.snia.xam.XAMException;
import org.snia.xam.XAMLibrary;
import org.snia.xam.XSystem;
import org.snia.xam.base.XAMImplementation;
import org.snia.xam.util.SASLUtils;
import org.snia.xam.vim.reference.ReferenceAuthenticationStatus;
import org.snia.xam.vim.reference.utils.ReferenceSaslUtils;

public class ExampleBase {

public static final String PROP_FILE = "rss2xam.props";
public static final String XRI_PROP = "xam.xri";
public static final String CONFIG_PROP = "xam.vims";
public static final String USER_PROP = "xam.username";
public static final String PASS_PROP = "xam.password";

protected static final String DEFAULT_USER = "test";
protected static final String DEFAULT_PASS = "test";
protected static String s_pass;
protected static String s_user;
protected static XAMLibrary s_xam;
protected static String s_xri;

public static void initLibrary() throws Exception {
System.out.println("\nInitializing VIM");

Properties props = new Properties();
String testPropFile = System.getProperty(PROP_FILE);
if (testPropFile == null)
testPropFile = PROP_FILE;

System.out.println("Loading properties from file: " + testPropFile);
props.load(new FileInputStream(testPropFile));

s_xri = props.getProperty(XRI_PROP);

s_user = props.getProperty(USER_PROP, DEFAULT_USER);
s_pass = props.getProperty(PASS_PROP, DEFAULT_PASS);

System.out.println("VIM Configuration contained in file: " + props.getProperty(CONFIG_PROP));
s_xam = new XAMImplementation(props.getProperty(CONFIG_PROP));
}

public static XSystem connectToVIM(String xri) throws Exception {
XSystem xsystem = null;
System.out.println("Connection arguments: " + xri);
xsystem = s_xam.connect(xri);
return xsystem;
}

public static void authenticate(XSystem system) throws XAMException {
String defMech = system.getString(XSystem.XAM_XSYSTEM_AUTH_SASL_DEFAULT);
ByteArrayOutputStream response = new ByteArrayOutputStream(200);
byte[] inputData = null;
int retValue = 0;
if (defMech.equals(ReferenceSaslUtils.SASL_MECHANISM_ANONYMOUS)) {
retValue = system.authenticate(inputData, response);
} else if (defMech.equals(SASLUtils.SASL_PLAIN)) {
byte[] creds = ReferenceSaslUtils.encodeSASLPlain(null,
ReferenceAuthenticationStatus.TEST_USERNAME,
ReferenceAuthenticationStatus.TEST_PASSWORD);
retValue = system.authenticate(creds, response);
} else {
throw new XAMException("Unknown SASL mechanism " + defMech);
}

if (retValue != XSystem.XAM_SASL_COMPLETE) {
throw new XAMException("Failed to authenticate.");
}
}
}

Here is a copy of my rss2xam.props. I am storing the generated XSets at /home/fpm/rss/store but you can specify any directory you like by modifying the dir= directive on the second line of this file. Make sure that this directory exists and is writeable or you will get an exception when you invoke the application. I will leave it to you to modify the source to test that the specified directory exists and is writeable. As you can see, this file also specifies that the Reference VIM configuration file (vim.config) exists in the current directory. Again you are free to change the location to wherever you like.

xam.vims=./vim.config
xam.xri=snia-xam://SNIA_Reference_VIM!localhost?dir=/home/fpm/rss/store
xam.username=testuser
xam.password=testpasswd

Here is my vim.config: It is basically the same as comes with the XAM SDK. The only difference is that I create a new log every time the application is invoked rather than appending to the existing log.

.xam.config.vim.alias.SNIA_Reference_VIM=org.snia.xam.vim.reference.ReferenceVIM
xam_boolean..xam.log.append=false
xam_int..xam.log.max.size=200
xam_int..xam.log.verbosity=1
xam_int..xam.log.level=5

Here is a copy of my rss.xml. It contains a simple feed i.e. this blog. You can add more feeds like you like. The check attribute is for future use and is not used by this application.

<config>
<feed check="10">http://blog.fpmurphy.com/feed</feed>
</config>

Here is the output when this application is complied and invoked.

$ javac RSS2XAM.java ExampleBase.java
$ java RSS2XAM
Connecting to Reference VIM
Initializing VIM
Loading properties from file: rss.props
VIM Configuration contained in file: ./vim.config
Connection arguments: snia-xam://SNIA_Reference_VIM!localhost?dir=/home/fpm/rss/store

Number of Feeds: 1
RSS feed: http://blog.fpmurphy.com/feed (rss_2.0)

Entry: 0
Title: Amazon SimpleDB Typica Tutorial
Link: http://blog.fpmurphy.com/2009/08/amazon-simpledb-typica-tutorial.html
Author: fpmurphy
Date Published: Tue Aug 04 12:35:14 GMT-05:00 2009
Categories: AWS, Java, XAM
XSet XUID ==> AAA6AwAqbu8xMjUwNDc2OTYwNzcwAcSLfePaTUlTnoaEkQ5kBOgDBBh2

Entry: 1
Title: XAM Query Language
Link: http://blog.fpmurphy.com/2009/07/xam-query-language.html
Author: fpmurphy
Date Published: Sun Jul 19 10:10:41 GMT-05:00 2009
Categories: XAM, XSet, XStream, XSystem, SNIA
XSet XUID ==> AAA6AwAqbH8xMjUwNDc2OTYwODExAjloAZmNdUUPto94Weh/11seuv3/

Entry: 2
Title: Linux HPET Support
Link: http://blog.fpmurphy.com/2009/07/linux-hpet-support.html
Author: fpmurphy
Date Published: Mon Jul 06 02:50:03 GMT-05:00 2009
Categories: C, HPET, Linux
XSet XUID ==> AAA6AwAquJ0xMjUwNDc2OTYwODMxAxk6BG7OQErMh3iqx3BMNwYGbeYJ

Entry: 3
Title: XAM Mandated Fields
Link: http://blog.fpmurphy.com/2009/06/xam-mandated-fields.html
Author: fpmurphy
Date Published: Mon Jun 29 21:07:04 GMT-05:00 2009
Categories: XAM, SNIA
XSet XUID ==> AAA6AwAqUWoxMjUwNDc2OTYwODUxBM5rT72XVEEeuQvgMU1G47JugiVN

Entry: 4
Title: Atahualpa Theme
Link: http://blog.fpmurphy.com/2009/06/blog-now-uses-wordpress-with-atahualpa-theme.html
Author: fpmurphy
Date Published: Wed Jun 24 16:14:55 GMT-05:00 2009
Categories: Uncategorized
XSet XUID ==> AAA6AwAqGhAxMjUwNDc2OTYwODY5BbZxkkmfZUMmnKAsE/THFApESwu7

Entry: 5
Title: XAM Canonical Format
Link: http://blog.fpmurphy.com/2009/06/xam-and-xop.html
Author: fpmurphy
Date Published: Sat Jun 20 17:15:00 GMT-05:00 2009
Categories: XAM, XOP, SNIA
XSet XUID ==> AAA6AwAq2AkxMjUwNDc2OTYwODg4Bhk1ODii0UOQlaAbfG+dvMwBUxFk

Entry: 6
Title: Fedora 11 New Extended File Attributes Namespace
Link: http://blog.fpmurphy.com/2009/06/fedore-11-extended-attibutes-namespace.html
Author: fpmurphy
Date Published: Mon Jun 15 09:31:00 GMT-05:00 2009
Categories: Extended Attributes, Fedora 11, XAM, Linux
XSet XUID ==> AAA6AwAqa8QxMjUwNDc2OTYwOTA3B3Mj+tqXN0YJjuftnzTjYW5uPlqR

Entry: 7
Title: Fedora 11 nVidia Twinview Support
Link: http://blog.fpmurphy.com/2009/06/fedora-11-nvidia-twinview-support.html
Author: fpmurphy
Date Published: Thu Jun 11 12:07:00 GMT-05:00 2009
Categories: Fedora 11, Linux, Twinview, nVidia, Fedora
XSet XUID ==> AAA6AwAq4JQxMjUwNDc2OTYwOTI5CNTrNQ44okBPnnJFleb57UFNj5t1

Entry: 8
Title: Linux Security Capabilities
Link: http://blog.fpmurphy.com/2009/05/linux-security-capabilities.html
Author: fpmurphy
Date Published: Thu May 28 10:14:00 GMT-05:00 2009
Categories: Fedora 11, Linux, POSIX.1e, linux capabilities
XSet XUID ==> AAA6AwAqURQxMjUwNDc2OTYwOTU1CTY+vTycKkMhgRj22XFJ+CpMdnKG

Entry: 9
Title: Windows Parallel Filesystems
Link: http://blog.fpmurphy.com/2009/05/windows-parallel-filesystems.html
Author: fpmurphy
Date Published: Tue May 26 12:27:00 GMT-05:00 2009
Categories: Windows parallel filesystem, Windows
XSet XUID ==> AAA6AwAq/jsxMjUwNDc2OTYwOTg0CuIRPc1YkUozqtuI+oCaeTkQx4n7

$

As you can see from the following listing, the Reference VIM creates an XML file and a subdirectory for each XSet that it creates as well as a Derby database called ReferenceVimDB. The name of each XML file and corresponding subdirectory is the XUID of the XSet.

$ ls store
ReferenceVimDB XSet_AAA6AwAqGhAxMjUwNDc2OTYwODY5BbZxkkmfZUMmnKAsE_THFApESwu7
XSet_AAA6AwAq2AkxMjUwNDc2OTYwODg4Bhk1ODii0UOQlaAbfG-dvMwBUxFk XSet_AAA6AwAqGhAxMjUwNDc2OTYwODY5BbZxkkmfZUMmnKAsE_THFApESwu7.xml
XSet_AAA6AwAq2AkxMjUwNDc2OTYwODg4Bhk1ODii0UOQlaAbfG-dvMwBUxFk.xml XSet_AAA6AwAq_jsxMjUwNDc2OTYwOTg0CuIRPc1YkUozqtuI-oCaeTkQx4n7
XSet_AAA6AwAq4JQxMjUwNDc2OTYwOTI5CNTrNQ44okBPnnJFleb57UFNj5t1 XSet_AAA6AwAq_jsxMjUwNDc2OTYwOTg0CuIRPc1YkUozqtuI-oCaeTkQx4n7.xml
XSet_AAA6AwAq4JQxMjUwNDc2OTYwOTI5CNTrNQ44okBPnnJFleb57UFNj5t1.xml XSet_AAA6AwAquJ0xMjUwNDc2OTYwODMxAxk6BG7OQErMh3iqx3BMNwYGbeYJ
XSet_AAA6AwAqa8QxMjUwNDc2OTYwOTA3B3Mj-tqXN0YJjuftnzTjYW5uPlqR XSet_AAA6AwAquJ0xMjUwNDc2OTYwODMxAxk6BG7OQErMh3iqx3BMNwYGbeYJ.xml
XSet_AAA6AwAqa8QxMjUwNDc2OTYwOTA3B3Mj-tqXN0YJjuftnzTjYW5uPlqR.xml XSet_AAA6AwAqURQxMjUwNDc2OTYwOTU1CTY-vTycKkMhgRj22XFJ-CpMdnKG
XSet_AAA6AwAqbH8xMjUwNDc2OTYwODExAjloAZmNdUUPto94Weh_11seuv3_ XSet_AAA6AwAqURQxMjUwNDc2OTYwOTU1CTY-vTycKkMhgRj22XFJ-CpMdnKG.xml
XSet_AAA6AwAqbH8xMjUwNDc2OTYwODExAjloAZmNdUUPto94Weh_11seuv3_.xml XSet_AAA6AwAqUWoxMjUwNDc2OTYwODUxBM5rT72XVEEeuQvgMU1G47JugiVN
XSet_AAA6AwAqbu8xMjUwNDc2OTYwNzcwAcSLfePaTUlTnoaEkQ5kBOgDBBh2 XSet_AAA6AwAqUWoxMjUwNDc2OTYwODUxBM5rT72XVEEeuQvgMU1G47JugiVN.xml
XSet_AAA6AwAqbu8xMjUwNDc2OTYwNzcwAcSLfePaTUlTnoaEkQ5kBOgDBBh2.xml
$

Here is the contents of one of these XML files. It corresponds to one post in my blog. You will see the set of properties the application created for this particular post towards the end of the file.

<?xml version="1.0" encoding="UTF-8"?>
<xsets xsi:schemaLocation="http://www.snia.org/2007/xam/export/XAMCanonicalXSetDefinition.xsd" xmlns="http://www.snia.org/2007/xam/export" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xop="http://www.w3.org/2004/08/xop/include">
<version>1.0.0</version>
<policies>
<policy name=".xsystem.management.policy.list.org.snia.refvim.default.mgmt.policy" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="35">
<string>org.snia.refvim.default.mgmt.policy</string>
</policy>
</policies>
<xset>
<properties>
<property name=".xset.hold" type="application/vnd.snia.xam.boolean" binding="false" readOnly="true" length="1">
<boolean>false</boolean>
</property>
<property name=".xset.management.policy" type="application/vnd.snia.xam.string" binding="true" readOnly="true" length="35">
<string>org.snia.refvim.default.mgmt.policy</string>
</property>
<property name=".xset.retention.base.enabled" type="application/vnd.snia.xam.boolean" binding="true" readOnly="true" length="1">
<boolean>true</boolean>
</property>
<property name=".xset.retention.base.starttime" type="application/vnd.snia.xam.datetime" binding="true" readOnly="true" length="29">
<date>2009-08-16T21:42:40.742-05:00</date>
</property>
<property name=".xset.retention.list.base" type="application/vnd.snia.xam.string" binding="true" readOnly="true" length="4">
<string>base</string>
</property>
<property name=".xset.retention.list.event" type="application/vnd.snia.xam.string" binding="true" readOnly="true" length="5">
<string>event</string>
</property>
<property name=".xset.time.access" type="application/vnd.snia.xam.datetime" binding="false" readOnly="true" length="29">
<date>2009-08-16T21:42:40.742-05:00</date>
</property>
<property name=".xset.time.commit" type="application/vnd.snia.xam.datetime" binding="false" readOnly="true" length="29">
<date>2009-08-16T21:42:40.742-05:00</date>
</property>
<property name=".xset.time.creation" type="application/vnd.snia.xam.datetime" binding="true" readOnly="true" length="29">
<date>2009-08-16T21:42:40.724-05:00</date>
</property>
<property name=".xset.time.residency" type="application/vnd.snia.xam.datetime" binding="false" readOnly="true" length="29">
<date>2009-08-16T21:42:40.742-05:00</date>
</property>
<property name=".xset.time.xuid" type="application/vnd.snia.xam.datetime" binding="false" readOnly="true" length="29">
<date>2009-08-16T21:42:40.742-05:00</date>
</property>
<property name=".xset.xuid" type="application/vnd.snia.xam.xuid" binding="true" readOnly="true" length="42">
<xuid>AAA6AwAqbu8xMjUwNDc2OTYwNzcwAcSLfePaTUlTnoaEkQ5kBOgDBBh2</xuid>
</property>
<property name="com.fpmurphy.rss.author" type="application/vnd.snia.xam.string" binding="false" readOnly="false" length="8">
<string>fpmurphy</string>
</property>
<property name="com.fpmurphy.rss.categories" type="application/vnd.snia.xam.string" binding="false" readOnly="false" length="14">
<string>AWS, Java, XAM</string>
</property>
<property name="com.fpmurphy.rss.link" type="application/vnd.snia.xam.string" binding="false" readOnly="false" length="69">
<string>http://blog.fpmurphy.com/2009/08/amazon-simpledb-typica-tutorial.html</string>
</property>
<property name="com.fpmurphy.rss.published_date" type="application/vnd.snia.xam.datetime" binding="false" readOnly="false" length="29">
<date>2009-08-04T12:35:14.000-05:00</date>
</property>
<property name="com.fpmurphy.rss.title" type="application/vnd.snia.xam.string" binding="false" readOnly="false" length="31">
<string>Amazon SimpleDB Typica Tutorial</string>
</property>
</properties>
<xstreams>
</xstreams>
</xset>
</xsets>

Well, that is all for now. As usual, I hope this post was useful to you and that you have increased your knowledge of XAM Storage Systems and how to use the XAM Reference VIM.

As the SNIA XAM SDK matures, the Reference VIM is becoming more useful for prototyping a XAM Storage System. Maybe one day, road warriors can use something like the Reference VIM to do useful work while disconnected from their corporate XAM Storage System with the Reference VIM automatically syncing up with the corporate XAM Storage System when a secure network connection becomes available.
 

XAM Query Language

As well as providing vendor-independent means of creating, retrieving, modifying and deleting XSets, the SNIA XAM v1.0 specification also defines a query language (XAM QL), based on a subset of the SQL language, for selecting and retrieving the XUIDs of XSets based on content-defined criteria.

The set of reserved words for this query language is quite small: select, where, and, or, not, like, exists, binding, readonly, typeof, length, date, TRUE, FALSE, before, after, contains, and within. By design, XAM queries look like an SQL select statement. The query language is case insensitive and uses the ASCII character set.

Here is an example of a simple XAM query:

select ".xset.xuid" where "com.example.name" = ’Tuckers Plantation'


Version 1.0 of the XAM specification defines two levels of query language support, i.e. Level 1 and Level 2. Level 1 defines queries on properties and field attributes in XSets and is mandatory. Any XSet property value that is accessible to an application program via the XAM library can be queried. Level 2 extends Level 1 to support queries on XStreams and is optional. Both levels of query are accessed through a single, defined job type that all XAM providers must support. Since no vendor that I am aware of has actually implemented level 2 queries, the remainder of this post focuses on level 1 queries.

A XAM query statement consists of a mandatory select clause followed by an optional where clause. For XAM v1.0 the only valid select clause is select '.xset.xuid'. This specifies that the application is requesting a list of XUID values. For example

select ".xset.xuid "


will return a list of every XSet that is readable at the time of the query.

The where clause is used to specify a subset of XSets to be matched. For level 1 queries it is restricted to comparisons between XSet properties and literal values and/or field attributes and literal values.

select ".xset.xuid" where ".xam.time.xuid" > date(’2009-02-01T00:00:00.0’)


will return the list of all Xsets which were created on or after February 1st 2009.

The following table shows which field and literal types can be validly compared.
[table id=14/]
The XAM library validates that strings and strings liberals are conforming UTF-8 strings. Non-conforming UTF-8 string literals generate a XAM non-fatal query syntax error. Issues such as single versus multiple glyph characters and non printable characters are unspecified and are implementation and application dependent. String comparisons are case sensitive and comparison operators operate on a byte-by-byte basis. For relational operators the relationships are defined by the byte values. All data normalization is the responsibility of the application.

The supported escape sequences are the following. Any other escape sequence will generate an non-fatal error.
[table id=15/]
String literals must be quoted with single quotes. String literals which contain one or more single quote characters must escape each single quote character using a backslash. For example, the string literal of ’This is a string literal’ would be represented in a query as

select ".xset.xuid" where "com.example.string-property" = ’This is a string literal’


To use a single quote in a string, guard it with a backslash

select ".xset.xuid" where "com.example.claimed.ownership" = ’Tucker\’s’


All instances of field names in a XAM query string must be quoted with double quotes. If a field name contains double quote characters, each double quote character must be escaped using a backslash. For example, the field name for the xam_boolean property com.example.”qstring” should be represented in a query as

select ".xam.xuid" where "com.example.\"qstring\"" = TRUE


If a field name contains a backslash character, the backslash itself must be escaped with another backslash. For example, the field name for the xam_double property com.example.file\ratio should be represented in a query as

select ".xam.xuid" where "com.example.file\\ratio" = 100.1


The query language accepts date-time and XUID-valued literals using the selector functions date() and xuid() respectively. The date() function takes a properly formed date-time value, specified as a literal string which is consistent with the XAM date-time specification.

select ".xset.xuid" where ".xam.time.xuid" = date(’2009-06-01T00:00:00.0’)


The xuid() function expects an XUID which is in the form of a base64 encoded string literal. An improperly formed string literal generates a non-fatal error during query parsing.

Field attributes are accessed using the exists, typeof, readonly, binding, and length field attribute accessor functions. The exists() function tests for the existence of an named field attribute (property) in an XSet. It evaluates to TRUE if an XSet contains the named field otherwise it evaluates FALSE.

select ".xset.xuid" where exists("com.example.name")


The typeof() function returns the MIME type of a named field in an XSet which is a string-valued property.

select ".xset.xuid" where typeof("com.example.data") = ’text/plain’
select ".xset.xuid" where typeof("com.example.data") like ’text%’


This function can be used whenever an application could use a field reference to a string-valued property. Note that comparisons with any non-string literal value generate a non-fatal error during the parsing of the query.

The readonly() function evaluates to TRUE when a field in an XSet is marked as readonly.

select ".xset.xuid" where readonly("com.example.flag")
select ".xset.xuid" where not readonly("com.example.name")


The binding() function evaluates to TRUE when a field in an XSet is marked as binding.

select ".xset.xuid" where binding("com.example.case_id")
select ".xset.xuid" where not binding("com.example.subject")


The length() function returns the length, in bytes, of a named field.

select ".xset.xuid" where length("com.example.data") > 1024


When used on property fields this function returns the length as defined for stypes. Note that this function should not be used to compare the number of characters in a string as this comparison depends on the character encoding being used.

Turning now to logical operators. Subclauses within the where clause may be combined and modified by using the logical operators and, or and not. These operators are similar to their SQL counterparts.

The and operator requires both subclauses to evaluate to TRUE before including an XSet in the results. For example

select ".xset.xuid" where typeof("com.example.stream") = ’image/gif’ and length("com.example.stream") > 4096


selects only those XSets containing GIF images whose size is greater than 4096 bytes. The or operator evaluates to TRUE if either subclause evaluates to TRUE. For example

select ".xset.xuid" where typeof("com.example.stream") = ’image/jpeg’ or typeof("com.example.stream") = ’image/gif’


selects only those XSets containing a named stream of image type JPEG or GIF. The not operator negates a boolean expression. For example

select ".xset.xuid" where not binding("com.example.property")


selects all XSets with the nonbinding property com.example.property.

As in SQL certain operators take precedence over other operators in queries. XAM QL operator precedence is as follows:
[table id=16/]
Operators of the same precedence are evaluated left to right within a query. However operator precedence may be overridden using parentheses as shown by the following example.

select ".xset.xuid" where not "com.example.bool-prop" and  "com.example.int-prop" = 64
select ".xset.xuid" where not ("com.example.bool-prop" and "com.example.int-prop" = 64)


In the first example, the not operator only applies to the com.example.bool-prop property. In the second example, the not operator applies to both com.example.bool-prop and com.example.int-prop = 42.

Now that we are somewhat familiar with XAM QL syntax, the next question to be answered is how do we tell a XAM Storage System to execute a query and return the results to us. Well it turns out that a query is executed as a particular type of XAM job called a query job. The input to a query job is an XSet which you must create. This XSet must contain two items - the field org.snia.xam.job.command which is always set to 'xam.job.query' and a XStream xam.job.query.command which is a UTF-8 text stream (MIME type 'text/plain; charset=utf-8’) containing the actual query expression string.

A query job may optionally be committed before or during the execution of the job, but this commit is not required. The ability to commit a running job is optional and implementation dependant. An application can determine if job commit is supported by checking the XSystem boolean property .xsystem.job.commit.supported. Failure to commit a query job before closing the XSet means the results of the query job are not persistently stored.

A query job is executed by invoking submitJob. If either of the two required items in the query job XSet is not correctly filled in, the standard job error field .xam.job.error is added to the query job XSet and it's value is set to either org.snia.xam::not_a_job or org.snia.xam::unspecified_command and the query job is aborted. As shown in the sample application below, the state of a query job may be determined by examining the value of .xam.job.status.

When a query job is successful, the results are stored in the query job XSet in the form of a new XStream named xam.job.query.results. This XStream has a MIME type of application/vnd.snia.xam.query.xuid_list and contains the XUIDs for the set of XSets resulting from the evaluation of the query. Each XUID is stored in a binary format as part of an 80-byte record. If an XUID is shorter than 80 bytes, the record is zero padded to 80 bytes.

A number of other properties relating to the results XStream are also set up. The xam_int property xam.job.query.results.count contains the current count of the XUID records in the results XStream. This property is updated as results are entered into the results XStream during query processing. An application can use this to provide status information to users as the query job executes. The xam_string property xam.job.query.level indicates the query level which matches the results. Its value is either org.snia.xam.job.query.level.1 or org.snia.xam.job.query.level.2.

It is important to remember that a query job operates within the roles and permissions granted to the connection. That means the results XStream only includes those XSets that are visible and accessible to an application, at least from a read perspective, according to the role under which the query job is executed.

The query job may set the following error codes in .xam.job.error:
[table id=17/]
When a query job is halted for whatever reason, the standard job health and status fields, i.e .xam.job.errorhealth and .xam.job.status, are set to appropriate values and the XAM Storage System may place zero or more additional XUIDs in the results XStream. Thus an application should always see complete XUID values in the results XStream. Resuming a halted query job is not supported.

Note that, unlike a traditional RDBMS, there is no locking of an XAM Storage System during the execution of a query job. Thus a query result is not an instantaneous snapshot of a XAM Storage System. The contents of a XAM Storage System may or may not change while a query job is executed. The general rule is that any XSet that has been stored before the start of the query job is included in the results XStream if it meets the query criteria, and any XSet that has been stored after the query job has completed is not be included in the results XStream. XSets that are stored during the query job execution may or may not be included. The same criteria applies to the results XStream. It may include XUIDs of XSets that are no longer in the XAM Storage System and XUIDs of XSets that no longer meet the search criteria due to a change in the XSet after the particular XSet was queried.

Here is a simple Java application that queries an XAM Storage System to determine it's level of support for XAM queries.

import org.snia.xam.XAMException;
import org.snia.xam.XAMLibrary;
import org.snia.xam.XSystem;
import org.snia.xam.toolkit.XAMXUID;
import org.snia.xam.util.XAMLibraryFactory;

public class CheckQuerySupport
{
private static XAMLibrary xamLib;
private static XSystem xSystem;

public static void main(String[] args)
{
String xri = "snia-xam://centera_vim!XXX.XXXX.XXX.XXX?/home/fpm/xam/xamconnect.pea";

long exitCode = 0;
boolean isSupported = false;

try
{
xamLib = XAMLibraryFactory.newXAMLibrary();
System.out.println("Connecting to XSystem: " + xri + "\n");

xSystem = xamLib.connect(xri);

// support job commit?
isSupported = xSystem.getBoolean(XSystem.XAM_XSYSTEM_JOB_COMMIT_SUPPORTED);
System.out.println("Support job commit? " + (isSupported ? "Yes" : "No"));

// support continuance of job?
isSupported = xSystem.getBoolean(XSystem.XAM_XSYSTEM_JOB_QUERY_CONTINUANCE_SUPPORTED);
System.out.println("Support job continuance? " + (isSupported ? "Yes" : "No"));

// support level 1 query?
isSupported = xSystem.getBoolean(XSystem.XAM_XSYSTEM_JOB_QUERY_LEVEL1_SUPPORTED);
System.out.println("Support level 1 query? " + (isSupported ? "Yes" : "No"));

// support level 2 query?
isSupported = xSystem.getBoolean(XSystem.XAM_XSYSTEM_JOB_QUERY_LEVEL2_SUPPORTED);
System.out.println("Support level 2 query? " + (isSupported ? "Yes" : "No"));

xSystem.close();
System.out.println("\nClosed connection to XSystem");

} catch (XAMException xe) {
exitCode = xe.getStatusCode();
System.err.println("XAM Error occured: " + xe.getMessage());
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
e.printStackTrace();
exitCode = 1;
}

System.exit((int) exitCode);
}
}

Here is the output from this application when connected to an EMC Centera XAM Storage System

Connecting to XSystem: snia-xam://centera_vim!XXX.XXX.XXX.XXX?/home/fpm/xam/xamconnect.pea

Support job commit? No
Support job continuance? No
Support level 1 query? Yes
Support level 2 query? No

Closed connection to XSystem

Here is a longer Java application that implements the three sample XSets and the sample queries which are discussed in section 10.8 of the XAM Architecture V1.0 document.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.snia.xam.XAMException;
import org.snia.xam.XAMLibrary;
import org.snia.xam.XSet;
import org.snia.xam.XStream;
import org.snia.xam.XSystem;
import org.snia.xam.XUID;
import org.snia.xam.toolkit.XAMXUID;
import org.snia.xam.util.XAMLibraryFactory;

public class QueryExample
{
private static XAMLibrary xamLib;
private static XSystem xSystem;

private static void CreateXset(int parm1, String parm2, double parm3)
throws XAMException
{
System.out.print("Create XSet 1 .... ");

XSet xSet = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
xSet.createProperty("com.example.rhc", false, true);
xSet.createProperty("com,example.foo", false, parm1);
xSet.createProperty("com.example.bar", false, parm2);
xSet.createProperty("com.example.num", false, parm3);

XUID xuID = xSet.commit();
System.out.println("XUID: " + xuID.toString());

xSet.close();
}

private static void CreateXset(int parm1, int parm2, int parm3)
throws XAMException
{
System.out.print("Create XSet 2 .... ");

XSet xSet = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
xSet.createProperty("com.example.rhc", false, true);
xSet.createProperty("com,example.foo", false, parm1);
xSet.createProperty("com.example.bar", false, parm2);
xSet.createProperty("com.example.num", false, parm3);

XUID xuID = xSet.commit();
System.out.println("XUID: " + xuID.toString());

xSet.close();
}

private static void CreateXset(int parm1, int parm2)
throws XAMException
{
System.out.print("Create XSet 3 .... ");

XSet xSet = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
xSet.createProperty("com.example.rhc", false, true);
xSet.createProperty("com,example.foo", false, parm1);
xSet.createProperty("com.example.num", false, parm2);

XUID xuID = xSet.commit();
System.out.println("XUID: " + xuID.toString());

xSet.close();
}

private static void QueryXsystem(String queryString)
throws XAMException
{
boolean finished = false;
final int XAM_MAX_XUID = 80;
int resultCount = 0;
String status;

System.out.println("Create query XSet. Query string: " + queryString);

XSet query = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
query.createProperty(XSet.XAM_JOB_COMMAND, true, XSet.XAM_JOB_QUERY);

byte[] buffer = queryString.getBytes();
XStream queryStream = query.createXStream(XSet.XAM_JOB_QUERY_COMMAND, true, XAMLibrary.TEXT_PLAIN_MIME_TYPE);
queryStream.write(buffer);
queryStream.close();

System.out.println("Submit query job ....");
query.submitJob();

System.out.println("Wait for query job to finish ....");

while (!finished)
{
// check status of query job
status = query.getString(XSet.XAM_JOB_ERRORHEALTH);
if (status.equals(XSet.XAM_JOB_ERRORHEALTH_ERROR))
{
System.out.println("ERROR: Errorhealth - " + query.getString(XSet.XAM_JOB_ERROR));
query.haltJob();
System.out.println("ERROR: Job halted");
break;
}

status = query.getString(XSet.XAM_JOB_STATUS);
resultCount = (int) query.getLong(XSet.XAM_JOB_QUERY_RESULTS_COUNT);

// uncomment if you want continuous job status
// System.out.println("Job Status: " + status + " Result count: " + resultCount);

// exit loop if job complete
if (status.equals(XSet.XAM_JOB_STATUS_COMPLETE))
{
finished = true;
}
}

System.out.println("Query job completed ....");
System.out.println("Result set final count is: " + resultCount);

if (resultCount > 0)
{
// open query job result stream
XStream results = query.openXStream(XSet.XAM_JOB_QUERY_RESULTS, XStream.MODE_READ_ONLY);

byte rawXUID[] = new byte[XAM_MAX_XUID];
long bytesRead = 0;

// output XUIDs in result set
while( (bytesRead = results.read(rawXUID, XAM_MAX_XUID)) >= 0 )
{
XAMXUID resultXUID = new XAMXUID(rawXUID);
System.out.println("XUID: " + resultXUID.toString());
}

results.close();
}

query.close();
}

public static void main(String[] args)
{
String xri = "snia-xam://centera_vim!XXX.XXX.XXX.XXX?/home/fpm/xam/xamconnect.pea";
long exitCode = 0;

try
{
xamLib = XAMLibraryFactory.newXAMLibrary();

// uncomment next 2 lines if you want extensive XAM Library logging
// xamLib.setProperty(XAMLibrary.XAM_LOG_VERBOSITY, 1);
// xamLib.setProperty(XAMLibrary.XAM_LOG_LEVEL, 5);

System.out.println("Connecting to XSystem " + xri + "\n");

xSystem = xamLib.connect(xri);
xSystem.setProperty(".xsystem.log.verbosity", 1);
xSystem.setProperty(".xsystem.log.level", 5);

CreateXset(1, "string", 123.55);
CreateXset(77, 42, 100);
CreateXset(6, 200);

xSystem.close();

System.out.println("\nClose/reopen XSystem\n");

xSystem = xamLib.connect(xri);

// uncomment one or more of the following queries
// QueryXsystem("select \".xset.xuid\"");
QueryXsystem("select \".xset.xuid\" where (\"com.example.foo\" > 0) and (\"com.example.foo\" < 50)");
// QueryXsystem("select \".xset.xuid\" where (\"com.example.bar\" > 0) and (\"com.example.bar\" < 100)");
// QueryXsystem("select \".xset.xuid\" where exists (\"com.example.bar\")");
// QueryXsystem("select \".xset.xuid\" where \"com.example.bar\" like '%ing%'");
// QueryXsystem("select \".xset.xuid\" where \"com.example.num\" >= 124");
// QueryXsystem("select \".xset.xuid\" where \"com.example.num\" >= 124.6 ");
// QueryXsystem("select \".xset.xuid\" where (\"com.example.num\" >= 123) and typeof(\"com.example.num\") = 'application/vnd.snia.xam.int'");

xSystem.close();
System.out.println("\nClosed connection to XSystem");

} catch (XAMException xe) {
exitCode = xe.getStatusCode();
System.err.println("XAM Error occured: " + xe.getMessage() + "("
+ exitCode + ")");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
e.printStackTrace();
exitCode = -1;
}

System.exit((int) exitCode);
}
}

Note that a results XStream produced by a query job may be consumed by an application before the XAM Storage System has finished storing all of the XUIDs in the results XStream if XStream.asyncRead is used instead of XStream.read.

Well I think I have provided enough information and examples to enable you to go play with the XAM query language by yourself. It you need help the I am sure the XAM Developer Group on Google Groups will be more than happy to help you.

In parting I would point out that since XAM Storage System providers are currently only required to implement support for queries over the content metadata, I suspect that support for actual full content-based searches using XAM may not occur for some time.
 

XAM Mandated Fields

In this post I look at what fields are mandated by the SNIA XAM v1.0 specification and write a small Java application to retrieve their default values using the XAM Reference VIM and EMC's Centera XAM VIM.

What is meant by a field in XAM? According to Section 3.1.5 of the XAM specification v1.0, Part 1, a field is
a piece of uniquely identifiable data that can be attached to an XSet, an XSystem, or a XAM Library.
More concretely, a field has a name, a number of attributes that describe how to interact with the object, and a value. Any XAM primary object, i.e. an XSystem, an XSet, or a XAM Library object can contain one or more fields.

Field names are case sentitive UTF-8 encoded strings with a maximum length of 512 bytes and no embedded NULL characters. To avoid namespace clashes, the field namespace is allocated between SNIA, XAM storage system vendors and XAM application vendors. The following table shows the currently reserved namespace for field names:
[table id=7 /]
To avoid field namespace clashes between XAM storage system vendors in the remaining unreserved namespace and aleviate the need for a central XAM field name registry, the first portion of a vendor field name shall be the vendor's domain name in reverse order, followed by the vendor-defined field name, e.g. com.emc.centera.xam.vim.version.

As mentionly previously a field can have attributes. The following four attributes are mandated by the XAM specification:
[table id=8 /]
Two distinct types of fields exist: a property and an XStream. For the purposes of this post, we are only interested in fields which are properties as defined in section 3.1.12 of the XAM specification, i.e
a field whose MIME type attribute is one of the XAM-defined simple types (stypes).

Here is a list of the specified XAM stypes:
[table id=9 /]
Turning now to the two objects that we are interested in, namely the XAM Library object and the XSystem object.

XAM Library properties are always nonbinding i.e. a change in the property value does not trigger the creation of a new XSet with a corresponding new XUID. Some may be readonly and intended to be only inspected by the application. Others such as .xam.log.level may be modifiable by a XAM application to effect a change in the behavior of the XAM Library object. Note however that changes are not persisted by the XAM Library.

Here is the list of the mandated properties for the XAM Library:
[table id=1 /]
An XSystem property is strongly typed to help XAM application interoperability. The stype is checked and the actual MIME type is set based on the specific method that the XAM application uses to create the field. A number of other field consistency checks are also done by an XSystem including checking that field names do not begin with a period, are a valid xam_string and do not have embedded NULLs. See section 6.3.5 of the XAM specification for a complete description of the field consistency checks.

Here is the list of mandated properties for an XSystem object:
[table id=2 /]
Here is a simple Java application which iterates over the list of available fields and output the fields which match the specified field prefix or all fields if no prefix is entered. It uses the XAM FieldIterator class to retrieve the requested fields from either the SNIA-XAM SDK reference VIM or a third party VIM such as the EMC Centera VIM.
import java.io.ByteArrayOutputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import java.util.Calendar;
import java.text.SimpleDateFormat;
import java.util.Properties;

import org.snia.xam.XAMLibrary;
import org.snia.xam.XAMLibraryObj;
import org.snia.xam.XAMException;
import org.snia.xam.XSystem;
import org.snia.xam.XSet;
import org.snia.xam.XIterator;
import org.snia.xam.toolkit.XAMXUID;
import org.snia.xam.util.XAMLibraryFactory;
import org.snia.xam.FieldContainer;

import org.snia.xam.XUID;
import org.snia.xam.base.XAMImplementation;
import org.snia.xam.util.SASLUtils;
import org.snia.xam.vim.reference.ReferenceAuthenticationStatus;
import org.snia.xam.vim.reference.utils.ReferenceSaslUtils;

public class XamFieldIterator {
private static XAMLibrary xamLib;
private static XSystem xSystem;
protected static String xri;

public static boolean IS_REFERENCE_VIM = false;
public static final String TEST_PROP_FILE = "xam.test.props";
public static final String XRI_PROP = "xam.test.xri";
public static final String CONFIG_PROP = "xam.test.vims";
public static final String USER_PROP = "xam.test.username";
public static final String PASS_PROP = "xam.test.password";
protected static final String DEFAULT_USER = "test";
protected static final String DEFAULT_PASS = "test";
protected static String s_pass;
protected static String s_user;

public static void initLibrary() throws Exception {
if (xamLib == null) {
System.out.println("\nInitializing VIM");
Properties props = new Properties();
String testPropFile = System.getProperty(TEST_PROP_FILE);
if (testPropFile == null)
testPropFile = TEST_PROP_FILE;

System.out.println("Loading test properties from file: " + testPropFile);
props.load(new FileInputStream(testPropFile));

xri = props.getProperty(XRI_PROP);
s_user = props.getProperty(USER_PROP, DEFAULT_USER);
s_pass = props.getProperty(PASS_PROP, DEFAULT_PASS);

// XAM Library loads the VIM name and associated Java implementation class
System.out.println("Loading the VIM using the Java XAM Library.");
System.out.println("VIM Configuration contained in file: " +
props.getProperty(CONFIG_PROP));
xamLib = new XAMImplementation(props.getProperty(CONFIG_PROP));
}
}

private static XSystem connectToVIM(String xri) throws Exception {
XSystem xsystem = null;
xsystem = xamLib.connect(xri);
return xsystem;
}

private static void authenticate(XSystem system) throws XAMException {
String defMech = system.getString(XSystem.XAM_XSYSTEM_AUTH_SASL_DEFAULT);
ByteArrayOutputStream response = new ByteArrayOutputStream(200);
byte[] inputData = null;
int retValue = 0;

if (defMech.equals(ReferenceSaslUtils.SASL_MECHANISM_ANONYMOUS)) {
retValue = system.authenticate(inputData, response);
} else if (defMech.equals(SASLUtils.SASL_PLAIN)) {
byte[] creds = ReferenceSaslUtils.encodeSASLPlain(null,
ReferenceAuthenticationStatus.TEST_USERNAME,
ReferenceAuthenticationStatus.TEST_PASSWORD);
retValue = system.authenticate(creds, response);
} else {
throw new XAMException("Unknown default SASL mechanism " + defMech);
}

// Validate return status value
if (retValue != XSystem.XAM_SASL_COMPLETE) {
throw new XAMException("Failed to authenticate.");
}
}

/* a FieldContainer is a XAM superclass for the 3 primary objects (XAMLibrary, XSystem, XSet) */
private static void iterateFields(FieldContainer fieldContainer, String prefix) throws XAMException {
String fieldName;
String fieldType;
String fieldValue;
String fieldBinding;
String fieldReadOnly;

XIterator xIterator = fieldContainer.openFieldIterator(prefix);
while (xIterator.hasNext()) {
fieldName = (String)xIterator.next();
fieldType = fieldContainer.getFieldType(fieldName);

fieldBinding = fieldContainer.getFieldBinding(fieldName) != true ? "NB" : "BI";
fieldReadOnly = fieldContainer.getFieldReadOnly(fieldName) != true ? "RW" : "RO";

if (fieldType.equals(XAMLibrary.STYPE_BOOLEAN_MIME_TYPE))
fieldValue = fieldContainer.getBoolean(fieldName) != true ? "false" : "true";
else if (fieldType.equals(XAMLibrary.STYPE_INT_MIME_TYPE))
fieldValue = Long.toString(fieldContainer.getLong(fieldName));
else if (fieldType.equals(XAMLibrary.STYPE_DOUBLE_MIME_TYPE))
fieldValue = Double.toString(fieldContainer.getDouble(fieldName));
else if (fieldType.equals(XAMLibrary.STYPE_XUID_MIME_TYPE))
fieldValue = fieldContainer.getXUID(fieldName).toString();
else if (fieldType.equals(XAMLibrary.STYPE_STRING_MIME_TYPE))
fieldValue = fieldContainer.getString(fieldName);
else if (fieldType.equals(XAMLibrary.STYPE_DATETIME_MIME_TYPE)) {
Calendar now = fieldContainer.getDateTime(fieldName);
int Y = now.get(Calendar.YEAR);
int M = now.get(Calendar.MONTH);
int D = now.get(Calendar.DAY_OF_MONTH);
int h = now.get(Calendar.HOUR_OF_DAY);
int m = now.get(Calendar.MINUTE);
int s = now.get(Calendar.SECOND);
fieldValue = Y + "-" + M + "-" + D + " " + h + ":" + m + ":" + s;
} else
fieldValue = fieldContainer.getFieldLength(fieldName) + " bytes";

System.out.println(String.format("%s (%s) %s %s \"%s\"",
fieldName, fieldType, fieldBinding, fieldReadOnly, fieldValue));
}
xIterator.close();
}

public static void main(String[] args) {
long exitCode = 0;

InputStreamReader inputReader = new InputStreamReader(System.in);
BufferedReader stdin = new BufferedReader(inputReader);

try {
/* simple check for command line option of "-r" */
if (args.length == 1 ) {
if (args[0].equals("-r"))
IS_REFERENCE_VIM = true;
}

if (IS_REFERENCE_VIM) {
initLibrary();
System.out.println("Connecting to XSystem " + xri + "\n");
xSystem = connectToVIM(xri);
authenticate(xSystem);
} else {
xri = "snia-xam://centera_vim!128.221.200.60?/home/fpm/xam/xamconnect.pea";
xamLib = XAMLibraryFactory.newXAMLibrary();
System.out.println("Connecting to XSystem " + xri + "\n");
xSystem = xamLib.connect(xri);
}

System.out.print("Enter prefix to filter results (blank for all): ");
String prefix = stdin.readLine();

iterateFields(xSystem, prefix);

xSystem.close();
System.out.println( "\nClosed connection to XSystem" );

} catch (XAMException xe) {
System.err.println("XAM ERROR: " + xe.getMessage());
exitCode = 1;
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
e.printStackTrace();
exitCode = 1;
} catch (IOException e) {
System.err.println("IO ERROR: " + e.getMessage());
e.printStackTrace();
exitCode = 1;
} catch (Exception ex) {
ex.printStackTrace();
exitCode = 1;
}

System.exit((int) exitCode);
}
}

Here is the set of fields, i.e those fields which start with .xam. for the XAMLibrary object when the application is connected to an EMC Centera using EMC's XAM VIM. It is followed by the set of fields, i.e. those fields starting with .xsystem., for an XSystem object which we create using this XAMLibrary object.
$ java XamFieldIterator
Connecting to XSystem snia-xam://centera_vim!XXX.XXX.XXX.XXX?/home/fpm/xam/xamconnect.pea

Enter prefix to filter results (blank for all): .xam

.xam.log.component.filter (application/vnd.snia.xam.string) NB RW ""
.xam.log.message.filter (application/vnd.snia.xam.string) NB RW ""
.xam.log.max.rollovers (application/vnd.snia.xam.int) NB RW "1"
.xam.log.max.size (application/vnd.snia.xam.int) NB RW "1048576"
.xam.log.append (application/vnd.snia.xam.boolean) NB RW "false"
.xam.log.format (application/vnd.snia.xam.int) NB RW "1"
.xam.log.path (application/vnd.snia.xam.string) NB RW "xam.log"
.xam.log.verbosity (application/vnd.snia.xam.int) NB RW "0"
.xam.log.level (application/vnd.snia.xam.int) NB RW "0"

Closed connection to XSystem

$ java XamFieldIterator
Connecting to XSystem snia-xam://centera_vim!XXX.XXX.XXX.XXX?/home/fpm/xam/xamconnect.pea

Enter prefix to filter results (blank for all): .xsystem

.xsystem.auth.identity.authorization (application/vnd.snia.xam.string) NB RO "xam_challenge"
.xsystem.auth.identity.authentication (application/vnd.snia.xam.string) NB RO "xam_challenge"
.xsystem.limits.maxSizeOfXStream (application/vnd.snia.xam.int) NB RO "107374182400"
.xsystem.limits.maxFieldsPerXSet (application/vnd.snia.xam.int) NB RO "9223372036854775807"
.xsystem.identity (application/vnd.snia.xam.string) NB RO "EMC Centera, ID# 34372862-1dd2-11b2-aea0-f013836b5e75"
.xsystem.auth.SASLmechanism.list.ANONYMOUS (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.SASLmechanism.list.PLAIN (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.SASLmechanism.default (application/vnd.snia.xam.string) NB RO "ANONYMOUS"
.xsystem.time (application/vnd.snia.xam.datetime) NB RO "2009-5-28 15:10:42"
.xsystem.auth.granule.list.read (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.write-application (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.write-system (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.create (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.delete (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.job (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.job-commit (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.auth.granule.list.hold (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.auth.granule.list.retention-event (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.job.list.xam.job.query (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.expiration (application/vnd.snia.xam.int) NB RO "-1"
.xsystem.access (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.job.commit.supported (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.job.xam.job.query.continuance.supported (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.job.xam.job.query.level2.supported (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.job.xam.job.query.level1.supported (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.deletion.autodelete (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.deletion.shred (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.management.policy.default (application/vnd.snia.xam.string) NB RO "default"
.xsystem.management.policy.list.default (application/vnd.snia.xam.string) NB RO "default"
.xsystem.retention.duration.policy.list.0seconds (application/vnd.snia.xam.string) NB RO "0seconds"
.xsystem.retention.duration.policy.list.10min (application/vnd.snia.xam.string) NB RO "10min"
.xsystem.retention.duration.policy.list.1day (application/vnd.snia.xam.string) NB RO "1day"
.xsystem.retention.duration.policy.list.1hr (application/vnd.snia.xam.string) NB RO "1hr"
.xsystem.retention.duration.policy.list.30days (application/vnd.snia.xam.string) NB RO "30days"
.xsystem.retention.duration.policy.list.60days (application/vnd.snia.xam.string) NB RO "60days"
.xsystem.retention.duration.policy.list.CBR_EMail (application/vnd.snia.xam.string) NB RO "CBR_EMail"
.xsystem.retention.duration.policy.list.CBR_Email (application/vnd.snia.xam.string) NB RO "CBR_Email"
.xsystem.retention.duration.policy.list.CBR_PACS (application/vnd.snia.xam.string) NB RO "CBR_PACS"
.xsystem.log.level (application/vnd.snia.xam.int) NB RW "0"
.xsystem.log.verbosity (application/vnd.snia.xam.int) NB RW "0"
.xsystem.log.path (application/vnd.snia.xam.string) NB RW "centera_vim.log"

Closed connection to XSystem
$

Here are the corresponding outputs when the application instead connects to the SNIA XAM-SDK reference VIM which incidently is written completely in Java; hence the need for two different factory methods in our application.
$ java XamFieldIterator -r
Initializing VIM
Loading test properties from file: xam.test.props
Loading the VIM using the Java XAM Library.
VIM Configuration contained in file: ../config/ReferenceVIM.config
Connecting to XSystem snia-xam://SNIA_Reference_VIM!localhost?dir=/home/fpm/xam/xam_storage

Enter prefix to filter results (blank for all): .xam
.xam.apiLevel (application/vnd.snia.xam.string) NB RW "01.00.00"
.xam.identity (application/vnd.snia.xam.string) NB RW "SNIA XAM Java Library v1.0"
.xam.log.append (application/vnd.snia.xam.boolean) NB RW "false"
.xam.log.level (application/vnd.snia.xam.int) NB RW "1"
.xam.log.max.rollovers (application/vnd.snia.xam.int) NB RW "1"
.xam.log.max.size (application/vnd.snia.xam.int) NB RW "10"
.xam.log.path (application/vnd.snia.xam.string) NB RW "SNIA-XAM.log"
.xam.log.verbosity (application/vnd.snia.xam.int) NB RW "1"
.xam.vim.list.SNIA_Reference_VIM (application/vnd.snia.xam.string) NB RW "SNIA_Reference_VIM"

Closed connection to XSystem

$ java XamFieldIterator -r

Initializing VIM
Loading test properties from file: xam.test.props
Loading the VIM using the Java XAM Library.
VIM Configuration contained in file: ../config/ReferenceVIM.config
Connecting to XSystem snia-xam://SNIA_Reference_VIM!localhost?dir=/home/fpm/xam/xam_storage

Enter prefix to filter results (blank for all): .xsystem
.xsystem.access (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.access.policy.list.read (application/vnd.snia.xam.string) NB RO "Read"
.xsystem.access.policy.list.write-application (application/vnd.snia.xam.string) NB RO "Write-application"
.xsystem.access.policy.list.write-system (application/vnd.snia.xam.string) NB RO "Write-system"
.xsystem.auth.SASLmechanism.default (application/vnd.snia.xam.string) NB RO "PLAIN"
.xsystem.auth.SASLmechanism.list.ANONYMOUS (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.SASLmechanism.list.PLAIN (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.expiration (application/vnd.snia.xam.int) NB RO "-1"
.xsystem.auth.granule.list.create (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.delete (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.hold (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.job (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.job-commit (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.read (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.retention-event (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.write-application (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.granule.list.write-system (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.auth.identity.authentication (application/vnd.snia.xam.string) NB RO ""
.xsystem.auth.identity.authorization (application/vnd.snia.xam.string) NB RO ""
.xsystem.deletion.autodelete (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.deletion.shred (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.identity (application/vnd.snia.xam.string) NB RO "ultra.localdomain File System"
.xsystem.job.commit.supported (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.job.list.xam.job.query (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.job.xam.job.query.continuance.supported (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.job.xam.job.query.level1.supported (application/vnd.snia.xam.boolean) NB RO "true"
.xsystem.job.xam.job.query.level2.supported (application/vnd.snia.xam.boolean) NB RO "false"
.xsystem.limits.maxFieldsPerXSet (application/vnd.snia.xam.int) NB RO "10000"
.xsystem.limits.maxSizeOfXStream (application/vnd.snia.xam.int) NB RO "9223372036854775807"
.xsystem.management.policy.default (application/vnd.snia.xam.string) NB RO ".org.snia.refvim.default.mgmt.policy"
.xsystem.management.policy.list..org.snia.refvim.default.mgmt.policy (application/vnd.snia.xam.string) NB RO ".org.snia.refvim.default.mgmt.policy"
.xsystem.time (application/vnd.snia.xam.datetime) NB RO "2009-5-29 10:24:0"

Closed connection to XSystem
$
As you can see there are lots of interesting and potentially useful information available in the fields associated with a XAMLIbrary or XSystem object. Hopefully this post has improved your knowledge of the mandated fields and the field namespace.

Happy Independence Day!
 

XAM Canonical Format

One of the key requirements for achieving long term data persistence is the ability to move data between archiving systems or, in the language of the SNIA XAM (eXtensible Access Method) specification, moving XSets between XSystems.

The XAM v1.0 specification supports this requirement by providing support for exporting and importing Xsets.  It specifies the methods used to export an XSet from an XSystem, the resultant XSet canonical data interchange format (package) and the methods used to import an Xset into an Xsystem

This post assumes that you are somewhat familiar with XAM and how to program to that specification using Java.  It focuses on the format and content of the XSet canonical format package which consists of two main parts: an XML document which describes the policies, properties and streams of one or more XSets followed by the binary representation of the stream(s).

The package format conforms to the 2005 W3C XML-binary Optimized Packaging (XOP) recommendation. To quote from the recommendation:

XOP define a general purpose serialization mechanism for the XML Infoset with binary content that is not only applicable to SOAP and MIME packaging, but to any XML Infoset and any packaging mechanism.
If you are unfamiliar with XOP, and most people are, an article by Andrey Butov in the December 2005 issue of Doctor Dobb's Journal contained a good introduction.

More than one XSet can be contained in a package.  However the current XAM SDK reference implementation only supports one XSet.  The XML document (AKA the XSet manifest) is a valid and well-formed XML document whose root element is xsets.  It can be parsed and manipulated using XSLT and other XML tools.  Annex B of the XAM Architecture document contains an XML Schema Definition (XSD) for the XSet manifest.

In order to study the package format in more detail, I wrote a small Java application called StoreHelloWorld which creates a new XSet containing two XStreams.  The first Xstream contains the source code for the ubiquitous HelloWorld.java program.  The second XStream contains the binary object HelloWorld.class encoded to base64 and with a MIME type of application/base64.  Normally you should not encode an XStream but displaying binary files in a blog is problematic and hence the workaround.

Here is the source code for StoreHelloWorld.
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.util.Calendar;

import org.snia.xam.XAMException;
import org.snia.xam.XAMLibrary;
import org.snia.xam.XSet;
import org.snia.xam.XStream;
import org.snia.xam.XSystem;
import org.snia.xam.XUID;
import org.snia.xam.util.XAMLibraryFactory;

public class StoreHelloWorld {
private static XAMLibrary xamLib;
private static XSystem xSystem;
private static final int BUFFER_SIZE = 1024 * 128;

private static XUID storeExportFile(String filename) throws XAMException {
XUID xuid = null;

System.out.println("Create XSet ....");
XSet xSet = xSystem.createXSet(XSet.MODE_UNRESTRICTED);
xSet.createProperty("app.filename", true, "HelloWorld.");

try {
System.out.println("Create XStream 1 ....");
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("HelloWorld.java"));
XStream xStream = xSet.createXStream("com.rhc.file.source", true, XAMLibrary.DEFAULT_MIME_TYPE);

byte[] buffer = new byte[BUFFER_SIZE];
long bytesRead = 0;

while((bytesRead = inputStream.read(buffer)) > 0) {
long bytesPos = 0;
while(bytesPos < bytesRead) {
long bytesWrite = xStream.write(buffer, bytesPos, bytesRead - bytesPos);
bytesPos += bytesWrite;
}
}
inputStream.close();
xStream.close();
} catch (FileNotFoundException fe) {
throw new IllegalArgumentException("Could not open HelloWorld.java for reading");
} catch (IOException ioe) {
System.err.println("Error reading from HelloWorld.java");
ioe.printStackTrace();
}

try {
System.out.println("Create XStream 2 ....");

InputStream inputStream = new Base64.InputStream(new FileInputStream("HelloWorld.class"), Base64.ENCODE | Base64.DO_BREAK_LINES);
XStream xStream = xSet.createXStream("com.rhc.file.binary", true, "application/base64");

byte[] buffer = new byte[BUFFER_SIZE];
long bytesRead = 0;

while((bytesRead = inputStream.read(buffer)) > 0) {
long bytesPos = 0;
while(bytesPos < bytesRead) {
long bytesWrite = xStream.write(buffer, bytesPos, bytesRead - bytesPos);
bytesPos += bytesWrite;
}
}
inputStream.close();
xStream.close();
} catch (FileNotFoundException fe) {
throw new IllegalArgumentException("Could not open HelloWorld.class for reading");
} catch (IOException ioe) {
System.err.println("Error reading from HelloWorld.class");
ioe.printStackTrace();
}

System.out.println("Commit XSet .... ");
xuid = xSet.commit();
System.out.println("Assigned XUID: " + xuid.toString());

System.out.println("Close XSet .... ");
xSet.close();

File saveFile = new File(filename);
try {
System.out.println("\nOpen XSet .... ");
xSet = xSystem.openXSet(xuid, XSet.MODE_READ_ONLY);

BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(saveFile));

System.out.println("Open XStream for export ....");
XStream xStream = xSet.openExportXStream();

byte[] buffer = new byte[BUFFER_SIZE];
long bytesRead = 0;

System.out.println("Retrieve data .... ");
while((bytesRead = xStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, (int)bytesRead);
}
xStream.close();
outputStream.close();

System.out.println("Close XSet .... ");
xSet.close();
} catch (IOException ioe) {
System.err.println("Error writing to " + saveFile);
ioe.printStackTrace();
}

return (xuid);
}

public static void main(String[] args) {
String xri = XRIstring.get();
long exitCode = 0;

InputStreamReader inputReader = new InputStreamReader(System.in);
BufferedReader stdin = new BufferedReader(inputReader);

try {
xamLib = XAMLibraryFactory.newXAMLibrary();

if (xri == null ) {
System.out.print("Enter Xsystem address: ");
String answer = stdin.readLine();
if (!answer.equals(""))
xri = answer;
else
System.exit(0);
}

System.out.println("Connecting to XSystem: " + xri );
xSystem = xamLib.connect(xri);

System.out.print("Name of export file: ");
String filename = stdin.readLine();
if (filename.equals(""))
System.exit(0);

XUID xuid = storeExportFile(filename);

xSystem.close();
System.out.println("\nClosed connection to XSystem");

inputReader.close();
stdin.close();

} catch (XAMException xe) {
exitCode = xe.getStatusCode();
System.err.println(
"XAM Error occured: " + xe.getMessage() + "(" + exitCode + ")");
} catch (IOException e) {
System.out.println("I/O Error occurred: " + e.getMessage());
e.printStackTrace();
exitCode = 1;
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
e.printStackTrace();
exitCode = 1;
}

System.exit((int) exitCode);
}
}
Rather than hard code an XRI (XAM Resource Identifier) in each XAM application, I use a separate Java class (XRIstring) to read in the XRI from the standard XAM properties file xam.properties.

I know that this is not kosher according to the current XAM specification but, in my humble opinion, this is the natural location for storing this string as most people will not be connecting simultaneously to multiple XSystems.

Here is the source for that Java class.
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.Properties;

public class XRIstring
{
private static Properties props;
private static String XRI = ".xam.config.xri";
private static String xri = null;

static {

props = new Properties();
try {
props.load(new FileInputStream("xam.properties"));

xri = props.getProperty(XRI);
if (xri != null)
System.out.println("XSystem Address: " + xri );

} catch (FileNotFoundException e) {
System.err.println("Could not find properties file. XSystem address must be manually entered");
} catch (IOException e) {
System.err.println("Could not access properties file. XSystem address must be manually entered");
}
}

public static String get()
{
return(xri);
}
}
This application also uses Robert Harder's popular Base64 class which is available here.  Assuming your CLASSPATH is set up correctly to find the XAM libraries, here is the output from StoreHelloWorld when you build and run it.
$ javac StoreHelloWorld.java XRIstring.java Base64.java

$ java StoreHelloWorld
Connecting to XSystem: snia-xam://centera_vim!XXX.XXX.XXX.XXX?/home/fpm/xam/xamconnect.pea
Name of export file: sample
Create XSet ....
Create XStream 1 ....
Create XStream 2 ....
Commit XSet ....
Assigned XUID: AAAEcwA9S8c0UTVHVDdRSDRSUU9VZUVWMkEyTTJGUUw2UUlHNDE0N1ZTMzFKTzFURkxWUjVKNkFDVEo2Ng==
Close XSet ....

Open XSet for export ....
Retrieve data ....
Close XSet ....

Closed connection to XSystem
$
Here is the contents of the export file (sample) which was created.
Content-Type: Multipart/Related;boundary="___EMC_XOP_BOUNDARY_START___1245537694___EMC_XOP_BOUNDARY_END___";
type="application/xop+xml";
start="<canonicalxset.xml@snia.org>";
start-info="text/xml"
Content-Description: EMC XSet Export Package

--___EMC_XOP_BOUNDARY_START___1245537694___EMC_XOP_BOUNDARY_END___
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: 8bit
Content-ID: <canonicalxset.xml@snia.org>

<xsets xmlns="http://www.snia.org/2007/xam/export"
xmlns:xop="http://www.w3.org/2004/08/xop/include"
xmlns:emcannotations="http://www.emc.com/2008/01/centera/emcannotations"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.snia.org/2007/xam/export XAMCanonicalXSetDefinition.xsd
http://www.w3.org/2004/08/xop/include http://www.w3.org/2004/08/xop/include
http://www.emc.com/2008/01/centera/emcannotations emcannotations.xsd ">
<version>1.0.0</version>

<policies>
<policy name=".xsystem.management.policy.list.default" type="application/vnd.snia.xam.string" readOnly="true" binding="false" length="7" >
<value>default</value>
</policy>
</policies>

<xset>
<properties>
<property name="app.filename" type="application/vnd.snia.xam.string" readOnly="false" binding="true" length="11" emcannotations:seqno="0" >
<value>HelloWorld.</value>
</property>
<property name=".xset.time.creation" type="application/vnd.snia.xam.datetime" readOnly="true" binding="true" length="24" emcannotations:seqno="3" >
<value>2009-06-20T22:20:19.000Z</value>
</property>
<property name=".xset.time.access" type="application/vnd.snia.xam.datetime" readOnly="true" binding="false" length="24" emcannotations:seqno="4" >
<value>2009-06-20T22:20:21.000Z</value>
</property>
<property name=".xset.time.commit" type="application/vnd.snia.xam.datetime" readOnly="true" binding="false" length="24" emcannotations:seqno="5" >
<value>2009-06-20T22:20:21.000Z</value>
</property>
<property name=".xset.time.residency" type="application/vnd.snia.xam.datetime" readOnly="true" binding="false" length="24" emcannotations:seqno="6" >
<value>2009-06-20T22:20:21.000Z</value>
</property>
<property name=".xset.xuid" type="application/vnd.snia.xam.xuid" readOnly="true" binding="false" length="61" emcannotations:seqno="7" >
<value>AAAEcwA9S8c0UTVHVDdRSDRSUU9VZUVWMkEyTTJGUUw2UUlHNDE0N1ZTMzFKTzFURkxWUjVKNkFDVEo2Ng==</value>
</property>
<property name=".xset.time.xuid" type="application/vnd.snia.xam.datetime" readOnly="true" binding="true" length="24" emcannotations:seqno="8" >
<value>2009-06-20T22:20:21.000Z</value>
</property>
<property name=".xset.retention.list.base" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="4" emcannotations:seqno="9" >
<value>base</value>
</property>
<property name=".xset.retention.base.enabled" type="application/vnd.snia.xam.boolean" readOnly="true" binding="true" length="1" emcannotations:seqno="10" >
<value>true</value>
</property>
<property name=".xset.retention.base.duration" type="application/vnd.snia.xam.int" readOnly="true" binding="true" length="8" emcannotations:seqno="11" >
<value>0</value>
</property>
<property name=".xset.retention.base.starttime" type="application/vnd.snia.xam.datetime" readOnly="true" binding="true" length="24" emcannotations:seqno="12" >
<value>2009-06-20T22:20:19.000Z</value>
</property>
<property name=".xset.hold" type="application/vnd.snia.xam.boolean" readOnly="true" binding="false" length="1" emcannotations:seqno="13" >
<value>false</value>
</property>
<property name=".xset.management.policy" type="application/vnd.snia.xam.string" readOnly="true" binding="false" length="7" emcannotations:seqno="14" >
<value>default</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.creation.poolid" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="39" emcannotations:seqno="15" >
<value>34372862-1dd2-11b2-aea0-f013836b5e75-10</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.retention.base.enabled" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="4" emcannotations:seqno="16" >
<value>true</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.sdk.version" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="7" emcannotations:seqno="17" >
<value>4.0.672</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.modification.poolid" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="39" emcannotations:seqno="18" >
<value>34372862-1dd2-11b2-aea0-f013836b5e75-10</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.retention.period" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="1" emcannotations:seqno="19" >
<value>0</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.type" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="11" emcannotations:seqno="20" >
<value>xam.binding</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.name" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="29" emcannotations:seqno="21" >
<value>EMC Centera Binding XSet Clip</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.creation.date" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="23" emcannotations:seqno="22" >
<value>2009.06.20 22:20:19 GMT</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.modification.date" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="23" emcannotations:seqno="23" >
<value>2009.06.20 22:20:21 GMT</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.creation.profile" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="13" emcannotations:seqno="24" >
<value>xam_challenge</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.modification.profile" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="13" emcannotations:seqno="25" >
<value>xam_challenge</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.numfiles" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="1" emcannotations:seqno="26" >
<value>2</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.totalsize" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="3" emcannotations:seqno="27" >
<value>731</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.refid" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="26" emcannotations:seqno="28" >
<value>6T90EGBR9LE5TFLVR5J6ACTJ66</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.clusterid" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="36" emcannotations:seqno="29" >
<value>34372862-1dd2-11b2-aea0-f013836b5e75</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.prev.clip" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="0" emcannotations:seqno="30" >
<value></value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.clip.naming.scheme" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="3" emcannotations:seqno="31" >
<value>MD5</value>
</property>
<property name=".vnd.com.emc.centera.meta.standard.binding.numtags" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="1" emcannotations:seqno="32" >
<value>3</value>
</property>
<property name=".vnd.com.emc.centera.hidden.nonbinding.clipid" type="application/vnd.snia.xam.string" readOnly="true" binding="false" length="4" emcannotations:seqno="33" >
<value>NULL</value>
</property>
<property name=".vnd.com.emc.centera.hidden.binding.clipid" type="application/vnd.snia.xam.string" readOnly="true" binding="true" length="53" emcannotations:seqno="34" >
<value>4Q5GT7QH4RQOUeEV2A2M2FQL6QIG4147VS31JO1TFLVR5J6ACTJ66</value>
</property>
</properties>
<xstreams>
<xstream name="com.rhc.file.source" type="application/octet-stream" readOnly="false" binding="true" length="156" emcannotations:seqno="1" >
<xop:Include href='cid:com.rhc.file.source' />
<emcannotations:emcannotation emcannotations:embedded="false" >
<emcannotations:emcblobannotation emcannotations:blobguid="1OROBIQ8CLBN3x8HRFRPNS7UV61G4147VS30JN0TFLVR5J6ACTJ66" emcannotations:offset="0" emcannotations:length="156" />
</emcannotations:emcannotation>
</xstream>
<xstream name="com.rhc.file.binary" type="application/base64" readOnly="false" binding="true" length="575" emcannotations:seqno="2" >
<xop:Include href='cid:com.rhc.file.binary' />
<emcannotations:emcannotation emcannotations:embedded="false" >
<emcannotations:emcblobannotation emcannotations:blobguid="09D943LSJ49S2xFF94NIQ5B10MGG4147VS31HG0TFLVR5J6ACTJ66" emcannotations:offset="0" emcannotations:length="575" />
</emcannotations:emcannotation>
</xstream>
</xstreams>
</xset>
</xsets>

--___EMC_XOP_BOUNDARY_START___1245537694___EMC_XOP_BOUNDARY_END___
Content-Type: text/text
Content-Transfer-Encoding: text
Content-ID: <TOC>

Offset of AAAEcwA9S8c0UTVHVDdRSDRSUU9VZUVWMkEyTTJGUUw2UUlHNDE0N1ZTMzFKTzFURkxWUjVKNkFDVEo2Ng==:com.rhc.file.source: 10955
Offset of AAAEcwA9S8c0UTVHVDdRSDRSUU9VZUVWMkEyTTJGUUw2UUlHNDE0N1ZTMzFKTzFURkxWUjVKNkFDVEo2Ng==:com.rhc.file.binary: 11285

--___EMC_XOP_BOUNDARY_START___1245537694___EMC_XOP_BOUNDARY_END___
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: com.rhc.file.source

/*
* HelloWorld.java
*
*/

public class HelloWorld {
public static void main(String[] args){
System.out.println("Hello World!");
}
}

--___EMC_XOP_BOUNDARY_START___1245537694___EMC_XOP_BOUNDARY_END___
Content-Type: application/base64
Content-Transfer-Encoding: binary
Content-ID: com.rhc.file.binary

yv66vgAAADIAHQoABgAPCQAQABEIABIKABMAFAcAFQcAFgEABjxpbml0PgEAAygpVgEABENvZGUB
AA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEAClNvdXJj
ZUZpbGUBAA9IZWxsb1dvcmxkLmphdmEMAAcACAcAFwwAGAAZAQAMSGVsbG8gV29ybGQhBwAaDAAb
ABwBAApIZWxsb1dvcmxkAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANv
dXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRs
bgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAgABAAcACAABAAkAAAAdAAEAAQAA
AAUqtwABsQAAAAEACgAAAAYAAQAAAAYACQALAAwAAQAJAAAAJQACAAEAAAAJsgACEgO2AASxAAAA
AQAKAAAACgACAAAACAAIAAkAAQANAAAAAgAO
--___EMC_XOP_BOUNDARY_START___1245537694___EMC_XOP_BOUNDARY_END___--
As you can see this export package is fairly large even though I am only storing two small XStreams in the XSet which I created.

The XSet manifest is everything between the <xsets> and </xsets> tags.  It appears to be a valid and well formed XML document although I have not formally checked it.

The first section, i.e. the <policies> section, lists the XSystem policies that are in effect for the included XSet(s).  Here the policy is set to the default policy for the Xsystem from which I exported the Xset.

It is followed by a section whose top element is <xset> which details the properties and XStream(s) for a single XSet.  Each XSet property element contains the following attributes: type, readonly, binding and length.  Each XStream is mapped to an <xstream> element which contains a child element <xop:include> which contains the content-id to the corresponding MIME part containing the actual contents of the XStream.

As you can see the actual contents of an XStream is included as a MIME attachment after the XSet manifest.  As an aside, note that there is no requirement for an XStream to verify that its contents actually match the declared MIME type. 

The first MIME attachment shown above (AKA the root MIME) lists the offsets of each of the two XStreams.  If an XSet does not contain an XStream, then there are no MIME attachments.  Other than the root MIME attachment the order of the MIME attachments is not significant.  The MIME attachments conform to the requirements of the MIME Multipart/Related Content-type specification (RFC 2387).

Here is the export package generated when StoreHelloWolld was targeted at the SNIA XAM reference VIM (Vendor Interface Module).
Content-Type: Multipart/Related;boundary="--SNIA_REFERENCE_VIM_MIME_BOUNDARY_2009-06-22T21:13:44.330-05:00.XZY_+";type="application/xop+xml";start="<canonicalxset.xml@snia.org>";start-info="text/xml"
Content-Description:Export of XSet: AAA6AwAePKwxMjQ1NzIzMjI0Mjc4ATTLJDRoe2iJ

----SNIA_REFERENCE_VIM_MIME_BOUNDARY_2009-06-22T21:13:44.330-05:00.XZY_+

Content-Type:application/xop+xml; charset="UTF-8"; type="text/xml"
Content-Transfer-Encoding:8bit
Content-ID:<canonicalxset.xml@snia.org>

<?xml version="1.0" encoding="UTF-8"?>
<xsets xsi:schemaLocation="http://www.snia.org/2007/xam/export/XAMCanonicalXSetDefinition.xsd" xmlns="http://www.snia.org/2007/xam/export" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xop="http://www.w3.org/2004/08/xop/include">
<version>1.0.0</version>
<xset>
<properties>
<property name=".xset.hold" type="application/vnd.snia.xam.boolean" binding="false" readOnly="true" length="1">
<boolean>false</boolean>
</property>
<property name=".xset.management.policy" type="application/vnd.snia.xam.string" binding="true" readOnly="true" length="36">
<string>.org.snia.refvim.default.mgmt.policy</string>
</property>
<property name=".xset.retention.base.enabled" type="application/vnd.snia.xam.boolean" binding="true" readOnly="true" length="1">
<boolean>true</boolean>
</property>
<property name=".xset.retention.base.starttime" type="application/vnd.snia.xam.datetime" binding="true" readOnly="true" length="29">
<string>2009-06-22T21:13:44.276-05:00</string>
</property>
<property name=".xset.retention.list.base" type="application/vnd.snia.xam.string" binding="true" readOnly="true" length="4">
<string>base</string>
</property>
<property name=".xset.retention.list.event" type="application/vnd.snia.xam.string" binding="true" readOnly="true" length="5">
<string>event</string>
</property>
<property name=".xset.time.access" type="application/vnd.snia.xam.datetime" binding="false" readOnly="true" length="29">
<string>2009-06-22T21:13:44.276-05:00</string>
</property>
<property name=".xset.time.commit" type="application/vnd.snia.xam.datetime" binding="false" readOnly="true" length="29">
<string>2009-06-22T21:13:44.276-05:00</string>
</property>
<property name=".xset.time.creation" type="application/vnd.snia.xam.datetime" binding="true" readOnly="true" length="29">
<string>2009-06-22T21:13:44.225-05:00</string>
</property>
<property name=".xset.time.xuid" type="application/vnd.snia.xam.datetime" binding="false" readOnly="true" length="29">
<string>2009-06-22T21:13:44.276-05:00</string>
</property>
<property name=".xset.xuid" type="application/vnd.snia.xam.xuid" binding="true" readOnly="true" length="30">
<xuid>AAA6AwAePKwxMjQ1NzIzMjI0Mjc4ATTLJDRoe2iJ</xuid>
</property>
<property name="app.filename" type="application/vnd.snia.xam.string" binding="true" readOnly="false" length="11">
<string>HelloWorld.</string>
</property>
</properties>
<xstreams>
<xstream name="com.rhc.file.binary" type="application/base64" binding="true" readOnly="false" length="575">
<org.snia.refvim.xstream_data>XStream_8508062132085140723.data</org.snia.refvim.xstream_data>
<xop:Include href="cid:com.rhc.file.binary@snia.org" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
</xstream>
<xstream name="com.rhc.file.source" type="application/octet-stream" binding="true" readOnly="false" length="156">
<org.snia.refvim.xstream_data>XStream_3869980320333904649.data</org.snia.refvim.xstream_data>
<xop:Include href="cid:com.rhc.file.source@snia.org" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
</xstream>
</xstreams>
</xset>
</xsets>
----SNIA_REFERENCE_VIM_MIME_BOUNDARY_2009-06-22T21:13:44.330-05:00.XZY_+

Content-Type:text/text
Content-Transfer-Encoding:text
Content-ID:<TOC>
Offset of AAA6AwAePKwxMjQ1NzIzMjI0Mjc4ATTLJDRoe2iJ:com.rhc.file.binary@snia.org:3948
Offset of AAA6AwAePKwxMjQ1NzIzMjI0Mjc4ATTLJDRoe2iJ:com.rhc.file.source@snia.org:4706

----SNIA_REFERENCE_VIM_MIME_BOUNDARY_2009-06-22T21:13:44.330-05:00.XZY_+

Content-Type:application/base64
Content-Transfer-Encoding:binary
Content-ID:<com.rhc.file.binary@snia.org>

yv66vgAAADIAHQoABgAPCQAQABEIABIKABMAFAcAFQcAFgEABjxpbml0PgEAAygpVgEABENvZGUB
AA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEAClNvdXJj
ZUZpbGUBAA9IZWxsb1dvcmxkLmphdmEMAAcACAcAFwwAGAAZAQAMSGVsbG8gV29ybGQhBwAaDAAb
ABwBAApIZWxsb1dvcmxkAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANv
dXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRs
bgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAgABAAcACAABAAkAAAAdAAEAAQAA
AAUqtwABsQAAAAEACgAAAAYAAQAAAAYACQALAAwAAQAJAAAAJQACAAEAAAAJsgACEgO2AASxAAAA
AQAKAAAACgACAAAACAAIAAkAAQANAAAAAgAO

----SNIA_REFERENCE_VIM_MIME_BOUNDARY_2009-06-22T21:13:44.330-05:00.XZY_+

Content-Type:application/octet-stream
Content-Transfer-Encoding:binary
Content-ID:<com.rhc.file.source@snia.org>

/*
* HelloWorld.java
*
*/

public class HelloWorld {
public static void main(String[] args){
System.out.println("Hello World!");
}
}

----SNIA_REFERENCE_VIM_MIME_BOUNDARY_2009-06-22T21:13:44.330-05:00.XZY_+--
As you can see this export package is considerably more compact then the previous export package generated when I used the EMC Centera XAM VIM.

Well, that is about all for now.  Hopefully this post was useful to you if you are just coming up to speed on XAM and wondering how to import and export XSets.

BTW, Happy Father's Day to all dads who may be reading this post.