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="[email protected]">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());
+       }
+}

Reply via email to