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">
+
+ Profile Service
+
+ </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 =, !=, <, >, <=, >=, 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() > 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]
+[<?xml version="1.0" encoding="UTF-8"?>
+<profile><profAttributes><profId>urn:sk:tr91BC.mp3</profId>...</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><Title></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">
+
+
+
+
+
+
+
+
+ <h5>Project Documentation</h5>
+ <ul><li class="collapsed">
+ <a href="../project-info.html">Project Information</a>
+ </li>
+
+
+
+
+
+
+
+
+
+
+
+ <li class="collapsed">
+ <a href="../project-reports.html">Project Reports</a>
+ </li>
+ </ul><h5>User's Guide</h5>
+ <ul><li class="none">
+ <a href="../info/">Information Captured</a>
+ </li>
+
+ <li class="none">
+ <a href="../querying/">Querying Profiles</a>
+ </li>
+
+ <li class="none">
+ <a href="../rep/">Profile Repr.</a>
+ </li>
+
+ <li class="none">
+ <a href="../handler/">Basic Handler</a>
+ </li>
+
+ <li class="none">
+ <a href="../adv/">Advanced Handler</a>
+ </li>
+ </ul>
+
+
+
+
+
+
+
+
+ </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 & 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">
+
+ Profile Service
+
+ </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` <query-expression>" 1>&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">
+
+
+
+
+
+
+
+
+ <h5>Project Documentation</h5>
+ <ul><li class="collapsed">
+ <a href="../project-info.html">Project Information</a>
+ </li>
+
+
+
+
+
+
+
+
+
+
+
+ <li class="collapsed">
+ <a href="../project-reports.html">Project Reports</a>
+ </li>
+ </ul><h5>User's Guide</h5>
+ <ul><li class="none">
+ <a href="../info/">Information Captured</a>
+ </li>
+
+ <li class="none">
+ <a href="../querying/">Querying Profiles</a>
+ </li>
+
+ <li class="none">
+ <a href="../rep/">Profile Repr.</a>
+ </li>
+
+ <li class="none">
+ <a href="../handler/">Basic Handler</a>
+ </li>
+
+ <li class="none">
+ <a href="../adv/">Advanced Handler</a>
+ </li>
+ </ul>
+
+
+
+
+
+
+
+
+ </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 & 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