You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oodt.apache.org by ma...@apache.org on 2011/03/09 05:02:12 UTC

svn commit: r1079660 [8/17] - in /oodt/site/components/maven: apidocs/org/apache/oodt/cas/crawl/action/ apidocs/org/apache/oodt/cas/crawl/action/class-use/ apidocs/org/apache/oodt/cas/filemgr/metadata/ apidocs/org/apache/oodt/cas/filemgr/metadata/class...

Added: oodt/site/components/maven/profile/adv/index.html
URL: http://svn.apache.org/viewvc/oodt/site/components/maven/profile/adv/index.html?rev=1079660&view=auto
==============================================================================
--- oodt/site/components/maven/profile/adv/index.html (added)
+++ oodt/site/components/maven/profile/adv/index.html Wed Mar  9 04:02:09 2011
@@ -0,0 +1,1002 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>Apache™ OODT</title><link rel="stylesheet" href="http://oodt.apache.org/oodt.css" type="text/css" /><link rel="stylesheet" href="../css/print.css" type="text/css" media="print" /><meta name="author" content="Sean Kelly" /></head><body><div id="outerWrapper"><div id="innerWrapper"><div class="visualHeaderWrapper"><div class="header"><div class="apacheOODTLogo"><a href="http://oodt.apache.org/"><img src="http://oodt.apache.org/images/apacheOODT.png" alt="Apache™ OODT" /></a></div><div class="fright"><ul class="globalnav"><!-- One of these li's must be given the currentNavTab class --><li><a id="tab-contact" href="http://oodt.apache.org/contact">Contact</a></li><li><a id="tab-development" href="http://oodt.apache.org/development">Development</a></li><li><a id="tab-downloads" href="http://oodt.apache.org/downloads">Downloa
 ds</a></li><li><a id="tab-components" href="http://oodt.apache.org/components" class="currentNavTab">Components</a></li><li><a id="tab-about" href="http://oodt.apache.org/about">About</a></li><li><a id="tab-home" href="http://oodt.apache.org/">Home</a></li></ul></div><div class="visualClear"></div></div></div><div class="subProductWrapper"><div class="subProductHeader"><h1 id="productNameSlot"><span id="bannerLeft">&#13;
+    &#13;
+            Profile Service&#13;
+    &#13;
+            </span></h1><h2 id="productSubheadSlot"><h2><a name="Developing_a_Real_Profile_Handler" id="Developing_a_Real_Profile_Handler"></a>Developing a Real Profile Handler</h2><h2><a name="The_Music_Database" id="The_Music_Database"></a>The Music Database</h2><h2><a name="Developing_the_Handler" id="Developing_the_Handler"></a>Developing the Handler</h2><h2><a name="Compiling_the_Handler" id="Compiling_the_Handler"></a>Compiling the Handler</h2><h2><a name="Querying_the_Profile_Server" id="Querying_the_Profile_Server"></a>Querying the Profile Server</h2><h2><a name="Conclusion" id="Conclusion"></a>Conclusion</h2></h2></div></div><div class="visualContentWrapper"><div class="contentGradient"></div><div id="content"><div class="diptych triptychSpace"><div class="visualPadding" id="leftPlate"><p>In the <a href="../handler/">basic profile handler
+	  tutorial</a>, we developed a profile handler that answered
+	every query with a list of exactly zero profile objects.  It
+	also responded to requests to retrieve a profile by a
+	specific ID with <code>null</code>, meaning "not found by
+	this handler."  Useful, huh?  Not really.
+      </p><p>But it did get our profile server ready for <em>this</em>
+	tutorial, where we'll write a <em>real</em> profile handler
+	that analyzes incoming queries, consults a local "database",
+	constructs a set of matching profile results, and responds to
+	requests to profiles by ID.  Get some fresh coffee, because
+	this is going to be a tough one.
+      </p><p>And yes, you'll need to have gone through <em>all</em> of the
+	following before proceeding:
+      </p><ul><li><a href="../info/">Information Captured in a Profile</a></li>
+<li><a href="../querying/">Querying Profiles</a></li>
+<li><a href="../rep">Profile Representation</a></li>
+<li><a href="../handler">Developing a Profile Handler</a></li>
+</ul><div class="section"><h2><a name="The_Music_Database" id="The_Music_Database"></a>The Music Database</h2>
+<p>Let's say you've got an OODT <a href="http://oodt.apache.org/grid-product/">product
+	  server</a> already running that serves up your favorite
+	  music files.  All you have to do is pass in the URI to a
+	  track and it spits back the MP3 data which can run into your
+	  favorite media player.  You've set it up so your URIs are
+	  all unique for each track, and you just have to pass in an
+	  unparsed XMLQuery like <code>urn:sk:tr5B7E.mp3</code> and
+	  you get the matching data.
+      </p>
+<p>But since you're not in the habit of memorizing hexadecimal
+	numbers inside of URIs, let's write a profile server who's job
+	it is to take queries for specific artists, genres, albums,
+	ratings, track titles, and so forth, and spit out the matching
+	profiles.  The profiles have a places for a URI (in the
+	<code>Identifier</code> field) that you can then pass to the
+	hypothetical product server to get the track data.  (In fact,
+	this is a common OODT pattern: profile query to do resource
+	location, product query to do resource retrieval.)  While
+	you're listening to the track, you can read all sorts of other
+	juicy metadata about it by examining the returned profile.
+      </p>
+<div class="section"><h3><a name="The_Metadata" id="The_Metadata"></a>The Metadata</h3>
+<p>For this demonstration, we'll just focus on three kinds of
+	  metadata instead of going all-out, <a href="http://www.apple.com/itunes/" class="externalLink">iTunes</a> style:
+	</p>
+<ul><li>Artist name</li>
+<li>Album name</li>
+<li>Track name</li>
+</ul><p>Each profile will describe a single track.  The resource
+	  attributes will have:
+	</p>
+<ul><li>The URI of the track as the <code>Identifier</code>.</li>
+<li>The name of the track as the <code>Title</code>.</li>
+<li>The name of the artist as the <code>Creator</code>.</li>
+</ul><p>In addition, we'll put in two profile elements:</p>
+<ul><li>The name of the album as an
+	    <code>EnumeratedProfileElement</code>.
+	  </li>
+<li>The name of the artist as an
+	    <code>EnumeratedProfileElement</code>.  Yes, this is
+	    redundant with the artist named as the <code>Creator</code>
+	    in the resource attributes; but one profile element by
+	    itself would get too lonely!</li>
+</ul></div>
+<div class="section"><h3><a name="Query_Style" id="Query_Style"></a>Query Style</h3>
+<p>Both product handlers and profile handlers get to choose
+	  whether they want unparsed query expressions in their
+	  XMLQuery objects or if they want parsed query expressions.
+	  Parsed query expressions generate the "where" boolean stack.
+	  While the product server that this profile server is meant
+	  to work with wants unparsed ones, we'll use parsed
+	  expressions for this profile handler.  Why?  Well, having a
+	  well-defined query language and a way to operate on it will
+	  save a little trouble from us having to generate a parser.
+	</p>
+<p>The queries will use element names <code>artist</code>,
+	  <code>album</code>, and <code>track</code> only, to match
+	  what we'll save in our music database.  Here are a couple example queries:
+	</p>
+<div class="source"><pre>artist = Beatles AND album = Revolver
+track = 'Blue Suede Shoes'</pre>
+</div>
+</div>
+</div><div class="section"><h2><a name="Developing_the_Handler" id="Developing_the_Handler"></a>Developing the Handler</h2>
+<p>We'll develop the handler in parts, so we can discuss each
+	section, and then show the entire source file.
+      </p>
+<div class="section"><h3><a name="Making_the_Database" id="Making_the_Database"></a>Making the "Database"</h3>
+<p>Our music database will be nothing more than Java objects
+	  kept in memory.  We'll create separate objects of three
+	  classes:
+	</p>
+<ul><li><code>Artist</code>.  <code>Artist</code> objects represent
+	      people or groups who create music.  <code>Artist</code>s
+	      will have zero or more <code>Track</code>s.
+	  </li>
+<li><code>Album</code>.  <code>Album</code> objects are
+	    collections of <code>Track</code>s.
+	  </li>
+<li><code>Track</code>.  <code>Track</code> objects appear
+	    on one <code>Album</code> and are made by one
+	    <code>Artist</code>.  They have the URN necessary to pass
+	    to the hypothetical music product server in order to
+	    actually play music.
+	  </li>
+</ul><p>A better music model would probably separate out artists
+	  and composers, account for remixes, compilation albums,
+	  re-issues, multiple renditions, and so forth, but this is
+	  government work, and it'll do.
+	</p>
+<p>Here's class <code>Artist</code>:</p>
+<div class="source"><pre>class Artist {
+  public Artist(String name) {
+    this.name = name;
+    tracks = new ArrayList();
+  }
+  public String getName() {
+    return name;
+  }
+  public List getTracks() {
+    return tracks;
+  }
+  public int hashCode() {
+    return name.hashCode();
+  }
+  public boolean equals(Object obj) {
+    if (obj == this) return true;
+    if (!(obj instanceof Artist)) return false;
+    Artist rhs = (Artist) obj;
+    return name.equals(rhs.name);
+  }
+  private String name;
+  private List tracks;
+}</pre>
+</div>
+<p>As you can see, <code>Artist</code>s have a name and a
+	  <code>List</code> of <code>Track</code>s they've made.  Now,
+	  here's class <code>Album</code>:
+	</p>
+<div class="source"><pre>class Album {
+  public Album(String name) {
+    this.name = name;
+    tracks = new ArrayList();
+  }
+  public String getName() {
+    return name;
+  }
+  public List getTracks() {
+    return tracks;
+  }
+  public int hashCode() {
+    return name.hashCode();
+  }
+  public boolean equals(Object obj) {
+    if (obj == this) return true;
+    if (!(obj instanceof Album)) return false;
+    Album rhs = (Album) obj;
+    return name.equals(rhs.name);
+  }
+  private String name;
+  private List tracks;
+}</pre>
+</div>
+<p>As with <code>Artist</code>s, <code>Album</code>s (or
+	  should that be <em>Alba</em>?) have names and collections of
+	  <code>Track</code>s.  Finally, here's class <code>Track</code>:
+	</p>
+<div class="source"><pre>class Track {
+  public Track(String name, URI id, Artist artist,
+    Album album) {
+    this.name = name;
+    this.id = id;
+    this.artist = artist;
+    this.album = album;
+    artist.getTracks().add(this);
+    album.getTracks().add(this);
+  }
+  public String getName() { return name; }
+  public URI getID() { return id; }
+  public Artist getArtist() { return artist; }
+  public Album getAlbum() { return album; }
+  public int hashCode() {
+    return name.hashCode() ^ id.hashCode();
+  }
+  public boolean equals(Object obj) {
+    if (obj == this) return true;
+    if (!(obj instanceof Track)) return false;
+    Track rhs = (Track) obj;
+    return id.equals(rhs.id);
+  }
+  private String name;
+  private URI id;
+  private Artist artist;
+  private Album album;
+}</pre>
+</div>
+<p>As you can see from the code, a track belongs to an
+	  <code>Artist</code> and to an <code>Album</code> and has a
+	  URI which you can use to get to the track's MP3 data.
+	  Finally, with these three "entity" classes in hand, we can
+	  create a music database:
+	</p>
+<div class="source"><pre>class DB {
+  public static Set ARTISTS = new HashSet();
+  public static Set ALBUMS = new HashSet();
+  public static Set TRACKS = new HashSet();
+  static {
+    Artist bach = new Artist("Bach");
+    Album brandenburg123
+      = new Album("Brandenburg Concerti 1, 2, 3");
+    Album brandenburg456
+      = new Album("Brandenburg Concerti 4, 5, 6");
+    Track brandenburg1
+      = new Track("Brandenburg Concerto #1",
+      URI.create("urn:sk:tr91BC.mp3"), bach,
+      brandenburg123);
+    Track brandenburg2
+      = new Track("Brandenburg Concerto #2",
+      URI.create("urn:sk:tr311E.mp3"), bach,
+      brandenburg123);
+    Track brandenburg3
+      = new Track("Brandenburg Concerto #3",
+      URI.create("urn:sk:trA981.mp3"), bach,
+      brandenburg123);
+    Track brandenburg4
+      = new Track("Brandenburg Concerto #4",
+      URI.create("urn:sk:tr233A.mp3"), bach,
+      brandenburg456);
+    Track brandenburg5
+      = new Track("Brandenburg Concerto #5",
+      URI.create("urn:sk:trA6E5.mp3"), bach,
+      brandenburg456);
+
+     Track brandenburg6
+      = new Track("Brandenburg Concerto #6",
+      URI.create("urn:sk:tr01E9.mp3"), bach,
+      brandenburg456);
+
+    Artist delerium = new Artist("Delerium");
+    Album semantic = new Album("Semantic Spaces");
+    Album poem = new Album("Poem");
+    Track flowers
+      = new Track("Flowers Become Screens",
+      URI.create("urn:sk:tr3A5E.mp3"), delerium,
+      semantic);
+    Track metaphor = new Track("Metaphor",
+      URI.create("urn:sk:tr0E13.mp3"), delerium,
+      semantic);
+    Track innocente = new Track("Innocente",
+      URI.create("urn:sk:tr004A.mp3"), delerium,
+      poem);
+    Track aria = new Track("Aria",
+      URI.create("urn:sk:tr004A.mp3"), delerium,
+      poem);
+
+    ARTISTS.add(bach);
+    ARTISTS.add(delerium);
+    ALBUMS.add(brandenburg123);
+    ALBUMS.add(brandenburg456);
+    ALBUMS.add(semantic);
+    ALBUMS.add(poem);
+    TRACKS.add(brandenburg1);
+    TRACKS.add(brandenburg2);
+    TRACKS.add(brandenburg3);
+    TRACKS.add(brandenburg4);
+    TRACKS.add(brandenburg5);
+    TRACKS.add(brandenburg6);
+    TRACKS.add(flowers);
+    TRACKS.add(metaphor);
+    TRACKS.add(innocente);
+    TRACKS.add(aria);
+  }
+}</pre>
+</div>
+<p>(Please don't judge this limited collection as the breadth
+	  of my listening tastes.  It's actually much narrower now!)
+	  In this small database, we've got two artists, Bach and
+	  Delerium, with four albums: <i>Brandenburg Concerti 1, 2,
+	  3</i> and <i>4, 5, 6</i>; and <i>Semantic Spaces</i> and
+	  <i>Poem</i>.  And we've got 10 tracks: 3 belonging to one
+	  album, 3 belonging to another, 2 belonging to yet another,
+	  and the last 2 belonging to the last album.  Six are by
+	  Bach, and four by Delerium.  Each track has
+	</p>
+</div>
+<div class="section"><h3><a name="Querying_our_Database" id="Querying_our_Database"></a>Querying our Database</h3>
+<p>Recall that the <a href="http://oodt.apache.org/edm-query/">XMLQuery</a>'s query
+	  language uses triples of the form (element, relation,
+	  literal) like <code>album != Poem</code>.  The relations
+	  include =, !=, &lt;, &gt;, &lt;=, &gt;=, LIKE, and NOTLIKE.
+	  The triples are linked with AND, OR, and NOT.  For this
+	  tutorial, we'll do the = and != cases.  The rest you can
+	  fill in for your own edification.  Our approach will be to
+	  examine the postfix "where" boolean stack and convert it
+	  into an infix boolean expression tree.  We'll ask the tree
+	  to evaluate itself into a matching set of
+	  <code>Track</code>s.  Then all we have to do is descibe the
+	  matching <code>Track</code>s as <code>Profile</code>
+	  objects.
+	</p>
+<p>Let's start by defining a node in our expression tree:</p>
+<div class="source"><pre>interface Expr {
+  Set evaluate();
+}</pre>
+</div>
+<p>The <code>evaluate</code> method means "evaluate into a
+	  <code>Set</code> of matching <code>Track</code> objects."
+	  With this interface, we can then define classes that make up
+	  different flavors of tree nodes.  One of the easier ones is
+	  a constant tree node that either matches <em>every</em>
+	  track available (constant true) or <em>none</em> of them
+	  (constant false):
+	</p>
+<div class="source"><pre>class Constant implements Expr {
+  public Constant(boolean value) {
+    this.value = value;
+  }
+  public Set evaluate() {
+    return value? DB.TRACKS
+      : Collections.EMPTY_SET;
+  }
+  private boolean value;
+}</pre>
+</div>
+<p>Next, let's do negation.  This takes the set complement of
+	  an existing tree node:
+	</p>
+<div class="source"><pre>class Not implements Expr {
+  public Not(Expr expr) {
+    this.expr = expr;
+  }
+  public Set evaluate() {
+    Set matches = expr.evaluate();
+    Set inverse = new HashSet();
+    for (Iterator i = DB.TRACKS.iterator();
+      i.hasNext();) {
+      Track t = (Track) i.next();
+      if (!matches.contains(t))
+        inverse.add(t);
+    }
+    return inverse;
+  }
+  private Expr expr;
+}</pre>
+</div>
+<p>As you can see, this node is constructed with another tree
+	  node expression.  To evaluate this node, we evaluate the
+	  expression passed in.  Then we take its inverse by iterating
+	  through each track in the database and adding it to the
+	  matching set if it <em>doesn't</em> occur in the
+	  expression's matching set.
+	</p>
+<p>The union tree node takes two expressions and adds the two
+	  sets of matching tracks together:
+	</p>
+<div class="source"><pre>class Or implements Expr {
+  public Or(Expr lhs, Expr rhs) {
+    this.lhs = lhs;
+    this.rhs = rhs;
+  }
+  public Set evaluate() {
+    Set left = lhs.evaluate();
+    Set right = rhs.evaluate();
+    left.addAll(right);
+    return left;
+  }
+  private Expr lhs;
+  private Expr rhs;
+}</pre>
+</div>
+<p>The intersection tree node evaluates to <code>Track</code>s
+	  that occur only in both expressions' tracks:
+	</p>
+<div class="source"><pre>class And implements Expr {
+  public And(Expr lhs, Expr rhs) {
+    this.lhs = lhs;
+    this.rhs = rhs;
+  }
+  public Set evaluate() {
+    Set left = lhs.evaluate();
+    Set right = rhs.evaluate();
+    left.retainAll(right);
+    return left;
+  }
+  private Expr lhs;
+  private Expr rhs;
+}</pre>
+</div>
+<p>With these nodes, we can cover the logical operators AND,
+	  OR, and NOT that appear in a postfix "where" stack, as well
+	  as an empty "where" stack, which, by convention, is meant to
+	  be a constant "true", matching all available resources.  Now
+	  we just have to handle triples (element, relation, literal).
+	  First up, comparisons against <code>Artist</code>s:
+	</p>
+<div class="source"><pre>class ArtistExpr implements Expr {
+  public ArtistExpr(String op, String value) {
+    this.op = op;
+    this.value = value;
+  }
+  public Set evaluate() {
+    Set tracks = new HashSet();
+    if ("EQ".equals(op)) {
+      for (Iterator i = DB.ARTISTS.iterator();
+        i.hasNext();) {
+        Artist a = (Artist) i.next();
+        if (a.getName().equals(value))
+          tracks.addAll(a.getTracks());
+      }
+    } else if ("NE".equals(op)) {
+      for (Iterator i = DB.ARTISTS.iterator();
+        i.hasNext();) {
+        Artist a = (Artist) i.next();
+        if (!a.getName().equals(value))
+          tracks.addAll(a.getTracks());
+      }
+    } else throw new
+      UnsupportedOperationException("NYI");
+    return tracks;
+  }
+  private String op;
+  private String value;
+}</pre>
+</div>
+<p>For an expression like <code>artist = Bach</code> or
+	  <code>artist != Delerium</code> we use a expression node
+	  object of the above class.  When it's <code>EQ</code>, we
+	  iterate through all the artists in the database and, when
+	  the artist's name matches, add all of that artist's tracks
+	  to the set of matches.  When it's <code>NE</code>, we
+	  instead add all of the artists' tracks whose name
+	  <em>doesn't</em> match.  (The other relational operators,
+	  <code>LT</code>, <code>GT</code>, <code>LE</code>,
+	  <code>GE</code>, <code>LIKE</code>, and <code>NOTLIKE</code>
+	  currently throw an exception.  You're welcome to try to
+	  implement those.)
+	</p>
+<p>The <code>AlbumExpr</code> expression node is quite similar:</p>
+<div class="source"><pre>class AlbumExpr implements Expr {
+  public AlbumExpr(String op, String value) {
+    this.op = op;
+    this.value = value;
+  }
+  public Set evaluate() {
+    Set tracks = new HashSet();
+    if ("EQ".equals(op)) {
+      for (Iterator i = DB.ALBUMS.iterator();
+        i.hasNext();) {
+        Album a = (Album) i.next();
+        if (a.getName().equals(value))
+          tracks.addAll(a.getTracks());
+      }
+    } else if ("NE".equals(op)) {
+      for (Iterator i = DB.ALBUMS.iterator();
+        i.hasNext();) {
+        Album a = (Album) i.next();
+        if (!a.getName().equals(value))
+          tracks.addAll(a.getTracks());
+      }
+    } else throw new
+      UnsupportedOperationException("NYI");
+    return tracks;
+  }
+  private String op;
+  private String value;
+}</pre>
+</div>
+<p>(Another exercise for the reader: refactor out common code
+	  between these two classes.)  Finally, the
+	  <code>TrackExpr</code> node is for expressions like
+	  <code>track = Poem</code>:
+	</p>
+<div class="source"><pre>class TrackExpr implements Expr {
+  public TrackExpr(String op, String value) {
+    this.op = op;
+    this.value = value;
+  }
+  public Set evaluate() {
+    Set tracks = new HashSet();
+    if ("EQ".equals(op)) {
+      for (Iterator i = DB.TRACKS.iterator();
+        i.hasNext();) {
+        Track t = (Track) i.next();
+        if (t.getName().equals(value))
+          tracks.add(t);
+      }
+    } else if ("NE".equals(op)) {
+      for (Iterator i = DB.TRACKS.iterator();
+        i.hasNext();) {
+        Track t = (Track) i.next();
+        if (!t.getName().equals(value))
+          tracks.add(t);
+      }
+    } else throw new
+      UnsupportedOperationException("NYI");
+    return tracks;
+  }
+  private String op;
+  private String value;
+}</pre>
+</div>
+<p>For <code>EQ</code>, we just iterate through every track in
+	  the database and add it to the set of matching tracks if the
+	  names match the name passed into the user's query.  For
+	  <code>NE</code>, we add them if their names <em>don't</em> match.
+	</p>
+<p>That completes all the code for the expression tree.  Now
+	  we can start working on the class that implements the
+	  <code>ProfileHandler</code> interface,
+	  <code>MusicHandler</code>.  Here, we'll build that
+	  expression tree with the incoming <code>XMLQuery</code>,
+	  which provides its "where" element stack as a postfix
+	  boolean expression.  Here's the approach:
+	</p>
+<ol type="1"><li>Make a new, empty stack.</li>
+<li>For each element in the "where" stack:
+	    <ol type="1"><li>If it's an element name (<code>artist</code>,
+		<code>album</code>, <code>track</code>), push the name
+		onto the stack.
+	      </li>
+<li>If it's a literal value (<code>Bach</code>,
+		<code>Poem</code>, etc.), push it onto the stack.
+	      </li>
+<li>If it's a relational operator (<code>EQ</code>, <code>NE</code>, etc.):
+		<ol type="1"><li>Pop two values off.</li>
+<li>Push an <code>ArtistExpr</code>, <code>AlbumExpr</code>, or <code>TrackExpr</code>.</li>
+</ol></li>
+<li>It it's a logical operator:
+		<ul><li>For <code>AND</code>, pop two values off and push an <code>And</code> node.</li>
+<li>For <code>OR</code>, pop two values off and push an <code>Or</code> node.</li>
+<li>For <code>NOT</code>, pop one value off and push a <code>Not</code> node.</li>
+</ul></li>
+</ol></li>
+</ol><p>In the end, there will be one element left on the stack, an
+	  <code>Expr</code> node representing the root of the
+	  expression tree.  Here's the method of
+	  <code>MusicHandler</code> that implements the algorithm:
+	</p>
+<div class="source"><pre>private static Expr transform(XMLQuery q) {
+  Stack stack = new Stack();
+  for (Iterator i = q.getWhereElementSet()
+    .iterator(); i.hasNext();) {
+    QueryElement e = (QueryElement) i.next();
+    String keyword = e.getValue();
+    String type = e.getRole();
+    if ("elemName".equals(type))
+	stack.push(keyword);
+    else if ("LITERAL".equals(type))
+	stack.push(keyword);
+    else if ("RELOP".equals(type))
+	addRelational(keyword, (String)stack.pop(),
+	  (String)stack.pop(), stack);
+    else if ("LOGOP".equals(type))
+	addLogical(keyword, stack);
+    else throw new
+	IllegalArgumentException("Unknown query "
+	  + type + " type");
+  }
+  if (stack.size() == 0)
+    return new Constant(true);
+  else if (stack.size() &gt; 1)
+    throw new IllegalArgumentException("Unbalanced"
+      + " query");
+  else return (Expr) stack.pop();
+}</pre>
+</div>
+<p>For relational and logical operators, this method defers to
+	  two other utility methods, which we'll see shortly.  After
+	  iterating through the entire "where" set, we check to see if
+	  there's an empty stack.  That's the case where the user
+	  passes in an empty expression, which by convention we'll
+	  take to mean they want everything.  Otherwise, there should
+	  be just one <code>Expr</code> node on the stack, the root of
+	  the expression tree.
+	</p>
+<p>To handle adding a <code>RELOP</code>, we pop two values
+	  off, the element name (<code>artist</code>,
+	  <code>album</code>, or <code>track</code>), and the literal
+	  value the user wants (<code>Bach</code>, <code>Poem</code>,
+	  etc.), along with the operator and the stack:
+	</p>
+<div class="source"><pre>private static void addRelational(String op,
+  String value, String kind, Stack stack) {
+  if ("artist".equals(kind))
+    stack.push(new ArtistExpr(op, value));
+  else if ("album".equals(kind))
+    stack.push(new AlbumExpr(op, value));
+  else if ("track".equals(kind))
+    stack.push(new TrackExpr(op, value));
+  else throw new
+    IllegalArgumentException("Unknown profile"
+      + " element " + kind);
+}</pre>
+</div>
+<p>This method then replaces the popped off values with the
+	  matching <code>Expr</code> class for artists, albums, or
+	  tracks.
+	</p>
+<p>To handle adding a <code>LOGOP</code>, we pass the logical
+	  operator and the entire stack to this method:
+	</p>
+<div class="source"><pre>private static void addLogical(String op,
+  Stack stack) {
+  if ("AND".equals(op))
+    stack.push(new And((Expr)stack.pop(),
+      (Expr) stack.pop()));
+  else if ("OR".equals(op))
+    stack.push(new Or((Expr)stack.pop(),
+      (Expr) stack.pop()));
+  else if ("NOT".equals(op))
+    stack.push(new Not((Expr)stack.pop()));
+  else throw new
+    IllegalArgumentException("Illegal operator "
+      + op);
+}</pre>
+</div>
+<p>With all this code in place we can generate the expression
+	  tree.  Let's look at an example.  Suppose when constructing the
+	  <code>XMLQuery</code>, the user passed in</p>
+<div class="source"><pre>artist = Bach and not album = Poem or track != Aria</pre>
+</div>
+<p>The XMLQuery query language generates a postfix stack of
+	  <code>QueryElement</code> objects in the "where" list:
+	</p>
+<img src="../images/stack.png" alt="Stack" /><p>And we then create this tree:</p>
+<img src="../images/tree.png" alt="Tree" /><p>Calling the root's <code>evaluate</code> method then yields
+	  a <code>java.util.Set</code> of <code>Track</code> objects
+	  that match that expression.
+	</p>
+<p>OK, we've got a set of <code>Track</code>s.  But what we
+	  want are a set of <em><code>Profile</code>s</em>.  The next
+	  step is to describe those tracks using the profile metadata
+	  model.
+	</p>
+</div>
+<div class="section"><h3><a name="Describing_Tracks" id="Describing_Tracks"></a>Describing Tracks</h3>
+<p>Query handlers serve up <code>List</code>s of
+	  <code>Profile</code> objects, where <code>Profile</code>s
+	  contain metadata descriptions of resources.  For this
+	  tutorial, the resources we're describing are music tracks,
+	  represented by instances of <code>Track</code> objects.
+	  When the handler's <code>findProfiles</code> and
+	  <code>get</code> methods are called by the OODT framework to
+	  service a request, all we have to do is find the matching
+	  <code>Track</code> (or <code>Track</code>s) and create
+	  matching <code>Profile</code>s.
+	</p>
+<p>Recall that we're setting up the resource attributes of the
+	  profile so that
+	</p>
+<ul><li>The URI of the track appears in the <code>Identifier</code>.</li>
+<li>The name of the track appears in the <code>Title</code>.</li>
+<li>The name of the artist appears the <code>Creator</code>.</li>
+</ul><p>In addition, we'll put in two profile elements:</p>
+<ul><li>The name of the album as an
+	    <code>EnumeratedProfileElement</code>.
+	  </li>
+<li>The name of the artist redundantly as an
+	    <code>EnumeratedProfileElement</code>.
+	  </li>
+</ul><p>Now, let's create a utility method <code>describe</code>
+	  which takes a <code>java.util.Set</code> of matching
+	  <code>Track</code>s and yields a <code>java.util.List</code>
+	  of corresponding <code>Profile</code>s:
+	</p>
+<div class="source"><pre>private static List describe(Set tracks) {
+  List profiles = new ArrayList();
+  for (Iterator i = tracks.iterator();
+    i.hasNext();) {
+    Track t = (Track) i.next();
+    String id = t.getID().toString();
+    String trackName = t.getName();
+    String albumName = t.getAlbum().getName();
+    String artistName = t.getArtist().getName();
+    Profile p = createProfile(id, trackName,
+      albumName, artistName);
+    profiles.add(p);
+  }
+  return profiles;
+}</pre>
+</div>
+<p>We build a list of <code>Profile</code>s by calling another
+	  method, <code>createProfile</code>.  It takes the track's
+	  URI, its name, the name of the album on which it appears,
+	  and the name of the artist who created it, and yields a
+	  <code>Profile</code>:
+	</p>
+<div class="source"><pre>private static Profile createProfile(String id,
+  String trackName, String albumName,
+  String artistName) {
+  Profile p = new Profile();
+  ProfileAttributes pa=new ProfileAttributes(id,
+    "1.0", "profile", "active", "unclassified",
+    /*parent*/null, /*children*/EL,
+    "1.3.6.1.4.1.7655", /*revNotes*/EL);
+  p.setProfileAttributes(pa);
+  ResourceAttributes ra=new ResourceAttributes(p,
+    id, trackName,
+    Collections.singletonList("audio/mpeg"),
+    /*desc*/null,
+    Collections.singletonList(artistName),
+    /*subjects*/EL, /*pubs*/EL, /*contrib*/EL,
+    /*dates*/EL, /*types*/EL, /*sources*/EL,
+    /*langs*/EL, /*relations*/EL, /*covs*/EL,
+    /*rights*/EL,
+    Collections.singletonList("SK.Music"),
+    "granule", "system.productServer",
+    Collections.singletonList("urn:eda:rmi:"
+      + "MyProductServer"));
+    p.setResourceAttributes(ra);
+    EnumeratedProfileElement artistElem =
+    new EnumeratedProfileElement(p, "artist",
+    "artist", "Name of the artist of a work",
+    "string", "name", /*syns*/EL, /*ob*/true,
+    /*maxOccur*/1, /*comment*/null,
+    Collections.singletonList(artistName));
+  p.getProfileElements().put("artist",
+    artistElem);
+  EnumeratedProfileElement albumElem =
+    new EnumeratedProfileElement(p, "album",
+    "album", "Name of album where track occurs",
+    "string", "name", /*syns*/EL, /*ob*/true,
+    /*maxOccur*/1, /*comment*/null,
+    Collections.singletonList(albumName));
+  p.getProfileElements().put("album",
+    albumElem);
+  return p;
+}</pre>
+</div>
+<p>The profile attributes say that</p>
+<ul><li>The ID of the profile itself is the same as the track's URI.</li>
+<li>The version of the profile is 1.0.</li>
+<li>The type is "profile".</li>
+<li>It's currently active.</li>
+<li>It's not top-secret, it's "unclassified".</li>
+<li>It has no parent profile.</li>
+<li>It has no child profiles.</li>
+<li>The registration authority has OID 1.3.6.1.4.1.7655</li>
+<li>There are no revision notes.</li>
+</ul><p>The resource attributes say that</p>
+<ul><li>The Identifier is the track's URI.</li>
+<li>The Title is
+	  the track's title.</li>
+<li>The sole Format in which the
+	  track is available is <code>audio/mpeg</code>.</li>
+<li>There's no description.</li>
+<li>The sole Creator is the
+	  name of the artist.</li>
+<li>There are no subject keywords,
+	  publishers, contributors, dates, types, sources, languages,
+	  relations, coverages, nor rights.</li>
+<li>The sole resource context is "Tutorial.Music".</li>
+<li>The resource's aggregation is "granule", meaning this profile is describing a single, discrete resource.</li>
+<li>The resource's class is "system.productServer", meaning you need to contact a product server at the resource location to retrieve the resource.</li>
+<li>The resource location is <code>urn:eda:rmi:MyProductServer</code>.</li>
+</ul><p>Finally, the two profile elements tell (again) who the
+	  artist was and also on what album the track appears.
+	</p>
+<p>What's with all the <code>EL</code>s?  It's just to save on typing:</p>
+<div class="source"><pre>private static final List EL
+  = Collections.EMPTY_LIST;</pre>
+</div>
+</div>
+<div class="section"><h3><a name="Implementng_the_Interface" id="Implementng_the_Interface"></a>Implementng the Interface</h3>
+<p>The <code>ProfileHandler</code> interface stipulates two
+	  methods, one for finding profiles given an
+	  <code>XMLQuery</code> and another for retrieving a single
+	  profile given its ID.  With all of these utility methods in
+	  place, these are both easy to write.  First, the
+	  <code>findProfiles</code> method:
+	</p>
+<div class="source"><pre>public List findProfiles(XMLQuery q) {
+  Expr expr = transform(q);
+  Set matches = expr.evaluate();
+  List profiles = describe(matches);
+  return profiles;
+}</pre>
+</div>
+<p>The algorithm should be painfully obvious by now: transform
+	  the query to a tree, evaluate the tree into a set of
+	  matching tracks, and describe the tracks.
+	</p>
+<p>The <code>get</code> method takes a profile's ID and
+	  returns the matching profile, or <code>null</code> if it's
+	  not found.  Since we're using the track's ID as the
+	  profile's ID as well, we can just iterate through our
+	  tracks, find the one with the matching ID, and
+	  <code>describe</code> it:
+	</p>
+<div class="source"><pre>public Profile get(String id) {
+  URI uri = URI.create(id);
+  for (Iterator i = DB.TRACKS.iterator();
+    i.hasNext();) {
+    Track t = (Track) i.next();
+    if (t.getID().equals(uri))
+      return createProfile(t.getID().toString(),
+        t.getName(), t.getAlbum().getName(),
+        t.getArtist().getName());
+  }
+  return null;
+}</pre>
+</div>
+</div>
+<div class="section"><h3><a name="Complete_Source_Code" id="Complete_Source_Code"></a>Complete Source Code</h3>
+<p>Don't feel like cutting and pasting all of those code
+	  fragments?  No problem.  All of the source files are
+	  available <a href="../examples/src.jar">in a jar</a>.
+	</p>
+</div>
+</div><div class="section"><h2><a name="Compiling_the_Handler" id="Compiling_the_Handler"></a>Compiling the Handler</h2>
+<p>As with the <a href="../handler/"><code>NullHandler</code></a>, we'll use the
+	J2SDK command-line tools.  And if you've gone through the <a href="../handler/"><code>NullHandler</code> tutorial</a>, you've
+	got all the dependent jars in place already.  Just put the
+	<code>MusicHandler.java</code> and all the related source files
+	under <code>$PS_HOME/src</code>, compile, and build the jar:
+      </p>
+<div class="source"><pre>% <b>ls src</b>
+Album.java         Expr.java
+AlbumExpr.java     MusicHandler.java
+And.java           Not.java
+Artist.java        NullHandler.java
+ArtistExpr.java    Or.java
+Constant.java      Track.java
+DB.java            TrackExpr.java
+% <b>javac -extdirs lib -d classes src/*.java</b>
+% <b>ls classes</b>
+Album.class         Expr.class
+AlbumExpr.class     MusicHandler.class
+And.class           Not.class
+Artist.class        NullHandler.class
+ArtistExpr.class    Or.class
+Constant.class      Track.class
+DB.class            TrackExpr.class
+% <b>cd classes</b>
+% <b>jar -uf ../lib/my-handler.jar *.class</b>
+% <b>cd ..</b>
+% <b>jar -tf lib/my-handler.jar</b>
+META-INF/
+META-INF/MANIFEST.MF
+NullHandler.class
+Album.class
+AlbumExpr.class
+And.class
+Artist.class
+ArtistExpr.class
+Constant.class
+DB.class
+Expr.class
+MusicHandler.class
+Not.class
+Or.class
+Track.class
+TrackExpr.class</pre>
+</div>
+<p>We also need to update the <code>$PS_HOME/bin/ps</code>
+	script.  Currently, it's instantiating just the
+	<code>NullHandler</code>; we need it to instantiate the
+	<code>MusicHandler</code> too.  Stop any currently running
+	profile server by pressing CTRL+C (or whatever your interrupt
+	key is) in the window running the server.  Then edit the
+	script so it reads as follows:
+      </p>
+<div class="source"><pre>#!/bin/sh
+exec java -Djava.ext.dirs=$PS_HOME/lib \
+    -Dhandlers=NullHandler,MusicHandler \
+    jpl.eda.ExecServer \
+    jpl.eda.profile.rmi.ProfileServiceImpl \
+    urn:eda:rmi:MyProfileService</pre>
+</div>
+<p>Now the profile server will delegate to <em>two</em>
+	handlers: the <code>NullHandler</code> and the
+	<code>MusicHandler</code>.  With more than one handler, the
+	OODT framework calls each one in turn and collects all of the
+	matching profiles together to return to the
+	<code>ProfileClient</code>.  (Of course, the
+	<code>NullHandler</code> never actually generates any matching
+	profiles.)
+      </p>
+</div><div class="section"><h2><a name="Querying_the_Profile_Server" id="Querying_the_Profile_Server"></a>Querying the Profile Server</h2>
+<p>Start the profile server by running
+	<code>$PS_HOME/bin/ps</code> in one window (presumably the RMI
+	registry is still running in another window).  In yet another
+	window, we'll run our <code>$PS_HOME/bin/pc</code> script to
+	query the profile server:
+      </p>
+<div class="source"><pre>% <b>$PS_HOME/bin/pc 'artist = Delerium
+ AND album != Poem OR artist = Bach'</b>
+Object context ready; delegating to:
+[jpl.eda.object.jndi.RMIContext@dec8b3]
+[&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;profile&gt;&lt;profAttributes&gt;&lt;profId&gt;urn:sk:tr91BC.mp3&lt;/profId&gt;...</pre>
+</div>
+<p>Whoa!  There's a huge load of XML!  In fact what the
+	<code>ProfileClient</code> is printing is a
+	<code>java.util.List</code> of profiles in XML format, each
+	separated by a comma, and the whole list in square brackets.
+	If you search this output carefully, though, you can pick out
+	the <code>&lt;Title&gt;</code> elements and see indeed that
+	we've got six matching tracks:
+      </p>
+<ul><li>Brandenburg Concerto #1</li>
+<li>Brandenburg Concerto #2</li>
+<li>Brandenburg Concerto #3</li>
+<li>Brandenburg Concerto #4</li>
+<li>Brandenburg Concerto #5</li>
+<li>Brandenburg Concerto #6</li>
+<li>Flowers Become Screens</li>
+<li>Metaphor</li>
+</ul><p>Sure enough, this matches the XMLQuery query language
+	expression we passed in: There are tracks by Delerium but
+	<em>not</em> from the Poem album, and there are all the tracks
+	by Bach.
+      </p>
+</div><div class="section"><h2><a name="Conclusion" id="Conclusion"></a>Conclusion</h2>
+<p>In this long tutorial we developed a real profile handler
+	that answered queries by transforming them from postfix stacks
+	into expression trees and using those trees to query an
+	in-memory database made of Java objects.  We then described
+	matching data by creating <code>Profile</code>s.
+      </p>
+<p>You might be thinking that this seems like a lot of work, and
+	there might be some easier ways to go.  You could use the
+	<code>LightweightProfileHandler</code> for resources that
+	never change, but only if you don't have too many of them and
+	don't mind managing potentially large XML documents.  You
+	could choose to use unparsed XMLQuery expressions and instead
+	make the user query in the same language as your data system,
+	obviating the need for complex expression trees.
+      </p>
+<p>However, with the tools presented in this tutorial, you could
+	adapt the expression tree code to generating system-specific
+	queries, and describe those results with as much or as little
+	detail as necessary.
+      </p>
+<p>Happy profiling!</p>
+</div></div><div class="visualClear"></div></div><div class="triptychRight"><div class="columnBorder"><div class="visualPadding" id="rightPlate"><div id="navcolumn">&#13;
+           &#13;
+  &#13;
+&#13;
+  &#13;
+    &#13;
+  &#13;
+  &#13;
+    &#13;
+                   <h5>Project Documentation</h5>&#13;
+            <ul><li class="collapsed">&#13;
+                    <a href="../project-info.html">Project Information</a>&#13;
+                </li>&#13;
+              &#13;
+                &#13;
+              &#13;
+      &#13;
+            &#13;
+      &#13;
+            &#13;
+      &#13;
+            &#13;
+      &#13;
+              &#13;
+        <li class="collapsed">&#13;
+                    <a href="../project-reports.html">Project Reports</a>&#13;
+                </li>&#13;
+          </ul><h5>User's Guide</h5>&#13;
+            <ul><li class="none">&#13;
+                    <a href="../info/">Information Captured</a>&#13;
+          </li>&#13;
+              &#13;
+    <li class="none">&#13;
+                    <a href="../querying/">Querying Profiles</a>&#13;
+          </li>&#13;
+              &#13;
+    <li class="none">&#13;
+                    <a href="../rep/">Profile Repr.</a>&#13;
+          </li>&#13;
+              &#13;
+    <li class="none">&#13;
+                    <a href="../handler/">Basic Handler</a>&#13;
+          </li>&#13;
+              &#13;
+    <li class="none">&#13;
+                    <a href="../adv/">Advanced Handler</a>&#13;
+          </li>&#13;
+          </ul>&#13;
+                       &#13;
+  &#13;
+&#13;
+  &#13;
+    &#13;
+  &#13;
+  &#13;
+    &#13;
+        </div></div></div></div></div></div><div style="clear: both;"> </div></div><div id="push"></div></div><div id="footerWrapper"><div id="footerGradient"></div><div id="footer"><a id="footerLogo" href="http://oodt.apache.org/" name="footerLogo"></a><p>
+                    Apache™ OODT is © 2010 by the Apache Software Foundation (ASF). Apache and the Apache
+                    feather logo are trademarks of the ASF.
+                </p><p>
+                    Apache™ OODT OODT is a Top Level Project and endorsed by the ASF. You are full of win.
+                </p><div id="sitemap"><dl><dt><a href="http://oodt.apache.org/about">About OODT</a></dt><dd><a href="http://oodt.apache.org/about/history">History</a></dd><dd><a href="http://oodt.apache.org/about/users">Users</a></dd><dd><a href="http://oodt.apache.org/about/incubation">Incubation</a></dd></dl><dl><dt><a href="http://www.apache.org/">Apache</a></dt><dd><a href="http://incubator.apache.org/">Incubator</a></dd><dd><a href="http://www.apache.org/licenses">License</a></dd><dd><a href="http://www.apache.org/foundation/sponsorship.html">Donate</a></dd><dd><a href="http://www.apache.org/foundation/thanks.html">Thanks</a></dd><dd><a href="http://www.apache.org/security/">Security</a></dd></dl><dl><dt><a href="http://oodt.apache.org/components">Components</a></dt><dd><a href="http://oodt.apache.org/components/agility">Agility</a></dd><dd><a href="http://oodt.apache.org/components/cas">Catalog &amp; Archive</a></dd><dd><a href="http://oodt.apache.org/components/maven/
 xmlquery">Query</a></dd><dd><a href="http://oodt.apache.org/components/grid">Grid</a></dd><dd><a href="http://oodt.apache.org/components/common">Common</a></dd></dl><dl><dt><a href="http://oodt.apache.org/development">Development</a></dt><dd><a href="http://oodt.apache.org/development/roadmap">Roadmap</a></dd><dd><a href="http://oodt.apache.org/development/bugs">Report Bugs</a></dd><dd><a href="http://oodt.apache.org/development/source">Source Code</a></dd><dd><a href="http://oodt.apache.org/contact">Mailing Lists</a></dd></dl><dl><dt><a href="http://oodt.apache.org/contact">Social</a></dt><dd><a href="http://www.facebook.com/group.php?gid=322088549131">Facebook</a></dd><dd><a href="http://twitter.com/apache_oodt/">Twitter</a></dd><dd><a href="irc://irc.freenode.net/#oodt">Chat Room</a></dd></dl></div></div></div></body></html>
\ No newline at end of file

Added: oodt/site/components/maven/profile/handler/index.html
URL: http://svn.apache.org/viewvc/oodt/site/components/maven/profile/handler/index.html?rev=1079660&view=auto
==============================================================================
--- oodt/site/components/maven/profile/handler/index.html (added)
+++ oodt/site/components/maven/profile/handler/index.html Wed Mar  9 04:02:09 2011
@@ -0,0 +1,553 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>Apache™ OODT</title><link rel="stylesheet" href="http://oodt.apache.org/oodt.css" type="text/css" /><link rel="stylesheet" href="../css/print.css" type="text/css" media="print" /><meta name="author" content="Sean Kelly" /></head><body><div id="outerWrapper"><div id="innerWrapper"><div class="visualHeaderWrapper"><div class="header"><div class="apacheOODTLogo"><a href="http://oodt.apache.org/"><img src="http://oodt.apache.org/images/apacheOODT.png" alt="Apache™ OODT" /></a></div><div class="fright"><ul class="globalnav"><!-- One of these li's must be given the currentNavTab class --><li><a id="tab-contact" href="http://oodt.apache.org/contact">Contact</a></li><li><a id="tab-development" href="http://oodt.apache.org/development">Development</a></li><li><a id="tab-downloads" href="http://oodt.apache.org/downloads">Downloa
 ds</a></li><li><a id="tab-components" href="http://oodt.apache.org/components" class="currentNavTab">Components</a></li><li><a id="tab-about" href="http://oodt.apache.org/about">About</a></li><li><a id="tab-home" href="http://oodt.apache.org/">Home</a></li></ul></div><div class="visualClear"></div></div></div><div class="subProductWrapper"><div class="subProductHeader"><h1 id="productNameSlot"><span id="bannerLeft">&#13;
+    &#13;
+            Profile Service&#13;
+    &#13;
+            </span></h1><h2 id="productSubheadSlot"><h2><a name="Developing_a_Profile_Handler" id="Developing_a_Profile_Handler"></a>Developing a Profile Handler</h2><h2><a name="Introduction" id="Introduction"></a>Introduction</h2><h2><a name="Writing_the_Handler_Class" id="Writing_the_Handler_Class"></a>Writing the Handler Class</h2><h2><a name="Our_First_Profile_Handler" id="Our_First_Profile_Handler"></a>Our First Profile Handler</h2><h2><a name="Conclusion" id="Conclusion"></a>Conclusion</h2></h2></div></div><div class="visualContentWrapper"><div class="contentGradient"></div><div id="content"><div class="diptych triptychSpace"><div class="visualPadding" id="leftPlate"><p>Profiles describe resources. But where do profiles come from?
+	From profile handlers. Profile handlers are interchangeable
+	components of a profile server that accept queries for
+	profiles and return matching profiles. Profile handlers take a
+	static or a dynamic collection of information and serve an
+	equivalent set of profiles. Developing a new profile handler
+	is as simple as writing a Java class that implements a
+	specific interface or two.
+      </p><p>If you're not already familiar with the <code>XMLQuery</code>
+	class, go ahead and <a href="http://oodt.apache.org/edm-query/tutorial/">take its
+	  tutorial now</a>.  We'll wait!
+      </p><div class="section"><h2><a name="Introduction" id="Introduction"></a>Introduction</h2>
+<p>Profile handlers handle queries for profiles.  They're the
+	interchangeable part of a profile server that you can develop
+	for special needs.  Profile servers delegate all incoming
+	requests to zero or more profile handlers, as shown in the
+	following class diagram:
+      </p>
+<img src="../images/delegation.png" alt="Delegation model" /><p>Developing, testing, and deploying a new profile handler
+	involves:
+      </p>
+<ol type="1"><li>Creating a class that implements (however indirectly)
+	  the <code>jpl.eda.profile.handlers.ProfileHandler</code> interface.
+	</li>
+<li>Creating a new process that runs either the RMI or
+	    CORBA <code>ProfileServiceImpl</code> class,
+	    specifying the name of your handler class.
+	</li>
+<li>Starting the server and sending in queries.
+	</li>
+</ol><p>This document describes each of these steps in detail.</p>
+</div><div class="section"><h2><a name="Writing_the_Handler_Class" id="Writing_the_Handler_Class"></a>Writing the Handler Class</h2>
+<p>Writing the class that handles profile queries and delivers
+	profile results is easily the hardest part in developing a new
+	kind of profile server.  Profile servers' handlers can serve
+	profiles describing static resources, can synthesize profile
+	on the fly to describe resources, and can create profile
+	metadata for resources that change all the time.
+      </p>
+<p>Understanding the resources you're trying to describe with
+	profiles is the most important thing you can do before
+	beginning to write your profile handler:
+      </p>
+<ul><li>Do you always have the same set of static resources?
+	  If so, you can write a static profile document to describe
+	  them and use the
+	  <code>LightweightProfileHandler</code>, thus
+	  avoiding having to write a new handler at all.
+	</li>
+<li>Do you have resources that never change, but may add to or
+	  remove from that set?  If so, you can use the
+	  <code>OracleProfileImpl</code> handler which uses an Oracle
+	  database to store a set of profiles that you can update.
+	</li>
+<li>Do you have resources that do change, or that come
+	  from a dynamic set of data?  If so, you'll have to write a
+	  handler.
+	</li>
+</ul><div class="section"><h3><a name="Choosing_the_Handler_Interface_to_Implement" id="Choosing_the_Handler_Interface_to_Implement"></a>Choosing the Handler Interface to Implement</h3>
+<p>The OODT Framework provides two handler interfaces (one is
+	  an extension of the other):
+	</p>
+<ul><li><code>jpl.eda.profile.handlers.ProfileHandler</code>
+	      is the basic profile handler.  It defines methods for
+	    <em>handling</em> searches for profiles.
+	  </li>
+<li><code>jpl.eda.profile.handlers.ProfileManager</code> is an
+	    extension that not just <em>handles</em> profile queries
+	    but also <em>manages</em> the set of profiles maintained
+	    by the server, by providing methods for adding to,
+	    removing from, and updating the set of managed profiles.
+	  </li>
+</ul><p>For nearly all applications, the <code>ProfileHandler</code>
+	  interface is sufficient.  If you need to provide profile
+	  management capabilities, it still may be handy to start with
+	  the <code>ProfileHandler</code> interface, implement and test
+	  its methods, and <em>then</em> change to the
+	  <code>ProfileManager</code>.
+	</p>
+</div>
+<div class="section"><h3><a name="The_ProfileHandler_Interface" id="The_ProfileHandler_Interface"></a>The ProfileHandler Interface</h3>
+<p>The <code>ProfileHandler</code> interface is
+	  as follows:
+	</p>
+<div class="source"><pre>package jpl.eda.profile.handlers;
+
+import java.util.List;
+import jpl.eda.profile.Profile;
+import jpl.eda.profile.ProfileException;
+import jpl.eda.xmlquery.XMLQuery;
+
+public interface ProfileHandler {
+  List findProfiles(XMLQuery query) throws ProfileException;
+  Profile get(String profID) throws ProfileException;
+}</pre>
+</div>
+<p>The two methods are described in detail below.</p>
+<dl><dt><code>findProfiles</code></dt>
+<dd>This method accepts a query in the form of an
+	    <code>XMLQuery</code> object and returns a
+	    Java <code>List</code> of
+	    <code>Profile</code> objects that match.  If
+	    there are no matches, this method must return an empty
+	    list.  If an error occurs, it should throw the
+	      <code>ProfileException</code>.
+
+	    This is by far the most used and most important
+	      method, and really is the <i>raison
+	      d'etre</i> for profile servers.  It's
+	      what the OODT Framework uses to allow clients to ask
+	      your server for resources based on metadata .
+	    </dd>
+<dt><code>get</code></dt>
+<dd>This method accepts the ID of a profile in the form
+	    of a Java <code>String</code> and returns
+	    either a <code>Profile</code> object with that
+	    ID or null if the ID is unknown.  This method enables a
+	    client to retrieve a profile using a priori knowledge of
+	    the profile's ID (perhaps from a previous search).
+	  </dd>
+</dl></div>
+<div class="section"><h3><a name="The_ProfileManager_Interface" id="The_ProfileManager_Interface"></a>The ProfileManager Interface</h3>
+<p>The <code>ProfileManager</code> interface
+	  builds on the <code>ProfileHandler</code>, and is
+	  listed below:
+	</p>
+<div class="source"><pre>package jpl.eda.profile.handlers;
+
+import java.util.Collection;
+import java.util.Iterator;
+import jpl.eda.profile.Profile;
+import jpl.eda.profile.ProfileException;
+import jpl.eda.xmlquery.XMLQuery;
+
+public interface ProfileManager extends ProfileHandler {
+  void add(Profile profile) throws ProfileException;
+  void addAll(Collection collection) throws ProfileException;
+  void clear() throws ProfileException;
+  boolean contains(Profile profile) throws ProfileException;
+  boolean containsAll(Collection collection) throws ProfileException;
+  Collection getAll() throws ProfileException;
+  boolean isEmpty() throws ProfileException;
+  Iterator iterator() throws ProfileException;
+  boolean remove(String profID, String version) throws ProfileException;
+  boolean remove(String profID) throws ProfileException;
+  int size() throws ProfileException;
+  void replace(Profile profile) throws ProfileException;
+}</pre>
+</div>
+<p>If you choose to implement a profile manager, please see
+	  the API documentation for the
+	  <code>ProfileManager</code> class for the
+	  expectations of each method.
+	</p>
+</div>
+</div><div class="section"><h2><a name="Our_First_Profile_Handler" id="Our_First_Profile_Handler"></a>Our First Profile Handler</h2>
+<p>Although not terribly useful, a “null” profile
+	handler is a good example to start with because it is small and
+	will make sure your environment is in good working order before
+	proceeding to a real profile handler.
+      </p>
+<p>What's a “null” profile handler?  It's one that
+	serves no profiles.  That is, for any query with
+	<code>findProfiles</code> and any retrieval with
+	<code>get</code> it never returns any profiles.
+      </p>
+<div class="section"><h3><a name="Directory_Layout" id="Directory_Layout"></a>Directory Layout</h3>
+<p>For these examples, we'll work on a kind of Unix system
+	  with a <code>csh</code> shell.  Other shell users or Windows
+	  users will need to adjust.  We'll also use the J2SDK
+	  command-line tools.  If you're using an Integrated
+	  Development Environment of some sort, please adjust
+	  accordingly.
+	</p>
+<p>We'll create a "home" directory for our profile servers
+	  with subdirectories to hold specific components like source
+	  code, jar files, and scripts.  We'll call this home
+	  directory by an environment variable, <code>PS_HOME</code>
+	  (PS for Profile Server), so that scripts won't have to refer
+	  to things by relative paths:
+	</p>
+<div class="source"><pre>% <b>mkdir ps</b>
+% <b>cd ps</b>
+% <b>setenv PS_HOME `pwd`</b>
+% <b>mkdir bin classes lib src</b></pre>
+</div>
+</div>
+<div class="section"><h3><a name="Source_File" id="Source_File"></a>Source File</h3>
+<p>One of the easier parts is the source itself for the
+	  ‘null’ profile handler.  Here it is:
+	</p>
+<div class="source"><pre>import java.util.Collections;
+import java.util.List;
+import jpl.eda.profile.Profile;
+import jpl.eda.profile.handlers.ProfileHandler;
+import jpl.eda.xmlquery.XMLQuery;
+
+public class NullHandler implements ProfileHandler {
+  public List findProfiles(XMLQuery query) {
+    return Collections.EMPTY_LIST;
+  }
+  public Profile get(String id) {
+    return null;
+  }
+}</pre>
+</div>
+<p>Note that for every query, the
+	  <code>findProfiles</code> method returns an empty list
+	  (meaning that no profiles matched), and that for any retrieval
+	  the <code>get</code> method returns null, meaning that
+	  the handler believes there's no such profile.
+	</p>
+<p>This class should be compiled into a file named
+	  <code>$PS_HOME/src/NullHandler.java</code>
+	  since it is a public class.
+	</p>
+<p><em>Note:</em> Profile handler classes <em>must</em> be
+	  public <em>and</em> provide a no-arguments
+	  constructor.  You should retrieve any initialization
+	  settings through the System Properties or by other means
+	  specific to your profile handler.
+	</p>
+</div>
+<div class="section"><h3><a name="Compiling_the_Handler" id="Compiling_the_Handler"></a>Compiling the Handler</h3>
+<p>Compiling this profile handler requires the following
+	  dependent components:
+	</p>
+<ul><li><a href="http://oodt.apache.org/grid-profile/">Profile Service</a>.  This
+	    defines the entire profile model, handler interfaces,
+	    servers, clients, and so forth.
+	  </li>
+<li><a href="http://oodt.apache.org/edm-query/">Query Expression</a>.  This
+	    defines the <code>XMLQuery</code> and related classes.
+	  </li>
+</ul><p>Download the binary distributions of the above two packages
+	  and copy the jar file from each into the
+	  <code>$PS_HOME/lib</code> directory.  Then you can compile
+	  the <code>NullHandler.java</code> file.
+	</p>
+<div class="source"><pre>% <b>ls</b>
+bin   classes   lib    src
+% <b>ls -l lib</b>
+total 244
+-rw-r--r--  1 kelly  kelly   43879 28 Feb 07:05 edm-query-2.0.2.jar
+-rw-r--r--  1 kelly  kelly  201453 28 Feb 07:01 grid-profile-3.0.2.jar
+% <b>javac -extdirs lib -d classes src/NullHandler.java</b>
+% <b>ls -l classes</b>
+total 4
+-rw-r--r--  1 kelly  kelly  511 28 Feb 07:07 NullHandler.class
+% <b>jar -cf lib/my-handler.jar -C classes NullHandler.class</b>
+% <b>jar -tf lib/my-handler.jar</b>
+META-INF/
+META-INF/MANIFEST.MF
+NullHandler.class</pre>
+</div>
+<p>We now have a new jar file, <code>my-handler.jar</code>
+	  which contains our ‘null’ profile handler,
+	  compiled and ready to go.
+	</p>
+</div>
+<div class="section"><h3><a name="Starting_an_RMI_Registry" id="Starting_an_RMI_Registry"></a>Starting an RMI Registry</h3>
+<p>Clients access profile servers with an open-ended set of
+	  network protocols.  We currently have implementations for
+	  RMI and CORBA.  For this tutorial, we'll use RMI, since it's
+	  enormously less complex.  Clients of RMI systems first
+	  contact an RMI registry and look up a server object's
+	  network address.  The registry maintains mappings from a
+	  server object's name to its network address.  When servers
+	  start up, they register with the RMI registry so clients can
+	  later find them.
+	</p>
+<p>To start an RMI Registry, you'll need the following components:</p>
+<ul><li><a href="http://oodt.apache.org/edm-commons/">EDM Common Components</a>.
+	    These are common utilities used by every OODT
+	    service.
+	  </li>
+<li><a href="http://oodt.apache.org/rmi-registry/">OODT RMI Registry</a>.  This is the
+	    actual RMI registry.</li>
+</ul><p>Download each component's binary distribution, unpack each
+	  one, and take collect the jar files into the
+	  <code>lib</code> directory.  The RMI Registry will also need
+	  the <code>grid-profile</code> jar file, which we've already
+	  got.
+	</p>
+<div class="source"><pre>% <b>ls -l $PS_HOME/lib</b>
+total 404
+-rw-r--r--  1 kelly  kelly  149503 28 Feb 07:28 edm-commons-2.2.5.jar
+-rw-r--r--  1 kelly  kelly   43879 28 Feb 07:05 edm-query-2.0.2.jar
+-rw-r--r--  1 kelly  kelly  201453 28 Feb 07:01 grid-profile-3.0.2.jar
+-rw-r--r--  1 kelly  kelly     796 28 Feb 07:07 my-handler.jar
+-rw-r--r--  1 kelly  kelly    8055 28 Feb 07:28 rmi-registry-1.0.0.jar</pre>
+</div>
+<p>Now all we need is a convenient script to start the RMI
+	  registry.  We'll call it <code>rmi-reg</code> and stick it
+	  in the <code>bin</code> directory.  Here's the
+	  <code>rmi-reg</code> script:</p>
+<div class="source"><pre>#!/bin/sh
+exec java -Djava.ext.dirs=$PS_HOME/lib \
+    gov.nasa.jpl.oodt.rmi.RMIRegistry</pre>
+</div>
+<p>This script tells the Java virtual machine to find
+	  extension jars in the directory <code>$PS_HOME/lib</code>.  It
+	  then says that the main class to execute is
+	  <code>gov.nasa.jpl.oodt.rmi.RMIRegistry</code>.
+	</p>
+<p>Go ahead and make this script executable and start the RMI
+	  Registry. In another window (with the appropriate setting of
+	  <code>PS_HOME</code>), run
+	  <code>$PS_HOME/bin/rmi-reg</code>.  You should see output
+	  similar to the following:
+	</p>
+<div class="source"><pre>% <b>chmod 755 $PS_HOME/bin/rmi-reg</b>
+% <b>$PS_HOME/bin/rmi-reg</b>
+Mon Feb 28 07:30:13 CST 2005: no objects registered</pre>
+</div>
+<p>The RMI Registry is now running.  Every two minutes it will
+	  display an update of all registered objects.  Naturally, we
+	  don't have any profile service running right now, so it will
+	  say <code>no objects registered</code>.  Go ahead and ignore
+	  this window for now.  It's time to start our profile server.
+	</p>
+</div>
+<div class="section"><h3><a name="Starting_the_Profile_Server" id="Starting_the_Profile_Server"></a>Starting the Profile Server</h3>
+<p>With our handler compiled and our RMI registry running,
+	  we're ready to start our profile server.  As said before,
+	  profile servers delegate to zero or more profile handlers to
+	  actually handle all incoming requests.  You tell the profile
+	  server what handlers to instantiate by naming their classes
+	  in a system property.  That property is called
+	  <code>handlers</code>, and its value is a comma-separated
+	  list of fully qualified class names, including the package
+	  name prefixes.  Since our <code>NullHandler</code> is just
+	  in the default package, <code>NullHandler</code><em>is</em>
+	  its fully-qualified class name.
+	</p>
+<p>Profile server processes require the following components
+	  in addition to the ones we've downloaded so far:</p>
+<ul><li><a href="http://ws.apache.org/xmlrpc" class="externalLink">Apache
+	      XML-RPC</a>.  This is used internally by OODT services.
+	    Download version 1.1, not a later version!  If you prefer,
+	    you can <a href="http://ibiblio.org/maven/xmlrpc/jars/xmlrpc-1.1.jar" class="externalLink">fetch
+	      the jar file directly</a>.
+	  </li>
+<li><a href="http://jena.sourceforge.net/" class="externalLink">Jena Semantic Web
+	      Framework for Java</a>.  This is used by the classes
+	      that represent profiles.  You'll need version 1.6.1 You
+	      can also <a href="http://oodt.jpl.nasa.gov/download/public/Jena/jars/jena-1.6.1.jar" class="externalLink">fetch
+	      the jar file directly</a>.
+	  </li>
+</ul><p>Copy these two other jars to the <code>$PS_HOME/lib</code>
+	  directory.  You should now have seven jars there:
+	</p>
+<div class="source"><pre>% <b>ls -l $PS_HOME/lib</b>
+total 1580
+-rw-r--r--  1 kelly  kelly   149503 28 Feb 07:28 edm-commons-2.2.5.jar
+-rw-r--r--  1 kelly  kelly    43879 28 Feb 07:05 edm-query-2.0.2.jar
+-rw-r--r--  1 kelly  kelly   201453 28 Feb 07:01 grid-profile-3.0.2.jar
+-rw-r--r--  1 kelly  kelly  1144107 28 Feb 09:23 jena-1.6.1.jar
+-rw-r--r--  1 kelly  kelly      796 28 Feb 07:07 my-handler.jar
+-rw-r--r--  1 kelly  kelly     8055 28 Feb 07:28 rmi-registry-1.0.0.jar
+-rw-r--r--  1 kelly  kelly    53978 28 Feb 09:20 xmlrpc-1.1.jar</pre>
+</div>
+<p>Now, create a second shell script to make starting the
+	  profile server convenient and call it
+	  <code>$PS_HOME/bin/ps</code>.  It should look like this:
+	</p>
+<div class="source"><pre>#!/bin/sh
+exec java -Djava.ext.dirs=$PS_HOME/lib \
+    -Dhandlers=NullHandler \
+    jpl.eda.ExecServer \
+    jpl.eda.profile.rmi.ProfileServiceImpl \
+    urn:eda:rmi:MyProfileService</pre>
+</div>
+<p>Make the script executable and start the profile server:</p>
+<div class="source"><pre>% <b>chmod 755 $PS_HOME/bin/ps</b>
+% <b>$PS_HOME/bin/ps</b>
+Object context ready; delegating to: [jpl.eda.object.jndi.RMIContext@dec8b3]</pre>
+</div>
+<p>The profile server will start, check its
+	  <code>handlers</code> property, and create an object of each
+	  class named by it.  Then it'll register itself with the RMI
+	  registry and wait for requests to come in from profile clients.
+	</p>
+</div>
+<div class="section"><h3><a name="Whats_in_a_Name" id="Whats_in_a_Name"></a>What's in a Name?</h3>
+<p>The profile server registers itself using a name provided
+	  on the command-line, in this case,
+	  <code>urn:eda:rmi:MyProfileService</code>.  Let's take apart
+	  the name and see how it works.
+	</p>
+<p>If you're familiar with web standards, you can see that the
+	  name is a Uniform Resource Name (URN), since it starts with
+	  <code>urn:</code>.  The OODT Framework uses URNs to identify
+	  services and other objects.  The <code>eda:</code> tells
+	  that the name is part of the Enterprise Data Architecture
+	  (EDA) namespace.  (EDA was the name of a project related to
+	  OODT that was merged with OODT.  For now, just always use
+	  <code>eda:</code> in your URNs.)
+	</p>
+<p>Next comes <code>rmi:</code>.  This is a special flag for
+	  the OODT services that tells that we're using a name of an
+	  RMI-accessible object.  The OODT framework will know to use
+	  an RMI registry to register the server. 
+	</p>
+<p>Finally is <code>MyProfileService</code>.  This is the
+	  actual name used in the RMI registry.  You can call your
+	  profile server anything you want.  For example, suppose you
+	  have three profile servers; one in the US, one in Canada,
+	  and one in Australia.  You might name them:
+	</p>
+<ul><li><code>urn:eda:rmi:US</code></li>
+<li><code>urn:eda:rmi:Canada</code></li>
+<li><code>urn:eda:rmi:Australia</code></li>
+</ul><p>Or you might prefer to use ISO country codes.  Or you might
+	  name them according to the kinds of profiles they serve,
+	  such as <code>urn:eda:rmi:BiomarkerMetadata</code> or
+	  <code>urn:eda:rmi:BusniessForecastMetadata</code>.
+	</p>
+<p>The RMI registry will happily re-assign a name if one's
+	  already in use, so when deploying your own profile (and
+	  other) servers, be sure to give each one a unique name.
+	</p>
+</div>
+<div class="section"><h3><a name="Querying_the_Profile_Server" id="Querying_the_Profile_Server"></a>Querying the Profile Server</h3>
+<p>To query a profile server, you use the
+	  <code>ProfileClient</code> class.  It provides methods to
+	  contact a named profile server, performing the lookup in the
+	  RMI registry, contacting the profile server, and passing in
+	  queries and profile retrievals.  The
+	  <code>ProfileClient</code> class is also an
+	  <em>executable</em> class, making it perfect for testing a
+	  new profile server from the command-line.
+	</p>
+<p>Still, we'll make a script, called
+	  <code>$PS_HOME/bin/pc</code> (for "profile client") to
+	  execute it, though, to save from having to type hugely long
+	  Java command-lines:
+	</p>
+<div class="source"><pre>#!/bin/sh
+if [ $# -ne 1 ]; then
+   echo "Usage: `basename $0` &lt;query-expression&gt;" 1&gt;&amp;2
+   exit 1
+fi
+exec java -Djava.ext.dirs=$PS_HOME/lib \
+     jpl.eda.profile.ProfileClient \
+     urn:eda:rmi:MyProfileService \
+     "$1"</pre>
+</div>
+<p>Make this script executable and then run it:</p>
+<div class="source"><pre>% <b>chmod 755 $PS_HOME/bin/pc</b>
+% <b>$PS_HOME/bin/pc "temperature = 37"</b>
+Object context ready; delegating to: [jpl.eda.object.jndi.RMIContext@dec8b3]
+[]</pre>
+</div>
+<p>Although it may not look spetacular, this is a success!
+	  The two square brackets, <code>[]</code>, indicates the list
+	  of matching profiles to our query expression,
+	  <code>temperature = 37</code>.  In this case, there were no
+	  matches, which is exactly what we wanted.
+	</p>
+</div>
+</div><div class="section"><h2><a name="Conclusion" id="Conclusion"></a>Conclusion</h2>
+<p>The Null Profile Server made sure our development
+	environment worked from end to end for creating, deploying, and
+	testing a profile handler.  Now you're ready to implement a real
+	profile handler:
+      </p>
+<ul><li>Instead of returning an empty list, create a
+	    <code>Profile</code> object and return it as a
+	    singleton list.  See the API documentation for
+	    <code>Profile</code> as well as other articles
+	    for manipulating this class.
+	</li>
+<li>Analyze the methods of the
+	  <code>XMLQuery</code> class to determine the
+	  query passed in from the user.  Use that information to
+	  synthesize the correct <code>Profile</code>
+	  object.  See the API documentation for class
+	  <code>XMLQuery</code> for more information.
+	</li>
+<li>Connecting to an external data source (such as the
+	    local filesystem or a database), synthesize appropriate
+	  profiles in response to queries and profile retrieval with
+	  the <code>get</code> method.
+	</li>
+</ul></div></div><div class="visualClear"></div></div><div class="triptychRight"><div class="columnBorder"><div class="visualPadding" id="rightPlate"><div id="navcolumn">&#13;
+           &#13;
+  &#13;
+&#13;
+  &#13;
+    &#13;
+  &#13;
+  &#13;
+    &#13;
+                   <h5>Project Documentation</h5>&#13;
+            <ul><li class="collapsed">&#13;
+                    <a href="../project-info.html">Project Information</a>&#13;
+                </li>&#13;
+              &#13;
+                &#13;
+              &#13;
+      &#13;
+            &#13;
+      &#13;
+            &#13;
+      &#13;
+            &#13;
+      &#13;
+              &#13;
+        <li class="collapsed">&#13;
+                    <a href="../project-reports.html">Project Reports</a>&#13;
+                </li>&#13;
+          </ul><h5>User's Guide</h5>&#13;
+            <ul><li class="none">&#13;
+                    <a href="../info/">Information Captured</a>&#13;
+          </li>&#13;
+              &#13;
+    <li class="none">&#13;
+                    <a href="../querying/">Querying Profiles</a>&#13;
+          </li>&#13;
+              &#13;
+    <li class="none">&#13;
+                    <a href="../rep/">Profile Repr.</a>&#13;
+          </li>&#13;
+              &#13;
+    <li class="none">&#13;
+                    <a href="../handler/">Basic Handler</a>&#13;
+          </li>&#13;
+              &#13;
+    <li class="none">&#13;
+                    <a href="../adv/">Advanced Handler</a>&#13;
+          </li>&#13;
+          </ul>&#13;
+                       &#13;
+  &#13;
+&#13;
+  &#13;
+    &#13;
+  &#13;
+  &#13;
+    &#13;
+        </div></div></div></div></div></div><div style="clear: both;"> </div></div><div id="push"></div></div><div id="footerWrapper"><div id="footerGradient"></div><div id="footer"><a id="footerLogo" href="http://oodt.apache.org/" name="footerLogo"></a><p>
+                    Apache™ OODT is © 2010 by the Apache Software Foundation (ASF). Apache and the Apache
+                    feather logo are trademarks of the ASF.
+                </p><p>
+                    Apache™ OODT OODT is a Top Level Project and endorsed by the ASF. You are full of win.
+                </p><div id="sitemap"><dl><dt><a href="http://oodt.apache.org/about">About OODT</a></dt><dd><a href="http://oodt.apache.org/about/history">History</a></dd><dd><a href="http://oodt.apache.org/about/users">Users</a></dd><dd><a href="http://oodt.apache.org/about/incubation">Incubation</a></dd></dl><dl><dt><a href="http://www.apache.org/">Apache</a></dt><dd><a href="http://incubator.apache.org/">Incubator</a></dd><dd><a href="http://www.apache.org/licenses">License</a></dd><dd><a href="http://www.apache.org/foundation/sponsorship.html">Donate</a></dd><dd><a href="http://www.apache.org/foundation/thanks.html">Thanks</a></dd><dd><a href="http://www.apache.org/security/">Security</a></dd></dl><dl><dt><a href="http://oodt.apache.org/components">Components</a></dt><dd><a href="http://oodt.apache.org/components/agility">Agility</a></dd><dd><a href="http://oodt.apache.org/components/cas">Catalog &amp; Archive</a></dd><dd><a href="http://oodt.apache.org/components/maven/
 xmlquery">Query</a></dd><dd><a href="http://oodt.apache.org/components/grid">Grid</a></dd><dd><a href="http://oodt.apache.org/components/common">Common</a></dd></dl><dl><dt><a href="http://oodt.apache.org/development">Development</a></dt><dd><a href="http://oodt.apache.org/development/roadmap">Roadmap</a></dd><dd><a href="http://oodt.apache.org/development/bugs">Report Bugs</a></dd><dd><a href="http://oodt.apache.org/development/source">Source Code</a></dd><dd><a href="http://oodt.apache.org/contact">Mailing Lists</a></dd></dl><dl><dt><a href="http://oodt.apache.org/contact">Social</a></dt><dd><a href="http://www.facebook.com/group.php?gid=322088549131">Facebook</a></dd><dd><a href="http://twitter.com/apache_oodt/">Twitter</a></dd><dd><a href="irc://irc.freenode.net/#oodt">Chat Room</a></dd></dl></div></div></div></body></html>
\ No newline at end of file