oodt-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mattm...@apache.org
Subject [1/6] oodt git commit: - add in xmlquery and product handler code necessary to build this app
Date Sun, 16 Jul 2017 18:33:30 GMT
Repository: oodt
Updated Branches:
  refs/heads/master f1e5bed64 -> 4066b63bc


http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/site/xdoc/tutorials/qh/index.xml
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/site/xdoc/tutorials/qh/index.xml b/webapp/fmprod/src/site/xdoc/tutorials/qh/index.xml
new file mode 100755
index 0000000..b8e34f6
--- /dev/null
+++ b/webapp/fmprod/src/site/xdoc/tutorials/qh/index.xml
@@ -0,0 +1,704 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<document>
+  <properties>
+    <title>Developing a Query Handler</title>
+    <author email="Sean.Kelly@jpl.nasa.gov">Sean Kelly</author>
+  </properties>
+
+  <body>
+    <section name="Developing a Query Handler">
+      <p>In the <a href="../ps/">last tutorial</a>, we started a
+	product server.  But this wasn't a very useful product server;
+	it could answer queries but always respond with no results.
+	That's because it had no query handlers.  Query handlers have
+	the responsibility of actually handling product queries.  In
+	this tutorial, we'll develop a query handler, install it into
+	our product server, and query it to see if it works.
+      </p>
+
+      <p>To do this tutorial, you'll need mastery of two things:</p>
+      <ul>
+	<li>Using the <code>XMLQuery</code> class.  Follow the <a
+	    href="/edm-query/tutorial/">query expression tutorail</a>
+	    now if you're not familiar with it.
+	</li>
+
+	<li>Running and querying a product server.  Follow the <a
+	    href="../ps/">Your First Product Server</a> tutorial to
+	    get your product server up and running.  In this tutorial,
+	    we'll build on that product server, so it's especially
+	    important to have it in good shape.
+	</li>
+      </ul>
+    </section>
+
+    <section name="Serving Up Constants">
+      <p>Product servers delegate to query handlers.  It's the job of
+	query handlers to interpret incoming queries (expressed as
+	<code>XMLQuery</code> objects), search for, retrieve, convert,
+	or synthesize matching product results, adorn the
+	<code>XMLQuery</code> object with <code>Result</code> objects,
+	and return the modified query.  At that point the OODT
+	framework takes over again and tries other installed query
+	handlers, eventually returning the completed
+	<code>XMLQuery</code> back to the product client that made the
+	query in the first place.
+      </p>
+
+      <p>We'll make a query handler that serves mathematical
+	constants.  Have you ever been in a position where you needed,
+	say, the value of the third Flajolet number or perhaps
+	Zeta(9)?  No?  Well, just pretend for now you did.  What we'll
+	do is develop a query handler for a product server that will
+	serve values of various mathematical constants.
+      </p>
+
+      <p>The approach we'll take has three simple steps:</p>
+
+      <ol>
+	<li>Get some handy constants.</li>
+	<li>Define the query expression.</li>
+	<li>Write a query handler.  The query handler will:
+	  <ol>
+	    <li>Examine the query expression to see if it's a request
+	      for a constant, and if so, what constant is
+	      requested.
+	    </li>
+	    <li>Examine the query's list of acceptable MIME types.</li>
+	    <li>If both check out, look up the desired constant's value.</li>
+	    <li>If found, add it as a <code>Result</code> in the <code>XMLQuery</code>.</li>
+	  </ol>
+	</li>
+      </ol>
+    </section>
+
+    <section name="Writing the Code">
+      <p>In this section, we'll build up the query handler source code
+	in pieces, examining each piece thoroughly.  We'll then
+	present the entire source file.
+      </p>
+
+      <subsection name='Gathering Handy Constants'>
+	<p>The wonderful world of science and mathematics is replete
+	  with useful constant values.  For this example, let's just pick three:
+	</p>
+
+	<ul>
+	  <li><var>pi</var> = 3.14159265...</li>
+	  <li><var>e</var> = 2.7182818285...</li>
+	  <li><var>gamma</var> = 0.577215664...</li>
+	</ul>
+
+	<p>In Java code, we can set up those values as a
+	  <code>Map</code> in a static field.  Thus we start forming our
+	  source file, <code>ConstantHandler.java</code>:
+	</p>
+
+	<source>import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
+import jpl.eda.product.QueryHandler;
+public class ConstantHandler
+  implements QueryHandler {
+  private static final Map CONSTANTS = new ConcurrentHashMap();
+  static {
+    CONSTANTS.put("pi",    "3.14159265...");
+    CONSTANTS.put("e",     "2.7182818285...");
+    CONSTANTS.put("gamma", "0.577215664...");
+  }
+}</source>
+
+	<p>As you can see, we're storing both the constant name and its
+	  value as <code>java.lang.String</code> objects.
+	</p>
+      </subsection>
+
+      <subsection name='Defining the Query Expression'>
+	<p>Recall that the <code>XMLQuery</code> class can use parsed
+	  queries (where it generates postfix boolean stacks) or
+	  unparsed ones.  While unparsed ones are easier, we'll go with
+	  parsed ones to demonstrate how on the server-side you deal
+	  with those postfix stacks.
+	</p>
+
+	<p>Using the XMLQuery's expression language, we'll look for
+	  queries of the form:</p>
+	
+	<p><code>constant = <var>name</var></code></p>
+
+	<p>where <var>name</var> is the name of a constant.  That will
+	  form a postfix "where" stack with exactly three
+	  <code>QueryElement</code> objects on it:
+	</p>
+
+	<ol>
+	  <li>The first (top) <code>QueryElement</code> will have role =
+	    <code>elemName</code> and value = <code>constant</code>.
+	  </li>
+
+	  <li>The second (middle) <code>QueryElement</code> will have
+	    role = <code>LITERAL</code> and a value equal to the
+	    constant <var>name</var>.
+	  </li>
+
+	  <li>The third (bottom) <code>QueryElement</code> will have
+	    role = <code>RELOP</code> and value = <code>EQ</code>.
+	  </li>
+	</ol>
+
+	<p>If we get any other kind of stack, we'll reject it and return
+	  no matching results.  That's reasonable behavior; after all, a
+	  query for <code>donutsEaten &gt; 5 AND RETURN =
+	    episodeNumber</code> may be handled by a
+	  <code>SimpsonsEpisodeQueryHandler</code> that's <em>also</em>
+	  installed in the same product server.
+	</p>
+
+	<p>We'll define a utility method, <code>getConstantName</code>,
+	  that will take the <code>XMLQuery</code>, check for the
+	  postfix "where" stack as described, and return the matching
+	  constant <var>name</var>.  If it gets a stack whose structure
+	  doesn't match, it will return <code>null</code>.  We'll add
+	  this method to our <code>ConstantHandler.java</code> file:
+	</p>
+
+	<source>import java.util.List;
+import jpl.eda.xmlquery.XMLQuery;
+import jpl.eda.xmlquery.QueryElement;
+...
+public class ConstantHandler
+  implements QueryHandler {
+  ...
+  private static String getConstantName(XMLQuery q) {
+    List stack = q.getWhereElementSet();
+    if (stack.size() != 3) return null;
+    QueryElement e = (QueryElement) stack.get(0);
+    if (!"elemName".equals(e.getRole())
+      || !"constant".equals(e.getValue()))
+      return null;
+    e = (QueryElement) stack.get(2);
+    if (!"RELOP".equals(e.getRole())
+      || !"EQ".equals(e.getValue()))
+      return null;
+    e = (QueryElement) stack.get(1);
+    if (!"LITERAL".equals(e.getRole()))
+	return null;
+    return e.getValue();
+  }
+}</source>
+
+	<p>Here, we first check to make sure there's exactly three
+	  elements, returning null if not.  There's no need to go further.
+	</p>
+
+	<p>Assuming there's three elements, the code then checks the
+	  topmost element.  For an expression <code>constant =
+	    <var>name</var></code>, the topmost element will have role
+	  <code>elemName</code> and value <code>constant</code>.  If
+	  neither condition is true, we return null right away.  No need
+	  to check further.
+	</p>
+
+	<p>If the topmost element checks out, we then check the
+	  bottommost element.  For <code>constant =
+	    <var>name</var></code>, the bottom element is generated from
+	  the equals sign.  It will have role <code>RELOP</code>
+	  (relational operator) and value <code>EQ</code>, meaning
+	  "equals".
+	</p>
+
+	<p>If it checks out, all we have to do is check the middle
+	  element.  The infix expression <code>constant =
+	    <var>name</var></code> generates a postfix middle element of
+	  <var>name</var> as the value, with a role of
+	  <code>LITERAL</code>.  We make sure it's <code>LITERAL</code>.
+	  If not, we're done; it's not a valid expression for our query
+	  handler.
+	</p>
+
+	<p>But if so, then the value of that query element is the name
+	  of the desired constant.  So we return it, regardless of what
+	  it is.
+	</p>
+      </subsection>
+
+      <subsection name='Checking for Acceptable MIME Types'>
+	<p>Since all of our mathematical constants are strings, we'll
+	  say that the result MIME type of our products is
+	  <code>text/plain</code>.  That means that any incoming
+	  <code>XMLQuery</code> must include any of the following MIME types:
+	</p>
+	<ol>
+	  <li><code>text/plain</code></li>
+	  <li><code>text/*</code></li>
+	  <li><code>*/*</code></li>
+	</ol>
+	<p>All of these match <code>text/plain</code>, which is the only
+	  product type we're capable of serving.  (In your own product
+	  servers, you might have more complex logic; for example, you
+	  could write code to draw the numbers into an image file if the
+	  requested type is <code>image/jpeg</code> ... but I wouldn't
+	  want to.)
+	</p>
+	<p>To support this in our query handler, we'll write another
+	  utility method.  It'll be called
+	  <code>isAcceptableType</code>, and it will take the
+	  <code>XMLQuery</code> and examine it to see what MIME types
+	  are acceptable to the caller.  If it finds any of the ones in
+	  the above list, it will return <code>true</code>, and the
+	  caller can continue to process the query.  If not, it will
+	  return <code>false</code>, and the query handler will stop
+	  processing and return the <code>XMLQuery</code> unadorned with
+	  any results.
+	</p>
+	<p>Here's the code:</p>
+
+	<source>import java.util.Iterator;
+...
+public class ConstantHandler
+  implements QueryHandler {
+  ...
+  private static boolean isAcceptableType(XMLQuery q) {
+    List mimes = q.getMimeAccept();
+    if (mimes.isEmpty()) return true;
+    for (Iterator i = mimes.iterator(); i.hasNext();) {
+      String type = (String) i.next();
+      if ("text/plain".equals(type)
+        || "text/*".equals(type)
+        || "*/*".equals(type)) return true;
+    }
+    return false;
+  }
+}</source>
+
+	<p>Here, we check if the list of acceptable MIME types is empty.
+	  An empty list is the same as saying <code>*/*</code>, so that
+	  automatically says we've got an acceptable type.  For a
+	  non-empty list, we go through each type one-by-one.  If it's
+	  any of the strings <code>text/plain</code>,
+	  <code>text/*</code>, or <code>*/*</code>, then that's an
+	  acceptable type.
+	</p>
+
+	<p>However, if we get through the entire list and we don't find
+	  any type that the user wants that we can provide, we return
+	  <code>false</code>.  The query handler will check for a
+	  <code>false</code> value and return early from handling the
+	  query, leaving the <code>XMLQuery</code> untouched.
+	</p>
+      </subsection>
+
+      <subsection name='Inserting the Result'>
+	<p>Assuming the query handler has found an acceptable MIME type,
+	  and has found a valid query and the name of the desired
+	  constant, it can lookup the constant in the
+	  <code>CONSTANTS</code> map.  And assuming it finds a matching
+	  constant in that map, it can insert the value as a
+	  <code>Result</code> object.
+	</p>
+
+	<p>To insert the constant's value, we'll develop yet another
+	  utility method, this time called <code>insert</code>.  This
+	  method will take the name of the constant, its value, and the
+	  <code>XMLQuery</code>.  It will add a <code>Result</code>
+	  object to the <code>XMLQuery</code>.  When the query handler
+	  returns this modified <code>XMLQuery</code> object, the
+	  framework will return it to the product client, which can then
+	  display the matching result.
+	</p>
+
+	<p><code>Result</code> objects can also have optional
+	  <code>Header</code> objects that serve as "column headings"
+	  for tabular like results.  Our result isn't tabular, it's just
+	  a single value, but we'll add a heading anyway just to
+	  demonstrate how it's done.  (You could argue that it's a
+	  one-by-one table, too!)  The header's name will be the same as
+	  the constant's name; the data type will be <code>real</code>
+	  and the units will be <code>none</code>.
+	</p>
+
+	<p>Here's the code:</p>
+
+	<source>import java.util.Collections;
+import jpl.eda.xmlquery.Header;
+import jpl.eda.xmlquery.Result;
+...
+public class ConstantHandler
+  implements QueryHandler {
+  ...
+  private static void insert(String name,
+    String value, XMLQuery q) {
+    Header h = new Header(name, "real", "none");
+    Result r = new Result(name, "text/plain",
+      /*profileID*/null, /*resourceID*/null,
+      Collections.singletonList(h),
+      value, /*classified*/false, Result.INFINITE);
+    q.getResults().add(r);
+  }
+}</source>
+
+	<p>In this method, we first create the header.  Then we create
+	  the result; the result's ID (which differentiates it from
+	  other results in the same <code>XMLQuery</code> is just the
+	  name of the constant.  Its MIME type is
+	  <code>text/plain</code>.  We set the profile ID and resource
+	  ID fields to <code>null</code>, as recommended back in the <a
+	    href="/edm-query/tutorial/">XMLQuery Tutorial</a>.  Then we
+	  add our sole header.  Then we add the mathematical constant's
+	  value.  Finally, this constant isn't classified, so we set the
+	  classified flag to <code>false</code>.  Also, these
+	  mathematical constants should be valid forever, so we set the
+	  validity period to <code>Result.INFINITE</code>, a special
+	  value that means a never-ending validity period.
+	</p>
+      </subsection>
+
+      <subsection name='Handling the Query'>
+	<p>With all of these utility methods in hand, it's easy to
+	  handle the query now.  The
+	  <code>jpl.eda.product.QueryHandler</code> interface
+	  specifies a single method that we must implement,
+	  <code>query</code>.  This method accepts an
+	  <code>XMLQuery</code> object and returns an
+	  <code>XMLQuery</code> object.  The returned one may or may
+	  not be adorned with matching results.
+	</p>
+
+	<p>Here's what we have to do:</p>
+	<ol>
+	  <li>Get the constant name with <code>getConstantName</code>.
+	    If we get <code>null</code>, it means the query's not of
+	    the form <code>constant = <var>name</var></code>, so we
+	    ignore it.
+	  </li>
+
+	  <li>See if the user's willing to accept a
+	    <code>text/plain</code> MIME type.  If not, we ignore this query.
+	  </li>
+
+	  <li>Find the constant in our <code>CONSTANTS</code> map.  If
+	    it's not there, we ignore this query.
+	  </li>
+
+	  <li>Insert the constant's value into the <code>XMLQuery</code>.</li>
+
+	  <li>Returned the modified <code>XMLQuery</code>.</li>
+	</ol>
+
+	<p>The source:</p>
+
+	<source>public class ConstantHandler
+  implements QueryHandler {
+  ...
+  public XMLQuery query(XMLQuery q) {
+    String name = getConstantName(q);
+    if (name == null) return q;
+    if (!isAcceptableType(q)) return q;
+    String value = (String) CONSTANTS.get(name);
+    if (value == null) return q;
+    insert(name, value, q);
+    return q;
+  }
+}</source>
+
+      </subsection>
+
+      <subsection name='Complete Source Code'>
+	<p>Here is the complete source file,
+	<code>ConstantHandler.java</code>:</p>
+
+	<source>import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import jpl.eda.product.QueryHandler;
+import jpl.eda.xmlquery.Header;
+import jpl.eda.xmlquery.Result;
+import jpl.eda.xmlquery.QueryElement;
+import jpl.eda.xmlquery.XMLQuery;
+
+public class ConstantHandler
+  implements QueryHandler {
+  private static final Map CONSTANTS = new ConcurrentHashMap();
+  static {
+    CONSTANTS.put("pi",    "3.14159265...");
+    CONSTANTS.put("e",     "2.7182818285...");
+    CONSTANTS.put("gamma", "0.577215664...");
+  }
+  private static String getConstantName(XMLQuery q) {
+    List stack = q.getWhereElementSet();
+    if (stack.size() != 3) return null;
+    QueryElement e = (QueryElement) stack.get(0);
+    if (!"elemName".equals(e.getRole())
+      || !"constant".equals(e.getValue()))
+      return null;
+    e = (QueryElement) stack.get(2);
+    if (!"RELOP".equals(e.getRole())
+      || !"EQ".equals(e.getValue()))
+      return null;
+    e = (QueryElement) stack.get(1);
+    if (!"LITERAL".equals(e.getRole()))
+	return null;
+    return e.getValue();
+  }
+  private static boolean isAcceptableType(XMLQuery q) {
+    List mimes = q.getMimeAccept();
+    if (mimes.isEmpty()) return true;
+    for (Iterator i = mimes.iterator(); i.hasNext();) {
+      String type = (String) i.next();
+      if ("text/plain".equals(type)
+        || "text/*".equals(type)
+        || "*/*".equals(type)) return true;
+    }
+    return false;
+  }
+  private static void insert(String name,
+    String value, XMLQuery q) {
+    Header h = new Header(name, "real", "none");
+    Result r = new Result(name, "text/plain",
+      /*profileID*/null, /*resourceID*/null,
+      Collections.singletonList(h),
+      value, /*classified*/false, Result.INFINITE);
+    q.getResults().add(r);
+  }
+  public XMLQuery query(XMLQuery q) {
+    String name = getConstantName(q);
+    if (name == null) return q;
+    if (!isAcceptableType(q)) return q;
+    String value = (String) CONSTANTS.get(name);
+    if (value == null) return q;
+    insert(name, value, q);
+    return q;
+  }
+}</source>
+
+	<p>How should you go about compiling this and installing it in
+	  a product server?  Read on!
+	</p>
+      </subsection>
+    </section>
+
+    <section name="Compiling the Code">
+      <p>We'll compile this code using the J2SDK command-line tools,
+	but if you're more comfortable with some kind of Integrated
+	Development Environment (IDE), adjust as necessary.
+      </p>
+
+      <p>First, let's go back to the <code>$PS_HOME</code> directory
+	we made earlier and make directories to hold both the source
+	code and classes that we'll compile from it:
+      </p>
+
+      <source>% <b>cd $PS_HOME</b>
+% <b>mkdir classes src</b></source>
+
+      <p>Then, create <code>$PS_HOME/src/ConstantHandler.java</code> using
+	your favorite text editor (or by cutting and pasting the source
+	from this page, or whatever).  Finally, compile the file as follows:
+      </p>
+
+      <source>% <b>javac -extdirs lib \
+  -d classes src/ConstantHandler.java</b>
+% ls -l classes
+total 4
+-rw-r--r--  1 kelly  kelly  2524 25 Feb 15:46 ConstantHandler.class</source>
+
+      <p>The <code>javac</code> command is the Java compiler.  The
+	<code>-extdirs lib</code> arguments tell the compiler where to
+	find extension jars.  In this case, the code references things
+	defined in edm-query-2.0.2.jar and grid-product-3.0.3.jar.
+	The <code>-d classes</code> tells where compiled classes
+	should go.
+      </p>
+
+      <p>Next, make a jar file that contains your compiled class:</p>
+
+      <source>% <b>jar -cf lib/my-handlers.jar \
+  -C classes ConstantHandler.class</b>
+% <b>jar -tf lib/my-handlers.jar</b>
+META-INF/
+META-INF/MANIFEST.MF
+ConstantHandler.class</source>
+
+      <p>We now have a new jar file of our own creation in the
+	<code>$PS_HOME/lib</code> directory; this means that the
+	product server will be able to find out new query handler.
+	All we have to do now is tell our product server about it.
+      </p>
+    </section>
+
+    <section name='Specfying the Query Handler'>
+      <p>Query handlers aren't really <em>installed</em> into product
+	servers.  What you do is tell the product server what query
+	handlers you want it to use by naming their classes.  The
+	product server will instantiate an object of each class and,
+	as queries come in, it will delegate queries to each
+	instantiated query handler.
+      </p>
+
+      <p>To tell a product server what query handlers to instantiate,
+	you specify a system property called <code>handlers</code>.
+	You set this property to a comma-separated list of class
+	names.  These should be fully-qualified class names (with
+	package prefixes, if you used packages when making your query
+	handlers), separated by commas.  In this tutorial, we made
+	just one query handler, and we didn't put it into a package,
+	so we'll just use <code>ConstantHandler</code>.
+      </p>
+
+      <p>First, stop any product server you have running now by
+	pressing CTRL+C (or whatever your interrupt key is) in the
+	window that was running the product server.  Next, modify the
+	<code>$PS_HOME/bin/ps</code> file so it reads as follows:
+      </p>
+
+      <source>#!/bin/sh
+exec java -Djava.ext.dirs=$PS_HOME/lib \
+    -Dhandlers=ConstantHandler \
+    jpl.eda.ExecServer \
+    jpl.eda.product.rmi.ProductServiceImpl \
+    urn:eda:rmi:MyProductService</source>
+
+      <p>We specified a system property on the command line using
+	Java's <code>-D</code> option.  This defines the system
+	property <code>handlers</code> as having the value
+	<code>ConstantHandler</code>.  Finally, start the product
+	server again by running <code>$PS_HOME/bin/ps</code>.
+      </p>
+    </section>
+
+    <section name='Querying for Constants'>
+      <p>Once again, edit the <code>$PS_HOME/bin/pc</code> script and
+	change <code>-xml</code> back to <code>-out</code> so that
+	instead of the XML output we'll see the raw product data.
+	Then run it and see what happens:
+      </p>
+
+      <source>% <b>$PS_HOME/bin/pc 'constant = pi'</b>
+3.14159265...% </source>
+
+      <p>Because the raw product data was the string
+	<code>3.14159265...</code> without any trailing newline, the
+	shell's prompt appeared right at the end of the product
+	result.  You might try piping the output of the above command
+	through a pager like <code>more</code> or <code>less</code> to
+	avoid this.
+      </p>
+
+      <p>Here's what happened when we ran this command:</p>
+
+      <ol>
+	<li>The product client created an <code>XMLQuery</code> object
+	  out of the string query <code>constant = pi</code>.
+	</li>
+
+	<li>It asked the RMI Registry to tell it where (network
+	  address) it could find the product service named
+	  <code>MyProductService</code>.
+	</li>
+
+	<li>After getting the response back from the RMI Registry, it
+	  then contacted the product service over a network connection
+	  (even if to the same local system) and asked it to handle
+	  the query, passing the query object.
+	</li>
+
+	<li>The product service had only one query handler, the
+	  <code>ConstantHandler</code>, to which to delegate, so it
+	  passed the XMLQuery to it.
+	</li>
+
+	<li>The <code>ConstantHandler</code>'s <code>query</code>
+	  method was called.  It checked if the query was the kind it
+	  wanted, extracted the desired mathematical constant's name,
+	  checked for an acceptable requested MIME type, looked up the
+	  constant's value, inserted it as a <code>Result</code> into
+	  the <code>XMLQuery</code>, and returned the modified query.
+	</li>
+
+	<li>The product service, seeing it had no other handlers to
+	  try, returned the modified <code>XMLQuery</code> to the
+	  product client over the network connection.
+	</li>
+
+	<li>The product client took the first <code>Result</code> out
+	  of the <code>XMLQuery</code>, called
+	  <code>Result.getInputStream</code>, and copied each byte of
+	  the result to the standard output.  This wrote
+	  <code>3.14159265...</code> to your window.
+	</li>
+      </ol>
+
+      <p>If you change the <code>$PS_HOME/bin/pc</code> script again
+	so that instead of <code>-out</code> it's <code>-xml</code>,
+	you'll again see the XMLQuery as an XML document.  The interesting part is the <code>&lt;queryResultSet&gt;</code>:
+      </p>
+
+      <source><![CDATA[<queryResultSet>
+  <resultElement classified="false" validity="-1">
+    <resultId>pi</resultId>
+    <resultMimeType>text/plain</resultMimeType>
+    <profId/>
+    <identifier/>
+    <resultHeader>
+      <headerElement>
+	<elemName>pi</elemName>
+	<elemType>real</elemType>
+	<elemUnit>none</elemUnit>
+      </headerElement>
+    </resultHeader>
+    <resultValue xml:space="preserve">3.14159265...</resultValue>
+  </resultElement>
+</queryResultSet>]]></source>
+
+      <p>I'll let you figure out how this maps to the
+	<code>Result</code> object we created in the code.  OK, so is
+	this really interesting?  Not really, except to note that the
+	actual result data, <code>3.1415265...</code>, appears in the
+	XML document.  In the OODT framework, we call this a "small"
+	result, because the product data was embedded in the XMLQuery
+	object.  Text data like <code>text/plain</code> products
+	appear just as text.  Binary data like <code>image/jpeg</code>
+	and <code>application/octet-stream</code> get base-64 encoded
+	into text.
+      </p>
+      <p>As you can guess, there's a point at which encoded data goes
+	from being nice and small and tidy to just too large to
+	contain in a single object.  In the OODT framework, we can use
+	a special query handler for such large products, but that's
+	another tutorial.
+      </p>
+    </section>
+
+    <section name='Conclusion'>
+      <p>In this tutorial, we learned how to write a complete query
+	handler for a product server, including handling of postfix
+	boolean "where" stacks, lists of acceptable MIME types, and
+	result headers.  We compiled the query handler, put it into a
+	jar file, and specified it to our product server.  And we
+	could even query it and get data back.
+      </p>
+
+      <p>Don't toss out this product server yet, though.  Another
+	tutorial will use it and cover the infamous
+	<code>LargeProductQueryHandler</code>.
+      </p>
+    </section>
+  </body>
+</document>

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CodecFactoryTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CodecFactoryTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CodecFactoryTest.java
new file mode 100755
index 0000000..756ee6f
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CodecFactoryTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import java.io.InputStream;
+import org.apache.oodt.commons.io.NullInputStream;
+import org.apache.oodt.xmlquery.CodecFactory; // Imported solely for Javadoc
+import junit.framework.TestCase;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/** Unit test the {@link CodecFactory} class.
+ *
+ * @author Kelly
+ */ 
+public class CodecFactoryTest extends TestCase {
+	/** Construct the test case for the {@link CodecFactory} class. */
+	public CodecFactoryTest(String name) {
+		super(name);
+	}
+
+	public void testInvalidCodec() {
+		try {
+			Codec codec = CodecFactory.createCodec("unknown.class.name");
+			fail("CodecFactory somehow created an object of an unknown class");
+		} catch (RuntimeException ignored) {}
+	}
+
+	public void testValidCodec() {
+		Codec c1 = CodecFactory.createCodec("org.apache.oodt.xmlquery.CodecFactoryTest$TestCodec");
+		assertNotNull(c1);
+		Codec c2 = CodecFactory.createCodec("org.apache.oodt.xmlquery.CodecFactoryTest$TestCodec");
+		assertSame(c1, c2);
+	}
+
+	public static class TestCodec implements Codec {
+		public TestCodec() {}
+		public Node encode(Object object, Document doc) { return null; }
+		public Object decode(Node node) { return null; }
+		public long sizeOf(Object obj) { return 0; }
+		public InputStream getInputStream(Object object) {
+			return new NullInputStream();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CodecTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CodecTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CodecTest.java
new file mode 100755
index 0000000..15e4017
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CodecTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import org.apache.oodt.commons.util.*;
+import junit.framework.*;
+import org.w3c.dom.*;
+
+/** Unit test a codec.
+ *
+ * @author Kelly
+ */ 
+abstract class CodecTest extends TestCase {
+	/** Construct the test case for a codec. */
+	protected CodecTest(String name) {
+		super(name);
+	}
+
+	/** Test the codec. */
+	protected void runTest(Codec codec) throws Exception {
+		// Test encoding and decoding
+		Document doc = XML.createDocument();
+		Node node = codec.encode(getTestObject(), doc);
+		Object object = codec.decode(node);
+		checkEquality(object);
+
+		// Test size computation
+		assertEquals(getTestSize(), codec.sizeOf(getTestObject()));
+	}
+
+	/** Get the test object to encode.
+	 *
+	 * @return The test object.
+	 */
+	protected Object getTestObject() {
+		return TEST_OBJECT;
+	}
+
+	/**
+	 * Get the size of the test object.
+	 *
+	 * @return Size of the test object in bytes.
+	 */
+	protected long getTestSize() {
+		return TEST_SIZE;
+	}
+
+	/** Test the encoded and decoded object for equality with the test object.
+	 *
+	 * @param encodedAndDecoded The encoded and decoded object.
+	 */
+	protected void checkEquality(Object encodedAndDecoded) {
+		assertEquals(getTestObject(), encodedAndDecoded);
+	}
+
+	/** The object we'll encode and decode with the codec. */
+	private static final String TEST_OBJECT = "This is my test object.";
+
+	/** Size of the test object in bytes. */
+	private static final long TEST_SIZE = 23;
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CompressedObjectCodecTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CompressedObjectCodecTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CompressedObjectCodecTest.java
new file mode 100755
index 0000000..ff9d49e
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CompressedObjectCodecTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import org.apache.oodt.xmlquery.CompressedObjectCodec; // Imported for javadoc
+
+/** Unit test the {@link CompressedObjectCodec} class.
+ *
+ * @author Kelly
+ */ 
+public class CompressedObjectCodecTest extends CodecTest {
+	/** Construct the test case for the {@link CompressedObjectCodec} class. */
+	public CompressedObjectCodecTest(String name) {
+		super(name);
+	}
+
+	public void testIt() throws Exception {
+		runTest(CodecFactory.createCodec("org.apache.oodt.xmlquery.CompressedObjectCodec"));
+	}
+
+	public long getTestSize() {
+		// Serialization overhead adds a few bytes, so we override the method here
+		// with this value:
+		return 30;
+	}
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CompressedStringCodecTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CompressedStringCodecTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CompressedStringCodecTest.java
new file mode 100755
index 0000000..e7f8a79
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/CompressedStringCodecTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import org.apache.oodt.xmlquery.CompressedStringCodec; // Imported for Javadoc
+
+
+/** Unit test the {@link CompressedStringCodec} class.
+ *
+ * @author Kelly
+ */ 
+public class CompressedStringCodecTest extends CodecTest {
+	/** Construct the test case for the {@link CompressedStringCodec} class. */
+	public CompressedStringCodecTest(String name) {
+		super(name);
+	}
+
+	public void testIt() throws Exception {
+		runTest(CodecFactory.createCodec("org.apache.oodt.xmlquery.CompressedStringCodec"));
+	}
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/EmptyByteArrayCodecTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/EmptyByteArrayCodecTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/EmptyByteArrayCodecTest.java
new file mode 100755
index 0000000..2d3eee6
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/EmptyByteArrayCodecTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import org.apache.oodt.xmlquery.ByteArrayCodec; // Imported for javadoc
+
+/**
+ * Unit test the {@link ByteArrayCodec} class with an empty byte array.
+ *
+ * @author Kelly
+ */ 
+public class EmptyByteArrayCodecTest extends CodecTest {
+	/** Construct the test case for the {@link ByteArrayCodec} class. */
+	public EmptyByteArrayCodecTest(String name) {
+		super(name);
+	}
+
+	public void testIt() throws Exception {
+		runTest(CodecFactory.createCodec("org.apache.oodt.xmlquery.ByteArrayCodec"));
+	}
+
+	protected Object getTestObject() {
+		return TEST_OBJECT;
+	}
+
+	protected long getTestSize() {
+		return 0;
+	}
+
+	protected void checkEquality(Object encodedAndDecoded) {
+		assertTrue("Empty byte array codec failed", java.util.Arrays.equals(TEST_OBJECT, (byte[]) encodedAndDecoded));
+	}
+
+	private static final byte[] TEST_OBJECT = new byte[0];
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/HeaderTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/HeaderTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/HeaderTest.java
new file mode 100755
index 0000000..94cd9d0
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/HeaderTest.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import java.util.*;
+import org.apache.oodt.commons.util.*;
+import junit.framework.*;
+import org.w3c.dom.*;
+
+/** Unit test the {@link Header} class.
+ *
+ * @author Kelly
+ */ 
+public class HeaderTest extends TestCase {
+	/** Construct the test case for the {@link Header} class. */
+	public HeaderTest(String name) {
+		super(name);
+	}
+
+	public void testNoArgsCtor() {
+		Header blank = new Header();
+		assertEquals("UNKNOWN", blank.getName());
+		assertNull(blank.getType());
+		assertNull(blank.getUnit());
+	}
+
+	public void testSimpleCtor() {
+		Header simple = new Header("name");
+		assertEquals("name", simple.getName());
+		assertNull(simple.getType());
+		assertNull(simple.getUnit());
+	}
+
+	public void testFullCtor() {
+		Header full = new Header("name", "type", "unit");
+		assertEquals("name", full.getName());
+		assertEquals("type", full.getType());
+		assertEquals("unit", full.getUnit());
+	}		
+
+	public void testSetters() {
+		Header h = new Header("name", "type", "unit");
+
+		assertEquals("name", h.getName());
+		h.setName("newName");
+		assertEquals("newName", h.getName());
+
+		assertEquals("type", h.getType());
+		h.setType("newType");
+		assertEquals("newType", h.getType());
+
+		assertEquals("unit", h.getUnit());
+		h.setUnit("newUnit");
+		assertEquals("newUnit", h.getUnit());
+	}
+
+	public void testObjectMethods() {
+		Header h1 = new Header("name1", "type1", "unit1");
+		Header h2 = new Header("name1", "type1", "unit1");
+		Header h3 = new Header("name2", "type2", "unit2");
+		assertEquals(h1, h1);
+		assertEquals(h1, h2);
+		assertTrue(!h1.equals(h3));
+		Header h4 = (Header) h3.clone();
+		assertEquals(h3, h4);
+		assertTrue(h3 != h4);
+	}
+
+	public void testXML() throws Exception {
+		Document doc = XML.createDocument();
+		Element bogus = doc.createElement("bogus");
+		try {
+			Header h0 = new Header(bogus);
+			fail("Header constructor failed to throw exception when given invalid XML node");
+		} catch (IllegalArgumentException ignored) {}
+
+		Header h1 = new Header("name1", "type1", "unit1");
+		Node root = h1.toXML(doc);
+		assertEquals("headerElement", root.getNodeName());
+		NodeList children = root.getChildNodes();
+		for (int i = 0; i < children.getLength(); ++i) {
+			Node child = children.item(i);
+			if ("elemName".equals(child.getNodeName())) {
+				assertEquals("name1", XML.text(child));
+			} else if ("elemType".equals(child.getNodeName())) {
+				assertEquals("type1", XML.text(child));
+			} else if ("elemUnit".equals(child.getNodeName())) {
+				assertEquals("unit1", XML.text(child));
+			} else fail("Unknown node \"" + child.getNodeName() + "\" in XML result");
+		}
+		Header h2 = new Header(root);
+		assertEquals(h1, h2);
+	}
+
+	public void testMultipleHeaders() throws Exception {
+		Document doc = XML.createDocument();
+		Element resultHeader = doc.createElement("resultHeader");
+		Element headerElement = doc.createElement("headerElement");
+		resultHeader.appendChild(headerElement);
+		XML.add(headerElement, "elemName", "name1");
+		headerElement = doc.createElement("headerElement");
+		resultHeader.appendChild(headerElement);
+		XML.add(headerElement, "elemName", "name2");
+		XML.add(headerElement, "elemType", "type2");
+		headerElement = doc.createElement("headerElement");
+		resultHeader.appendChild(headerElement);
+		XML.add(headerElement, "elemName", "name3");
+		XML.add(headerElement, "elemUnit", "unit3");
+		headerElement = doc.createElement("headerElement");
+		resultHeader.appendChild(headerElement);
+		XML.add(headerElement, "elemName", "name4");
+		XML.add(headerElement, "elemType", "type4");
+		XML.add(headerElement, "elemUnit", "unit4");
+
+		List headers = Header.createHeaders(resultHeader);
+		Header h1 = new Header("name1");
+		Header h2 = new Header("name2", "type2", null);
+		Header h3 = new Header("name3", null, "unit3");
+		Header h4 = new Header("name4", "type4", "unit4");
+		assertEquals(h1, headers.get(0));
+		assertEquals(h2, headers.get(1));
+		assertEquals(h3, headers.get(2));
+		assertEquals(h4, headers.get(3));
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/ObjectCodecTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/ObjectCodecTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/ObjectCodecTest.java
new file mode 100755
index 0000000..a997a06
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/ObjectCodecTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import org.apache.oodt.xmlquery.ObjectCodec; // Imported for javadoc
+
+/** Unit test the {@link ObjectCodec} class.
+ *
+ * @author Kelly
+ */ 
+public class ObjectCodecTest extends CodecTest {
+	/** Construct the test case for the {@link ObjectCodec} class. */
+	public ObjectCodecTest(String name) {
+		super(name);
+	}
+
+	public void testIt() throws Exception {
+		runTest(CodecFactory.createCodec("org.apache.oodt.xmlquery.ObjectCodec"));
+	}
+
+	public long getTestSize() {
+		// Serialization overhead adds a few bytes, so we override the method here
+		// with this value:
+		return 30;
+	}
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/QueryElementTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/QueryElementTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/QueryElementTest.java
new file mode 100755
index 0000000..65109d4
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/QueryElementTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import org.apache.oodt.commons.util.*;
+import junit.framework.*;
+import org.w3c.dom.*;
+
+/** Unit test the {@link QueryElement} class.
+ *
+ * @author Kelly
+ */ 
+public class QueryElementTest extends TestCase {
+	/** Construct the test case for the {@link QueryElement} class. */
+	public QueryElementTest(String name) {
+		super(name);
+	}
+
+	public void testNoArgsCtor() {
+		QueryElement blank = new QueryElement();
+		assertEquals("UNKNOWN", blank.getRole());
+		assertEquals("UNKNOWN", blank.getValue());
+	}
+
+	public void testCtor() {
+		QueryElement full = new QueryElement("role", "value");
+		assertEquals("role", full.getRole());
+		assertEquals("value", full.getValue());
+	}		
+
+	public void testSetters() {
+		QueryElement q = new QueryElement("role", "value");
+
+		assertEquals("role", q.getRole());
+		q.setRole("newRole");
+		assertEquals("newRole", q.getRole());
+		q.setRole(null);
+		assertEquals("UNKNOWN", q.getRole());
+
+		assertEquals("value", q.getValue());
+		q.setValue("newValue");
+		assertEquals("newValue", q.getValue());
+		q.setValue(null);
+		assertEquals("UNKNOWN", q.getValue());
+	}
+
+	public void testObjectMethods() {
+		QueryElement q1 = new QueryElement("a", "1");
+		QueryElement q2 = new QueryElement("a", "1");
+		QueryElement q3 = new QueryElement("b", "2");
+		assertEquals(q1, q1);
+		assertEquals(q1, q2);
+		assertTrue(!q1.equals(q3));
+		QueryElement q4 = (QueryElement) q3.clone();
+		assertEquals(q3, q4);
+		assertTrue(q3 != q4);
+	}
+
+	public void testXML() throws Exception {
+		QueryElement q1 = new QueryElement("a", "1");
+		Document doc = XML.createDocument();
+		Node root = q1.toXML(doc);
+		assertEquals("queryElement", root.getNodeName());
+		NodeList children = root.getChildNodes();
+		for (int i = 0; i < children.getLength(); ++i) {
+			Node child = children.item(i);
+			if ("tokenRole".equals(child.getNodeName())) {
+				assertEquals("a", XML.text(child));
+			} else if ("tokenValue".equals(child.getNodeName())) {
+				assertEquals("1", XML.text(child));
+			} else fail("Unknown node \"" + child.getNodeName() + "\" in XML result");
+		}
+		QueryElement q2 = new QueryElement(root);
+		assertEquals(q1, q2);
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/ResultTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/ResultTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/ResultTest.java
new file mode 100755
index 0000000..fd3e8ed
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/ResultTest.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import java.util.*;
+import org.apache.oodt.commons.util.*;
+import junit.framework.*;
+import org.w3c.dom.*;
+
+/** Unit test the {@link Result} class.
+ *
+ * @author Kelly
+ */ 
+public class ResultTest extends TestCase {
+	/** Construct the test case for the {@link Result} class. */
+	public ResultTest(String name) {
+		super(name);
+	}
+
+	public void testNoArgsCtor() {
+		Result blank = new Result();
+		assertEquals("UNKNOWN", blank.getID());
+		assertEquals("UNKNOWN", blank.getMimeType());
+		assertEquals("UNKNOWN", blank.getProfileID());
+		assertEquals("UNKNOWN", blank.getResourceID());
+		assertEquals(0, blank.getHeaders().size());
+		assertEquals("", blank.getValue());
+		assertTrue(!blank.isClassified());
+		assertEquals(Result.INFINITE, blank.getValidity());
+	}
+
+	public void testSimpleCtor() {
+		Result simple = new Result("1", "The Value");
+		assertEquals("1", simple.getID());
+		assertEquals("UNKNOWN", simple.getMimeType());
+		assertEquals("UNKNOWN", simple.getProfileID());
+		assertEquals("UNKNOWN", simple.getResourceID());
+		assertEquals(0, simple.getHeaders().size());
+		assertEquals("The Value", simple.getValue());
+		assertTrue(!simple.isClassified());
+		assertEquals(Result.INFINITE, simple.getValidity());
+	}
+
+	public void testFullCtor() {
+		List headers = new ArrayList();
+		headers.add(new Header("header"));
+		Result full = new Result("1", "text/xml", "edaDataSetInv1", "geeba1", headers,TEST_VALUE, /*classified*/true,
+			/*validity*/12345L);
+		assertEquals("1", full.getID());
+		assertEquals("text/xml", full.getMimeType());
+		assertEquals("edaDataSetInv1", full.getProfileID());
+		assertEquals("geeba1", full.getResourceID());
+		assertEquals(1, full.getHeaders().size());
+		assertEquals(TEST_VALUE, full.getValue());
+		assertEquals(true, full.isClassified());
+		assertEquals(12345L, full.getValidity());
+	}		
+
+	public void testSetters() {
+		Result result = new Result("1", "text/xml", "edaDataSetInv1", "geeba1", new ArrayList(), TEST_VALUE);
+
+		assertEquals("1", result.getID());
+		result.setID("2");
+		assertEquals("2", result.getID());
+
+		assertEquals("text/xml", result.getMimeType());
+		result.setMimeType("text/sgml");
+		assertEquals("text/sgml", result.getMimeType());
+
+		assertEquals("edaDataSetInv1", result.getProfileID());
+		result.setProfileID("ptiDataSet");
+		assertEquals("ptiDataSet", result.getProfileID());
+
+		assertEquals("geeba1", result.getResourceID());
+		result.setResourceID("fish2");
+		assertEquals("fish2", result.getResourceID());
+
+		assertEquals(TEST_VALUE, result.getValue());
+		result.setValue("<hello>world</hello>");
+		assertEquals("<hello>world</hello>", result.getValue());
+
+		assertEquals(false, result.isClassified());
+		result.setClassified(true);
+		assertEquals(true, result.isClassified());
+
+		assertEquals(Result.INFINITE, result.getValidity());
+		result.setValidity(54321L);
+		assertEquals(54321L, result.getValidity());
+	}
+
+	public void testObjectMethods() {
+		Result r1 = new Result("1", "text/xml", "edaDataSetInv1", "geeba1", new ArrayList(), TEST_VALUE);
+		Result r2 = new Result("1", "text/xml", "edaDataSetInv1", "geeba1", new ArrayList(), TEST_VALUE);
+		Result r3 = new Result("2", "text/xml", "edaDataSetInv1", "geeba1", new ArrayList(), TEST_VALUE);
+		assertEquals(r1, r1);
+		assertEquals(r1, r2);
+		assertTrue(!r1.equals(r3));
+		Result r4 = (Result) r3.clone();
+		assertEquals(r3, r4);
+		assertTrue(r3 != r4);
+	}
+
+	public void testXML() throws Exception {
+		Document doc = XML.createDocument();
+		Element bogus = doc.createElement("bogus");
+		try {
+			Result r0 = new Result(bogus);
+			fail("Result constructor failed to throw exception when given invalid XML node");
+		} catch (IllegalArgumentException ignored) {}
+
+		Result r1 = new Result("1", "text/xml", "edaDataSetInv1", "geeba1", new ArrayList(), TEST_VALUE,
+			/*classified*/true, /*validity*/3456789);
+		Node root = r1.toXML(doc);
+		assertEquals("resultElement", root.getNodeName());
+		assertEquals("true", ((Element) root).getAttribute("classified"));
+		assertEquals("3456789", ((Element) root).getAttribute("validity"));
+		NodeList children = root.getChildNodes();
+		for (int i = 0; i < children.getLength(); ++i) {
+			Node child = children.item(i);
+			if ("resultId".equals(child.getNodeName())) {
+				assertEquals("1", XML.text(child));
+			} else if ("resultMimeType".equals(child.getNodeName())) {
+				assertEquals("text/xml", XML.text(child));
+			} else if ("profId".equals(child.getNodeName())) {
+				assertEquals("edaDataSetInv1", XML.text(child));
+			} else if ("identifier".equals(child.getNodeName())) {
+				assertEquals("geeba1", XML.text(child));
+			} else if ("resultHeader".equals(child.getNodeName())) {
+				// ignore, use HeaderTest
+			} else if ("resultValue".equals(child.getNodeName())) {
+				assertEquals(TEST_VALUE, child.getFirstChild().getNodeValue());
+			} else fail("Unknown node \"" + child.getNodeName() + "\" in XML result");
+		}
+		Result r2 = new Result(root);
+		assertEquals(r1, r2);
+	}
+
+	public void testMimeTypes() {
+		try {
+			Result r = new Result("1", "invalid/mime.type", "", "", new ArrayList(), "");
+		} catch (IllegalArgumentException ex) {
+			// Good.
+			return;
+		}
+		fail("Result constructor failed to throw IllegalArgumentException for invalid mime type");
+	}
+
+	private static final String TEST_VALUE = "<?xml version='1.0' encoding='UTF-8'?>\n<test>value</test>";
+}
+

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/StringCodecTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/StringCodecTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/StringCodecTest.java
new file mode 100755
index 0000000..b869c45
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/StringCodecTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import org.apache.oodt.xmlquery.StringCodec; // Imported for javadoc
+
+
+/** Unit test the {@link StringCodec} class.
+ *
+ * @author Kelly
+ */ 
+public class StringCodecTest extends CodecTest {
+	/** Construct the test case for the {@link StringCodec} class. */
+	public StringCodecTest(String name) {
+		super(name);
+	}
+
+	public void testIt() throws Exception {
+		runTest(CodecFactory.createCodec("org.apache.oodt.xmlquery.StringCodec"));
+	}
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/XMLQueryTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/XMLQueryTest.java b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/XMLQueryTest.java
new file mode 100755
index 0000000..ee90684
--- /dev/null
+++ b/webapp/fmprod/src/test/java/org/apache/oodt/xmlquery/XMLQueryTest.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import java.util.*;
+import org.apache.oodt.commons.util.*;
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+/** Unit test the {@link XMLQuery} class.
+ *
+ * @author Kelly
+ */ 
+public class XMLQueryTest extends org.apache.oodt.commons.ConfiguredTestCase {
+	/** Construct the test case for the {@link XMLQuery} class. */
+	public XMLQueryTest(String name) {
+		super(name);
+	}
+
+	public void testCtor() {
+		List mimes = new ArrayList();
+		mimes.add("text/plain");
+		mimes.add("image/jpeg");
+		XMLQuery q = new XMLQuery("profStatusId = UNKNOWN OR A > 3 AND RETURN = C",
+			"id", "title", "description", "dataDictID", "resultModeID", "propType", "propLevels", 45, mimes);
+		assertEquals("profStatusId = UNKNOWN OR A > 3 AND RETURN = C", q.getKwdQueryString());
+		assertEquals(45, q.getMaxResults());
+		assertEquals(1, q.getSelectElementSet().size());
+		assertEquals(new QueryElement("elemName", "C"), q.getSelectElementSet().get(0));
+		assertEquals(0, q.getFromElementSet().size());
+		assertEquals(6, q.getWhereElementSet().size());
+		assertEquals(new QueryElement("elemName", "profStatusId"), q.getWhereElementSet().get(0));
+		assertEquals(new QueryElement("LITERAL", "UNKNOWN"), q.getWhereElementSet().get(1));
+		assertEquals(new QueryElement("RELOP", "EQ"), q.getWhereElementSet().get(2));
+		assertEquals(new QueryElement("elemName", "A"), q.getWhereElementSet().get(3));
+		assertEquals(new QueryElement("LITERAL", "3"), q.getWhereElementSet().get(4));
+		assertEquals(new QueryElement("RELOP", "GT"), q.getWhereElementSet().get(5));
+		// Need some testing of expressions with LOGOP's, but NOT handling seems broken.
+		assertEquals(0, q.getResults().size());
+		assertEquals(2, q.getMimeAccept().size());
+	}		
+	
+	public void testObjectMethods() {
+		XMLQuery q1 = new XMLQuery("Subject < Phrenology OR A > 3 AND RETURN = C",
+			"id", "title", "description", "dataDictID", "resultModeID", "propType", "propLevels", 45);
+		XMLQuery q2 = new XMLQuery("Subject < Phrenology OR A > 3 AND RETURN = C",
+			"id", "title", "description", "dataDictID", "resultModeID", "propType", "propLevels", 45);
+		XMLQuery q3 = new XMLQuery("Subject > Phrenology OR A < 3 AND RETURN = D",
+			"id", "title", "description", "dataDictID", "resultModeID", "propType", "propLevels", 45);
+		assertEquals(q1, q1);
+		assertEquals(q1, q2);
+		assertTrue(!q1.equals(q3));
+		XMLQuery q4 = (XMLQuery) q3.clone();
+		assertEquals(q3, q4);
+		assertTrue(q3 != q4);
+	}
+
+	public void testParser() {
+		XMLQuery q1 = new XMLQuery("(A < 1 AND A > 2) AND RETURN = B", "id", "title", "description", "dataDictID",
+			"resultModeID", "propType", "propLevels", 45);
+		List where = q1.getWhereElementSet();
+		assertEquals(7, where.size());
+
+		QueryElement qe;
+
+		qe = (QueryElement) where.get(0);
+		assertEquals("elemName", qe.getRole());
+		assertEquals("A", qe.getValue());
+
+		qe = (QueryElement) where.get(1);
+		assertEquals("LITERAL", qe.getRole());
+		assertEquals("1", qe.getValue());
+
+		qe = (QueryElement) where.get(2);
+		assertEquals("RELOP", qe.getRole());
+		assertEquals("LT", qe.getValue());
+
+		qe = (QueryElement) where.get(3);
+		assertEquals("elemName", qe.getRole());
+		assertEquals("A", qe.getValue());
+
+		qe = (QueryElement) where.get(4);
+		assertEquals("LITERAL", qe.getRole());
+		assertEquals("2", qe.getValue());
+
+		qe = (QueryElement) where.get(5);
+		assertEquals("RELOP", qe.getRole());
+		assertEquals("GT", qe.getValue());
+
+		qe = (QueryElement) where.get(6);
+		assertEquals("LOGOP", qe.getRole());
+		assertEquals("AND", qe.getValue());
+
+		List select = q1.getSelectElementSet();
+		assertEquals(1, select.size());
+		
+		qe = (QueryElement) select.get(0);
+		assertEquals("elemName", qe.getRole());
+		assertEquals("B", qe.getValue());
+	}
+
+	public void testXML() {
+		NodeList children;
+
+		List mimes = new ArrayList();
+		mimes.add("text/xml");
+		mimes.add("image/gif");
+		XMLQuery q1 = new XMLQuery("Subject < Phrenology OR A > 3 AND RETURN = C",
+			"id", "title", "description", "dataDictID", "resultModeID", "propType", "propLevels", 45, mimes);
+		Document doc = q1.getXMLDoc();
+		Node root = doc.getDocumentElement();
+		assertEquals("query", root.getNodeName());
+
+		Node queryAttributes = root.getFirstChild();
+		assertEquals("queryAttributes", queryAttributes.getNodeName());
+		children = queryAttributes.getChildNodes();
+		for (int i = 0; i < children.getLength(); ++i) {
+			Node child = children.item(i);
+			String name = child.getNodeName();
+			String text = XML.text(child);
+			if ("queryId".equals(name)) {
+				assertEquals("id", text);
+			} else if ("queryTitle".equals(name)) {
+				assertEquals("title", text);
+			} else if ("queryDesc".equals(name)) {
+				assertEquals("description", text);
+			} else if ("queryType".equals(name)) {
+				assertEquals("QUERY", text);
+			} else if ("queryStatusId".equals(name)) {
+				assertEquals("ACTIVE", text);
+			} else if ("querySecurityType".equals(name)) {
+				assertEquals("UNKNOWN", text);
+			} else if ("queryRevisionNote".equals(name)) {
+				assertEquals("1999-12-12 JSH V1.0 Under Development", text);
+			} else if ("queryDataDictId".equals(name)) {
+				assertEquals("dataDictID", text);
+			} else fail("Unknown node <" + name + "> under <queryAttributes>");
+		}
+
+		Node queryResultMode = queryAttributes.getNextSibling();
+		assertEquals("queryResultModeId", queryResultMode.getNodeName());
+		assertEquals("resultModeID", XML.text(queryResultMode));
+
+		Node propogationType = queryResultMode.getNextSibling();
+		assertEquals("queryPropogationType", propogationType.getNodeName());
+		assertEquals("propType", XML.text(propogationType));
+
+		Node propogationLevels = propogationType.getNextSibling();
+		assertEquals("queryPropogationLevels", propogationLevels.getNodeName());
+		assertEquals("propLevels", XML.text(propogationLevels));
+
+		Node mimeNode = propogationLevels.getNextSibling();
+		assertEquals("queryMimeAccept", mimeNode.getNodeName());
+		assertEquals("text/xml", XML.text(mimeNode));
+
+		mimeNode = mimeNode.getNextSibling();
+		assertEquals("queryMimeAccept", mimeNode.getNodeName());
+		assertEquals("image/gif", XML.text(mimeNode));		
+
+		Node maxResults = mimeNode.getNextSibling();
+		assertEquals("queryMaxResults", maxResults.getNodeName());
+		assertEquals("45", XML.text(maxResults));
+
+		Node results = maxResults.getNextSibling();
+		assertEquals("queryResults", results.getNodeName());
+		assertEquals("0", XML.text(results));
+
+		Node kwqString = results.getNextSibling();
+		assertEquals("queryKWQString", kwqString.getNodeName());
+		assertEquals("Subject < Phrenology OR A > 3 AND RETURN = C", XML.text(kwqString));
+
+		Node node = kwqString.getNextSibling();
+		assertEquals("queryStatistics", node.getNodeName());
+		node = node.getNextSibling();
+		assertEquals("querySelectSet", node.getNodeName());
+		node = node.getNextSibling();
+		assertEquals("queryFromSet", node.getNodeName());
+		node = node.getNextSibling();
+		assertEquals("queryWhereSet", node.getNodeName());
+		node = node.getNextSibling();
+		assertEquals("queryResultSet", node.getNodeName());
+		assertNull(node.getNextSibling());
+
+		XMLQuery q2 = new XMLQuery(root);
+		assertEquals(q1, q2);
+	}
+
+	/**
+	 * Test if we can parse an XML query document even with an inaccessible system ID.
+	 *
+	 * @throws SAXException if an error occurs.
+	 */
+	public void testXMLEntityResolution() throws SAXException {
+		new XMLQuery(BAD_HOST);
+	}
+
+	private static String BAD_HOST = "<!DOCTYPE query PUBLIC '-//JPL//DTD OODT Query 1.0//EN' "
+		+ "'http://unknown-host.unk/edm-query/query.dtd'>\n<query><queryAttributes><queryId>queryServlet</queryId>"
+		+ "<queryTitle>QueryfromQueryServlet</queryTitle><queryDesc>Bad host name in system ID</queryDesc><queryType>"
+		+ "QUERY</queryType><queryStatusId>ACTIVE</queryStatusId><querySecurityType>UNKNOWN</querySecurityType>"
+		+ "<queryRevisionNote>1999-12-12JSHV1.0UnderDevelopment</queryRevisionNote><queryDataDictId>UNKNOWN"
+		+ "</queryDataDictId></queryAttributes><queryResultModeId>ATTRIBUTE</queryResultModeId><queryPropogationType>"
+		+ "BROADCAST</queryPropogationType><queryPropogationLevels>N/A</queryPropogationLevels><queryMimeAccept>*/*"
+		+ "</queryMimeAccept><queryMaxResults>100</queryMaxResults><queryResults>0</queryResults><queryKWQString>"
+		+ "RETURN = SPECIMEN_COLLECTED_CODE</queryKWQString><queryStatistics/><querySelectSet><queryElement>"
+		+ "<tokenRole>elemName</tokenRole><tokenValue>SPECIMEN_COLLECTED_CODE</tokenValue></queryElement></querySelectSet>"
+		+ "<queryFromSet/><queryWhereSet/><queryResultSet/></query>";
+
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/org/apache/oodt/product/handlers/ofsn/util/OFSNUtilsTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/org/apache/oodt/product/handlers/ofsn/util/OFSNUtilsTest.java b/webapp/fmprod/src/test/org/apache/oodt/product/handlers/ofsn/util/OFSNUtilsTest.java
new file mode 100644
index 0000000..31a4494
--- /dev/null
+++ b/webapp/fmprod/src/test/org/apache/oodt/product/handlers/ofsn/util/OFSNUtilsTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.product.handlers.ofsn.util;
+
+import junit.framework.TestCase;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Collections;
+
+/**
+ * Unit test for {@link OFSNUtils}.
+ *
+ * @author riverma
+ */
+public class OFSNUtilsTest extends TestCase {
+    public OFSNUtilsTest(String id) {
+        super(id);
+    }
+
+    public void testValidateOFSN() {
+        
+        assertTrue(OFSNUtils.validateOFSN("/dataset/dir1"));
+        assertTrue(OFSNUtils.validateOFSN("/dataset/dir1/"));
+        assertTrue(OFSNUtils.validateOFSN("/dataset/dir1/file1.h5"));
+        assertFalse(OFSNUtils.validateOFSN("/dataset/../../../../../../etc/passwd"));
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/org/apache/oodt/xmlquery/ChunkedProductInputStreamTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/org/apache/oodt/xmlquery/ChunkedProductInputStreamTest.java b/webapp/fmprod/src/test/org/apache/oodt/xmlquery/ChunkedProductInputStreamTest.java
new file mode 100644
index 0000000..9ab531a
--- /dev/null
+++ b/webapp/fmprod/src/test/org/apache/oodt/xmlquery/ChunkedProductInputStreamTest.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import java.io.IOException;
+import java.util.Arrays;
+import org.apache.oodt.product.ProductException;
+import org.apache.oodt.product.Retriever;
+import junit.framework.TestCase;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Unit test for <code>ChunkedProductInputStream</code>.
+ *
+ * @author Kelly
+ * @version $Revision: 1.5 $
+ */
+public class ChunkedProductInputStreamTest extends TestCase implements Retriever {
+	/**
+	 * Creates a new <code>ChunkedProductInputStreamTest</code> instance.
+	 *
+	 * @param id Case name.
+	 */
+	public ChunkedProductInputStreamTest(String id) {
+		super(id);
+	}
+
+	public void setUp() throws Exception {
+		super.setUp();
+		data = new byte[4096];
+		for (int i = 0; i < 4096; ++i)
+			data[i] = (byte) (i % 256);
+	}
+
+	/**
+	 * Test reading a single byte at a time.
+	 *
+	 * @throws IOException if an error occurs.
+	 */
+ 	public void testByteReading() throws IOException {
+ 		ChunkedProductInputStream in = new ChunkedProductInputStream("test", this, 4096);
+ 		for (int i = 0; i < 4096; ++i)
+ 			assertEquals(toByte(i % 256), toByte(in.read() & 0xff));
+ 		assertEquals(-1, in.read());
+ 		in.close();
+ 	}
+
+	public void testArrayReading() throws IOException {
+		ChunkedProductInputStream in = new ChunkedProductInputStream("test", this, 4096);
+		ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
+		byte[] buf = new byte[256];
+		int num;
+		while ((num = in.read(buf)) != -1)
+			out.write(buf, 0, num);
+		in.close();
+		out.close();
+		assertTrue(Arrays.equals(data, out.toByteArray()));
+	}
+
+	/**
+	 * Test reading and skipping by various amounts.
+	 *
+	 * @throws IOException if an error occurs.
+	 */
+	public void testReadingAndSkipping() throws IOException {
+		ChunkedProductInputStream in = new ChunkedProductInputStream("test", this, 4096);
+		
+		byte[] buf = new byte[4];							    // Byte number:
+		assertEquals(0, in.read());							    // 0
+		assertEquals(0, in.skip(0));							    // 0
+		assertEquals(4, in.read(buf));							    // 1, 2, 3, 4
+		assertEquals(toByte(1), buf[0]);
+		assertEquals(toByte(2), buf[1]);
+		assertEquals(toByte(3), buf[2]);
+		assertEquals(toByte(4), buf[3]);
+		assertEquals(toByte(5), toByte(in.read()));					    // 5
+		assertEquals(4, in.skip(4));							    // 6, 7, 8, 9
+		assertEquals(toByte(10), toByte(in.read()));					    // 10
+		assertEquals(1000, in.skip(1000));						    // 11, 12, ..., 1010
+		assertEquals(toByte(1011 % 256), toByte(in.read()));				    // 1011
+
+		buf = new byte[1000];
+		int toRead = 1000;
+		int index = 0;
+		while (toRead > 0) {								    // 1012, 1013, ..., 2011
+			int numRead = in.read(buf, index, toRead);
+			if (numRead == -1)
+				fail("Premature EOF");
+			toRead -= numRead;
+			index += numRead;
+		}
+		for (int i = 0; i < buf.length; ++i)
+			assertEquals(data[i + 1012], buf[i]);
+
+		assertEquals(2, in.read(buf, 1, 2));						    // 2012, 2013
+		assertEquals(toByte(1012 % 256), buf[0]);
+		assertEquals(toByte(2012 % 256), buf[1]);
+		assertEquals(toByte(2013 % 256), buf[2]);
+		assertEquals(toByte(1015 % 256), buf[3]);
+
+		assertEquals(2082, in.skip(2083));						    // 2014, 2015, ..., 4095
+		// Shouldn't we get the -1 read first, and THEN get an IOException on subsequent reads?
+		try {
+			assertEquals(-1, in.read());
+		} catch (IOException ignore) {}
+		in.close();
+	}
+
+	/**
+	 * Test reading into larger and larger arrays.
+	 *
+	 * @throws IOException if an error occurs.
+	 */
+	public void testWideningWindows() throws IOException {
+		// Scary; this test hangs on Windows.  We really should investigate why as
+		// it could bite us on the bum in the future.
+		if (System.getProperty("os.name", "unknown").indexOf("Windows") != -1) return;
+
+		byte[] read = new byte[4096];
+		for (int size = 1; size <= 4096; size *= 2) {
+			byte[] buf = new byte[size];
+			ChunkedProductInputStream in = new ChunkedProductInputStream("test", this, 4096);
+			ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
+			int num;
+			while ((num = in.read(buf)) != -1)
+				out.write(buf, 0, num);
+			in.close();
+			out.close();
+			assertTrue(Arrays.equals(data, out.toByteArray()));
+		}
+	}
+
+	public byte[] retrieveChunk(String id, long offset, int length) {
+		if (!id.equals("test"))
+			throw new IllegalArgumentException("Unknown id " + id);
+		if (offset < 0 || offset > 4096 || length < 0 ||
+			(offset + length) > 4096 || (offset + length) < 0)
+			throw new IllegalArgumentException("Bad offset and/or length");
+		
+		int index = (int) offset;
+		byte[] sub = new byte[length];
+		System.arraycopy(data, index, sub, 0, length);
+		return sub;
+	}
+
+	private static byte toByte(int b) {
+		return (byte) (b & 0xff);
+	}
+
+	public void close(String id) {}
+
+	/** Test data. */
+	private byte[] data;
+}

http://git-wip-us.apache.org/repos/asf/oodt/blob/4066b63b/webapp/fmprod/src/test/org/apache/oodt/xmlquery/LargeResultTest.java
----------------------------------------------------------------------
diff --git a/webapp/fmprod/src/test/org/apache/oodt/xmlquery/LargeResultTest.java b/webapp/fmprod/src/test/org/apache/oodt/xmlquery/LargeResultTest.java
new file mode 100644
index 0000000..ff3679b
--- /dev/null
+++ b/webapp/fmprod/src/test/org/apache/oodt/xmlquery/LargeResultTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.oodt.xmlquery;
+
+import junit.framework.TestCase;
+import java.util.Collections;
+
+/**
+ * Unit test for {@link LargeResult}.
+ *
+ * @author Kelly
+ * @version $Revision: 1.2 $
+ */
+public class LargeResultTest extends TestCase {
+	public LargeResultTest(String id) {
+		super(id);
+	}
+
+	public void testLargeResults() {
+		LargeResult lr1 = new LargeResult("1.2.3", "text/plain", "JPL.Profile", "JPL.Resource", Collections.EMPTY_LIST, 1);
+		assertEquals("1.2.3", lr1.getID());
+		assertEquals("text/plain", lr1.getMimeType());
+		assertEquals("JPL.Profile", lr1.getProfileID());
+		assertEquals("JPL.Resource", lr1.getResourceID());
+		assertEquals(1, lr1.getSize());
+		assertTrue(lr1.getHeaders().isEmpty());
+
+		Result r = new Result("2.3.4", "application/vnd.jpl.large-product", "JPL.Profile", "JPL.Resource",
+			Collections.EMPTY_LIST, "text/plain 2");
+		LargeResult lr2 = new LargeResult(r);
+		assertEquals("text/plain", lr2.getMimeType());
+		assertEquals(2, lr2.getSize());
+
+		LargeResult lr3 = new LargeResult(lr2);
+		assertEquals("text/plain", lr2.getMimeType());
+		assertEquals(2, lr3.getSize());
+	}
+}


Mime
View raw message