Index: src/edu/ucsb/nceas/metacat/client/MetacatClient.java
===================================================================
--- src/edu/ucsb/nceas/metacat/client/MetacatClient.java	(revision 3284)
+++ src/edu/ucsb/nceas/metacat/client/MetacatClient.java	(revision 3285)
@@ -24,6 +24,12 @@
 
 package edu.ucsb.nceas.metacat.client;
 
+import com.oreilly.servlet.multipart.FilePart;
+import com.oreilly.servlet.multipart.MultipartParser;
+import com.oreilly.servlet.multipart.ParamPart;
+import com.oreilly.servlet.multipart.Part;
+import edu.ucsb.nceas.metacat.MetaCatUtil;
+import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -33,8 +39,15 @@
 import java.io.StringWriter;
 import java.io.Reader;
 import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Properties;
 import java.util.Vector;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
 
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
@@ -43,6 +56,8 @@
 import edu.ucsb.nceas.utilities.IOUtil;
 import edu.ucsb.nceas.utilities.XMLUtilities;
 import java.io.File;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
 
 
 /**
@@ -50,24 +65,32 @@
  *  Metacat server, and then querying, reading, transforming, inserting,
  *  updating and deleting documents from that server.
  */
-public class MetacatClient implements Metacat
-{
+public class MetacatClient implements Metacat {
     /** The URL string for the metacat server */
     private String metacatUrl;
-
+    
     /** The session identifier for the session */
     private String sessionId;
-
+    
+    public static final String              FGDC_SYSTEM_ID = "http://www.fgdc.gov/metadata/fgdc-std-001-1998.dtd";
+    private static XPath                    xpath = XPathFactory.newInstance().newXPath();
+    private Document                        loginResponse = null, metadataDoc = null;
+    private String                          user = null;
+    
     /**
+     * The Login cookie name.
+     */
+    public final static String                    LOGIN_COOOKIE = "cookie";
+    
+    /**
      * Constructor to create a new instance. Protected because instances
      * should only be created by the factory MetacatFactory.
      */
-    protected MetacatClient()
-    {
+    protected MetacatClient() {
         this.metacatUrl = null;
         this.sessionId = null;
     }
-
+    
     /**
      *  Method used to log in to a metacat server. Implementations will need
      *  to cache a cookie value to make the session persistent.  Each time a
@@ -81,14 +104,13 @@
      *                    not be authenticated
      */
     public String login(String username, String password)
-           throws MetacatAuthException, MetacatInaccessibleException
-    {
+    throws MetacatAuthException, MetacatInaccessibleException {
         Properties prop = new Properties();
         prop.put("action", "login");
         prop.put("qformat", "xml");
         prop.put("username", username);
         prop.put("password", password);
-
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
@@ -95,7 +117,7 @@
         } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
         }
-
+        
         if (response.indexOf("<login>") == -1) {
             setSessionId("");
             throw new MetacatAuthException(response);
@@ -121,12 +143,11 @@
      *  @throws MetacatAuthException when the username/password could
      *                    not be authenticated
      */
-    public String getloggedinuserinfo() throws MetacatInaccessibleException
-    {
+    public String getloggedinuserinfo() throws MetacatInaccessibleException {
         Properties prop = new Properties();
         prop.put("action", "getloggedinuserinfo");
         prop.put("qformat", "xml");
-
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
@@ -133,10 +154,10 @@
         } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
         }
-
+        
         return response;
     }
-
+    
     /**
      *  Method used to log out a metacat server. The Metacat server will end
      *  the session when this call is invoked.
@@ -145,12 +166,11 @@
      *  @throws MetacatInaccessibleException when the metacat server can not be
      *                                    reached or does not respond
      */
-    public String logout() throws MetacatInaccessibleException, MetacatException
-    {
+    public String logout() throws MetacatInaccessibleException, MetacatException {
         Properties prop = new Properties();
         prop.put("action", "logout");
         prop.put("qformat", "xml");
-
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
@@ -157,7 +177,7 @@
         } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
         }
-
+        
         if (response.indexOf("<logout>") == -1) {
             throw new MetacatException(response);
         }
@@ -164,7 +184,7 @@
         setSessionId("");
         return response;
     }
-
+    
     /**
      * Read an XML document from the metacat server session, accessed by docid,
      * and returned as a Reader.
@@ -178,10 +198,9 @@
      * @throws MetacatException when the metacat server generates another error
      */
     public Reader read(String docid) throws InsufficientKarmaException,
-        MetacatInaccessibleException, MetacatException, DocumentNotFoundException
-    {
+            MetacatInaccessibleException, MetacatException, DocumentNotFoundException {
         PushbackReader pbr = null;
-
+        
         Properties prop = new Properties();
         prop.put("action", "read");
         prop.put("qformat", "xml");
@@ -217,63 +236,62 @@
         }
         return pbr;
     }
-
-
+    
+    
     /**
-        * Read inline data from the metacat server session, accessed by
-        * inlinedataid and returned as a Reader.
-        *
-        * @param inlinedataid the identifier of the data to be read
-        * @return a Reader for accessing the document
-        * @throws InsufficientKarmaException when the user has insufficent rights
-        *                                    for the operation
-        * @throws MetacatInaccessibleException when the metacat server can not be
-        *                                    reached or does not respond
-        * @throws MetacatException when the metacat server generates another error
-        */
-       public Reader readInlineData(String inlinedataid)
-           throws InsufficientKarmaException,
-           MetacatInaccessibleException, MetacatException
-       {
-           PushbackReader pbr = null;
-
-           Properties prop = new Properties();
-           prop.put("action", "readinlinedata");
-           prop.put("inlinedataid", inlinedataid);
-
-           InputStream response = null;
-           try {
-               response = sendData(prop, null, null, 0);
-           } catch (Exception e) {
-               throw new MetacatInaccessibleException(e.getMessage());
-           }
-
-           pbr = new PushbackReader(new InputStreamReader(response), 512);
-           try {
-               char[] characters = new char[512];
-               int len = pbr.read(characters, 0, 512);
-               StringWriter sw = new StringWriter();
-               sw.write(characters, 0, len);
-               String message = sw.toString();
-               sw.close();
-               pbr.unread(characters, 0, len);
-
-               if (message.indexOf("<error>") != -1) {
-                   if (message.indexOf("does not have permission") != -1) {
-                       throw new InsufficientKarmaException(message);
-                   } else {
-                       throw new MetacatException(message);
-                   }
-               }
-           } catch (IOException ioe) {
-               throw new MetacatException(
-                       "MetacatClient: Error converting Reader to String."
-                       + ioe.getMessage());
-           }
-
-           return pbr;
-       }
-
+     * Read inline data from the metacat server session, accessed by
+     * inlinedataid and returned as a Reader.
+     *
+     * @param inlinedataid the identifier of the data to be read
+     * @return a Reader for accessing the document
+     * @throws InsufficientKarmaException when the user has insufficent rights
+     *                                    for the operation
+     * @throws MetacatInaccessibleException when the metacat server can not be
+     *                                    reached or does not respond
+     * @throws MetacatException when the metacat server generates another error
+     */
+    public Reader readInlineData(String inlinedataid)
+    throws InsufficientKarmaException,
+            MetacatInaccessibleException, MetacatException {
+        PushbackReader pbr = null;
+        
+        Properties prop = new Properties();
+        prop.put("action", "readinlinedata");
+        prop.put("inlinedataid", inlinedataid);
+        
+        InputStream response = null;
+        try {
+            response = sendData(prop, null, null, 0);
+        } catch (Exception e) {
+            throw new MetacatInaccessibleException(e.getMessage());
+        }
+        
+        pbr = new PushbackReader(new InputStreamReader(response), 512);
+        try {
+            char[] characters = new char[512];
+            int len = pbr.read(characters, 0, 512);
+            StringWriter sw = new StringWriter();
+            sw.write(characters, 0, len);
+            String message = sw.toString();
+            sw.close();
+            pbr.unread(characters, 0, len);
+            
+            if (message.indexOf("<error>") != -1) {
+                if (message.indexOf("does not have permission") != -1) {
+                    throw new InsufficientKarmaException(message);
+                } else {
+                    throw new MetacatException(message);
+                }
+            }
+        } catch (IOException ioe) {
+            throw new MetacatException(
+                    "MetacatClient: Error converting Reader to String."
+                    + ioe.getMessage());
+        }
+        
+        return pbr;
+    }
+    
     /**
      * Query the metacat document store with the given metacat-compatible
      * query document, and return the result set as a Reader.
@@ -282,22 +300,21 @@
      * @return a Reader for accessing the result set
      */
     public Reader query(Reader xmlQuery) throws MetacatInaccessibleException,
-                                                IOException
-    {
+            IOException {
         Reader reader = null;
         String query = null;
         try {
-          query = IOUtil.getAsString(xmlQuery, true);
+            query = IOUtil.getAsString(xmlQuery, true);
         } catch (IOException ioE) {
-          throw ioE;
+            throw ioE;
         }
-
+        
         //set up properties
         Properties prop = new Properties();
         prop.put("action", "squery");
         prop.put("qformat", "xml");
         prop.put("query", query);
-
+        
         InputStream response = null;
         try {
             response = sendData(prop, null, null, 0);
@@ -307,7 +324,7 @@
         reader = new InputStreamReader(response);
         return reader;
     }
-
+    
     /**
      * Insert an XML document into the repository.
      *
@@ -324,21 +341,20 @@
      * @throws IOException when there is an error reading the xml document
      */
     public String insert(String docid, Reader xmlDocument, Reader schema)
-        throws InsufficientKarmaException, MetacatException, IOException,
-        MetacatInaccessibleException
-    {
+    throws InsufficientKarmaException, MetacatException, IOException,
+            MetacatInaccessibleException {
         Reader reader = null;
         String doctext = null;
         String schematext = null;
         try {
-          doctext = IOUtil.getAsString(xmlDocument, true);
-          if (schema != null) {
-              schematext = IOUtil.getAsString(schema, true);
-          }
+            doctext = IOUtil.getAsString(xmlDocument, true);
+            if (schema != null) {
+                schematext = IOUtil.getAsString(schema, true);
+            }
         } catch (IOException ioE) {
-          throw ioE;
+            throw ioE;
         }
-
+        
         //set up properties
         Properties prop = new Properties();
         prop.put("action", "insert");
@@ -347,7 +363,7 @@
         if (schematext != null) {
             prop.put("dtdtext", schematext);
         }
-
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
@@ -354,7 +370,7 @@
         } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
         }
-
+        
         // Check for an error condition
         if (response.indexOf("<error>") != -1) {
             if (response.indexOf("does not have permission") != -1) {
@@ -363,10 +379,10 @@
                 throw new MetacatException(response);
             }
         }
-
+        
         return response;
     }
-
+    
     /**
      * Update an XML document in the repository.
      *
@@ -383,21 +399,20 @@
      * @throws IOException when there is an error reading the xml document
      */
     public String update(String docid, Reader xmlDocument, Reader schema)
-        throws InsufficientKarmaException, MetacatException, IOException,
-        MetacatInaccessibleException
-    {
+    throws InsufficientKarmaException, MetacatException, IOException,
+            MetacatInaccessibleException {
         Reader reader = null;
         String doctext = null;
         String schematext = null;
         try {
-          doctext = IOUtil.getAsString(xmlDocument, true);
-          if (schema != null) {
-              schematext = IOUtil.getAsString(schema, true);
-          }
+            doctext = IOUtil.getAsString(xmlDocument, true);
+            if (schema != null) {
+                schematext = IOUtil.getAsString(schema, true);
+            }
         } catch (IOException ioE) {
-          throw ioE;
+            throw ioE;
         }
-
+        
         //set up properties
         Properties prop = new Properties();
         prop.put("action", "update");
@@ -406,7 +421,7 @@
         if (schematext != null) {
             prop.put("dtdtext", schematext);
         }
-
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
@@ -413,7 +428,7 @@
         } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
         }
-
+        
         // Check for an error condition
         if (response.indexOf("<error>") != -1) {
             if (response.indexOf("does not have permission") != -1) {
@@ -422,107 +437,106 @@
                 throw new MetacatException(response);
             }
         }
-
+        
         return response;
     }
-
+    
     /**
-       * Upload a data document into the repository.
-       *
-       * @param docid the docid to insert the document
-       * @param document a Reader for accessing the document to be uploaded
-       * @return the metacat response message
-       * @throws InsufficientKarmaException when the user has insufficent rights
-       *                                    for the operation
-       * @throws MetacatInaccessibleException when the metacat server can not be
-       *                                    reached or does not respond
-       * @throws MetacatException when the metacat server generates another error
-       * @throws IOException when there is an error reading the xml document
-       */
-      public String upload(String docid, File file)
-          throws InsufficientKarmaException, MetacatException, IOException,
-          MetacatInaccessibleException
-      {
-
-          URL url = new URL(metacatUrl.trim());
-          HttpMessage msg = new HttpMessage(url);
-          //set up properties
-          Properties arg = new Properties();
-          arg.put("action", "upload");
-          arg.put("docid", docid);
-
-          Properties filenames = new Properties();
-          String filename = file.getAbsolutePath();
-          filenames.put("datafile", filename);
-
-          String response = null;
-          try {
+     * Upload a data document into the repository.
+     *
+     * @param docid the docid to insert the document
+     * @param document a Reader for accessing the document to be uploaded
+     * @return the metacat response message
+     * @throws InsufficientKarmaException when the user has insufficent rights
+     *                                    for the operation
+     * @throws MetacatInaccessibleException when the metacat server can not be
+     *                                    reached or does not respond
+     * @throws MetacatException when the metacat server generates another error
+     * @throws IOException when there is an error reading the xml document
+     */
+    public String upload(String docid, File file)
+    throws InsufficientKarmaException, MetacatException, IOException,
+            MetacatInaccessibleException {
+        
+        URL url = new URL(metacatUrl.trim());
+        HttpMessage msg = new HttpMessage(url);
+        //set up properties
+        Properties arg = new Properties();
+        arg.put("action", "upload");
+        arg.put("docid", docid);
+        
+        Properties filenames = new Properties();
+        String filename = file.getAbsolutePath();
+        filenames.put("datafile", filename);
+        
+        String response = null;
+        try {
             response = sendDataForString(arg, filenames, null, 0);
-          } catch (Exception e) {
+        } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
-          }
-
-          // Check for an error condition
-          if (response.indexOf("<error>") != -1) {
+        }
+        
+        // Check for an error condition
+        if (response.indexOf("<error>") != -1) {
             if (response.indexOf("does not have permission") != -1) {
-              throw new InsufficientKarmaException(response);
+                throw new InsufficientKarmaException(response);
             } else {
-              throw new MetacatException(response);
+                throw new MetacatException(response);
             }
-          }
-
-          return response;
-      }
-
-        /**
-          * Upload a data document into the repository.
-          *
-          * @param docid the docid to insert the document
-          * @param document a Reader for accessing the document to be uploaded
-          * @return the metacat response message
-          * @throws InsufficientKarmaException when the user has insufficent rights
-          *                                    for the operation
-          * @throws MetacatInaccessibleException when the metacat server can not be
-          *                                    reached or does not respond
-          * @throws MetacatException when the metacat server generates another error
-          * @throws IOException when there is an error reading the xml document
-          */
-
-
-      public String upload(String docid, String filename, InputStream fileData,
-                           int size)
-          throws InsufficientKarmaException, MetacatException, IOException,
-          MetacatInaccessibleException {
-
-          URL url = new URL(metacatUrl.trim());
-          HttpMessage msg = new HttpMessage(url);
-          //set up properties
-          Properties arg = new Properties();
-          arg.put("action", "upload");
-          arg.put("docid", docid);
-
-          Properties filenames = new Properties();
-          filenames.put("datafile", filename);
-
-          String response = null;
-          try {
+        }
+        
+        return response;
+    }
+    
+    /**
+     * Upload a data document into the repository.
+     *
+     * @param docid the docid to insert the document
+     * @param document a Reader for accessing the document to be uploaded
+     * @return the metacat response message
+     * @throws InsufficientKarmaException when the user has insufficent rights
+     *                                    for the operation
+     * @throws MetacatInaccessibleException when the metacat server can not be
+     *                                    reached or does not respond
+     * @throws MetacatException when the metacat server generates another error
+     * @throws IOException when there is an error reading the xml document
+     */
+    
+    
+    public String upload(String docid, String filename, InputStream fileData,
+            int size)
+            throws InsufficientKarmaException, MetacatException, IOException,
+            MetacatInaccessibleException {
+        
+        URL url = new URL(metacatUrl.trim());
+        HttpMessage msg = new HttpMessage(url);
+        //set up properties
+        Properties arg = new Properties();
+        arg.put("action", "upload");
+        arg.put("docid", docid);
+        
+        Properties filenames = new Properties();
+        filenames.put("datafile", filename);
+        
+        String response = null;
+        try {
             response = sendDataForString(arg, filenames, fileData, size);
-          } catch (Exception e) {
+        } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
-          }
-
-          // Check for an error condition
-          if (response.indexOf("<error>") != -1) {
+        }
+        
+        // Check for an error condition
+        if (response.indexOf("<error>") != -1) {
             if (response.indexOf("does not have permission") != -1) {
-              throw new InsufficientKarmaException(response);
+                throw new InsufficientKarmaException(response);
             } else {
-              throw new MetacatException(response);
+                throw new MetacatException(response);
             }
-          }
-
-          return response;
-      }
-
+        }
+        
+        return response;
+    }
+    
     /**
      * Delete an XML document in the repository.
      *
@@ -535,14 +549,13 @@
      * @throws MetacatException when the metacat server generates another error
      */
     public String delete(String docid)
-        throws InsufficientKarmaException, MetacatException,
-        MetacatInaccessibleException
-    {
+    throws InsufficientKarmaException, MetacatException,
+            MetacatInaccessibleException {
         //set up properties
         Properties prop = new Properties();
         prop.put("action", "delete");
         prop.put("docid", docid);
-
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
@@ -549,7 +562,7 @@
         } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
         }
-
+        
         // Check for an error condition
         if (response.indexOf("<error>") != -1) {
             if (response.indexOf("does not have permission") != -1) {
@@ -560,8 +573,8 @@
         }
         return response;
     }
-
-
+    
+    
     /**
      * set the access on an XML document in the repository.
      *
@@ -588,11 +601,10 @@
      * @throws MetacatException when the metacat server generates another error
      */
     public String setAccess(String _docid, String _principal, String
-                            _permission, String _permType, 
-                            String _permOrder )
-        throws InsufficientKarmaException, MetacatException,
-        MetacatInaccessibleException
-    {  
+            _permission, String _permType,
+            String _permOrder )
+            throws InsufficientKarmaException, MetacatException,
+            MetacatInaccessibleException {
         //set up properties
         Properties prop = new Properties();
         prop.put("action", "setaccess");
@@ -601,7 +613,7 @@
         prop.put("permission", _permission);
         prop.put("permType", _permType);
         prop.put("permOrder", _permOrder);
-
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
@@ -608,7 +620,7 @@
         } catch (Exception e) {
             throw new MetacatInaccessibleException(e.getMessage());
         }
-
+        
         // Check for an error condition
         if (response.indexOf("<error>") != -1) {
             if (response.indexOf("does not have permission") != -1) {
@@ -619,7 +631,7 @@
         }
         return response;
     }
-
+    
     /**
      * When the MetacatFactory creates an instance it needs to set the
      * MetacatUrl to which connections should be made.
@@ -626,11 +638,10 @@
      *
      * @param metacatUrl the URL for the metacat server
      */
-    public void setMetacatUrl(String metacatUrl)
-    {
+    public void setMetacatUrl(String metacatUrl) {
         this.metacatUrl = metacatUrl;
     }
-
+    
     /**
      * Get the session identifier for this session.  This is only valid if
      * the login methods has been called successfully for this Metacat object
@@ -638,11 +649,10 @@
      *
      * @returns the sessionId as a String, or null if the session is invalid
      */
-    public String getSessionId()
-    {
+    public String getSessionId() {
         return this.sessionId;
     }
-
+    
     /**
      * Set the session identifier for this session.  This identifier was
      * previously established with a call to login.  To continue to use the
@@ -651,49 +661,47 @@
      *
      * @param String the sessionId from a previously established session
      */
-    public void setSessionId(String sessionId)
-    {
+    public void setSessionId(String sessionId) {
         this.sessionId = sessionId;
     }
     
     /**
-     * The method will return the latest revision in metacat server 
+     * The method will return the latest revision in metacat server
      * for a given document id. If some error happens, this method will throw
-     * an exception.   
+     * an exception.
      * @param docId String  the given docid you want to use. the docid it self
      *                      can have or haven't revision number
      * @throws MetacatException
      */
-     public int getNewestDocRevision(String docId) throws MetacatException
-     {
-         int rev = 0;
-         //set up properties
-         Properties prop = new Properties();
-         prop.put("action", "getrevisionanddoctype");
-         prop.put("docid", docId);
-         
-         String response = null;
-         try {
-             response = sendDataForString(prop, null, null, 0);
-             //parseRevisionResponse will return null if there is an 
-             //error that it can't handle
-             String revStr = parserRevisionResponse(response);
-             Integer revObj = new Integer(revStr);
-             rev = revObj.intValue();
-             // Check for an error condition
-             if (response.indexOf("<error>") != -1 && revStr == null) {
-                 throw new MetacatException(response);
-             }
-         } catch (Exception e) {
-             throw new MetacatException(e.getMessage());
-         }
-         return rev;
-     }
-
+    public int getNewestDocRevision(String docId) throws MetacatException {
+        int rev = 0;
+        //set up properties
+        Properties prop = new Properties();
+        prop.put("action", "getrevisionanddoctype");
+        prop.put("docid", docId);
+        
+        String response = null;
+        try {
+            response = sendDataForString(prop, null, null, 0);
+            //parseRevisionResponse will return null if there is an
+            //error that it can't handle
+            String revStr = parserRevisionResponse(response);
+            Integer revObj = new Integer(revStr);
+            rev = revObj.intValue();
+            // Check for an error condition
+            if (response.indexOf("<error>") != -1 && revStr == null) {
+                throw new MetacatException(response);
+            }
+        } catch (Exception e) {
+            throw new MetacatException(e.getMessage());
+        }
+        return rev;
+    }
+    
     /**
      * Return the highest document id for a given scope.  This is used by
      * clients to make it easier to determine the next free identifier in a
-     * sequence for a given scope.  
+     * sequence for a given scope.
      * @param scope String  the scope to use for looking up the latest id
      * @throws MetacatException when an error occurs
      */
@@ -703,19 +711,19 @@
         Properties prop = new Properties();
         prop.put("action", "getlastdocid");
         prop.put("scope", scope);
-         
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
             // Check for an error condition
             if (response.indexOf("<error>") != -1) {
-               throw new MetacatException(response);
+                throw new MetacatException(response);
             } else {
                 Reader responseReader = new StringReader(response);
-                Node root = 
-                    XMLUtilities.getXMLReaderAsDOMTreeRootNode(responseReader); 
-                Node docidNode = 
-                    XMLUtilities.getNodeWithXPath(root, "/lastDocid/docid");
+                Node root =
+                        XMLUtilities.getXMLReaderAsDOMTreeRootNode(responseReader);
+                Node docidNode =
+                        XMLUtilities.getNodeWithXPath(root, "/lastDocid/docid");
                 lastIdentifier = docidNode.getFirstChild().getNodeValue();
             }
         } catch (Exception e) {
@@ -735,31 +743,28 @@
         //set up properties
         Properties prop = new Properties();
         prop.put("action", "getalldocids");
-        if(scope != null)
-        {
-          prop.put("scope", scope);
+        if(scope != null) {
+            prop.put("scope", scope);
         }
-         
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
             // Check for an error condition
             if (response.indexOf("<error>") != -1) {
-               throw new MetacatException(response);
+                throw new MetacatException(response);
             } else {
                 Reader responseReader = new StringReader(response);
-                Node root = 
-                    XMLUtilities.getXMLReaderAsDOMTreeRootNode(responseReader); 
+                Node root =
+                        XMLUtilities.getXMLReaderAsDOMTreeRootNode(responseReader);
                 NodeList nlist = root.getChildNodes();
-                for(int i=0; i<nlist.getLength(); i++)
-                {
-                  Node n = nlist.item(i);
-                  if(n.getNodeName().equals("docid"))
-                  {
-                    //add the content to the return vector
-                    String nodeVal = n.getFirstChild().getNodeValue();
-                    resultVec.addElement(nodeVal);
-                  }
+                for(int i=0; i<nlist.getLength(); i++) {
+                    Node n = nlist.item(i);
+                    if(n.getNodeName().equals("docid")) {
+                        //add the content to the return vector
+                        String nodeVal = n.getFirstChild().getNodeValue();
+                        resultVec.addElement(nodeVal);
+                    }
                 }
                 
             }
@@ -779,34 +784,31 @@
         //set up properties
         Properties prop = new Properties();
         prop.put("action", "isregistered");
-        if(docid == null)
-        {
-          throw new MetacatException("<error>Cannot check if a null docid " +
-            "is registered.</error>");
+        if(docid == null) {
+            throw new MetacatException("<error>Cannot check if a null docid " +
+                    "is registered.</error>");
         }
         prop.put("docid", docid);
-         
+        
         String response = null;
         try {
             response = sendDataForString(prop, null, null, 0);
             // Check for an error condition
             if (response.indexOf("<error>") != -1) {
-               throw new MetacatException(response);
+                throw new MetacatException(response);
             } else {
                 Reader responseReader = new StringReader(response);
                 StringBuffer sb = new StringBuffer();
                 char[] c = new char[1024];
                 int numread = responseReader.read(c, 0, 1024);
-                while(numread != -1)
-                {
-                  sb.append(new String(c, 0, numread));
-                  numread = responseReader.read(c, 0, 1024);
+                while(numread != -1) {
+                    sb.append(new String(c, 0, numread));
+                    numread = responseReader.read(c, 0, 1024);
                 }
                 
                 String responseStr = sb.toString();
-                if(responseStr.indexOf("true") != -1)
-                {
-                  return true;
+                if(responseStr.indexOf("true") != -1) {
+                    return true;
                 }
                 return false;
             }
@@ -814,11 +816,11 @@
             throw new MetacatException(e.getMessage());
         }
     }
-
+    
     /************************************************************************
      * PRIVATE METHODS
      ************************************************************************/
-
+    
     /**
      * Send a request to metacat.
      *
@@ -831,11 +833,10 @@
      *                  in case of upload, otherwise 0
      */
     synchronized private InputStream sendDataOnce(Properties args,
-                                                  Properties filename,
-                                                  InputStream fileData,
-                                                  int size)
-        throws Exception
-    {
+            Properties filename,
+            InputStream fileData,
+            int size)
+            throws Exception {
         InputStream returnStream = null;
         URL url = new URL(metacatUrl);
         HttpMessage msg = new HttpMessage(url);
@@ -848,11 +849,11 @@
             returnStream = msg.sendPostData(args, filename, fileData, size);
         } else {
             throw new MetacatException("Invalid size specified for " +
-                                       "the input stream being passed");
+                    "the input stream being passed");
         }
         return returnStream;
     }
-
+    
     /**
      * Send a request to Metacat
      *
@@ -866,11 +867,10 @@
      * @return      InputStream as returned by Metacat
      */
     synchronized private InputStream sendData(Properties args,
-                                              Properties filename,
-                                              InputStream fileData,
-                                              int size)
-        throws Exception
-    {
+            Properties filename,
+            InputStream fileData,
+            int size)
+            throws Exception {
         InputStream returnStream = null;
         /*
             Note:  The reason that there are three try statements all executing
@@ -880,9 +880,9 @@
             2nd and 3rd chance to work before throwing an error.
             THIS IS A TOTAL HACK.  THIS NEEDS TO BE LOOKED INTO AFTER THE BETA1
             RELEASE OF MORPHO!!!  cwb (7/24/01)
-          */
+         */
         try {
-           return sendDataOnce(args, filename, fileData, size);
+            return sendDataOnce(args, filename, fileData, size);
         } catch (Exception e) {
             try {
                 return sendDataOnce(args, filename, fileData, size);
@@ -898,7 +898,7 @@
             }
         }
     }
-
+    
     /**
      * Send a request to Metacat
      *
@@ -912,17 +912,16 @@
      * @return          a string as returned by Metacat
      */
     synchronized private String sendDataForString(Properties args,
-                                                  Properties filename,
-                                                  InputStream fileData,
-                                                  int size)
-        throws Exception
-    {
+            Properties filename,
+            InputStream fileData,
+            int size)
+            throws Exception {
         String response = null;
-
+        
         try {
             InputStreamReader returnStream =
                     new InputStreamReader(sendData(args, filename,
-                                                   fileData, size));
+                    fileData, size));
             StringWriter sw = new StringWriter();
             int len;
             char[] characters = new char[512];
@@ -943,28 +942,404 @@
      * The string format is "revision;doctype"(This is bad idea, we should use xml)
      * This method will get revision string from the response string
      */
-    private String parserRevisionResponse(String response) throws Exception
-    {
-      String revision = null;
-      if (response != null)
-      {
-        if(response.indexOf("<error>") != -1)
-        {
-          if(response.indexOf("There is not record") != -1)
-          {
-            return "0";
-          }
-          else
-          {
-            return null;
-          }
+    private String parserRevisionResponse(String response) throws Exception {
+        String revision = null;
+        if (response != null) {
+            if(response.indexOf("<error>") != -1) {
+                if(response.indexOf("There is not record") != -1) {
+                    return "0";
+                } else {
+                    return null;
+                }
+            } else {
+                int firstSemiCol = response.indexOf(";");
+                revision = response.substring(0, firstSemiCol);
+            }
         }
+        return revision;
+    }
+    
+    /**
+     * JSP API: This is a convenience method to reduce the amount of code in a Metacat Client
+     * JSP.  It handles creating/reusing an instance of a MetacatClient.
+     * @param request Since this is intended to be used by a JSP, it is passed the
+     * available "request" variable (the HttpServletRequest).
+     * @throws edu.ucsb.nceas.metacat.client.MetacatInaccessibleException Received by MetacatFactory.
+     * @return MetacatClient instance.
+     */
+    public static MetacatClient getMetacatClient(javax.servlet.http.HttpServletRequest request) throws MetacatInaccessibleException {
+        MetacatClient                       result;
+        String                              metacatPath = "http://%1$s%2$s/metacat";
+        String                              host, context;
+        javax.servlet.http.HttpSession      session;
+        
+        session = request.getSession();
+        result = (MetacatClient) session.getAttribute("MetacatClient");
+        if (result == null) {
+            host = request.getHeader("host");
+            context = request.getContextPath();
+            metacatPath = String.format(metacatPath, host, context);
+            result = (MetacatClient) MetacatFactory.createMetacatConnection(metacatPath);
+            session.setAttribute("MetacatClient", result);
+        }
+        return(result);
+    }
+    
+    /**
+     * JSP API: When the user logs in, the server will send back a string containing XML.  Calling
+     * this method with the string will allow other methods to work, such as isLoggIn()
+     * and getLoginResponseElement().
+     * @param xmlString XML in String format.
+     * @throws java.io.IOException Input/Output exception.
+     */
+    public void setLoginResponse(String xmlString) throws IOException {
+        if (xmlString != null) {
+            loginResponse = XMLUtilities.getXMLReaderAsDOMDocument(new StringReader(xmlString));
+        }
+    }
+    
+    private void setMetadataDoc(InputStream ioStream) throws IOException {
+        BufferedReader                          buffy;
+        
+        if (ioStream != null) {
+            buffy = new BufferedReader(new InputStreamReader(ioStream));
+            metadataDoc = XMLUtilities.getXMLReaderAsDOMDocument(buffy);
+        }
+    }
+    
+    /**
+     * JSP API: A convenient and efficient method to retrieve info from the "login response".
+     * @param elementName String of the elementName.
+     * NOTE: setLoginResponse() must have been called first,
+     * otherwise it will always return null.
+     * @throws javax.xml.xpath.XPathExpressionException XPath error.
+     * @return String containing the text content of the element, or null.
+     */
+    public String getLoginResponseElement(String elementName) throws XPathExpressionException {
+        String                      result = null;
+        
+        if (loginResponse != null) {
+            result = (String) xpath.evaluate(elementName, loginResponse.getDocumentElement(), XPathConstants.STRING);
+            if (result != null)
+                result = result.trim();
+        }
+        return(result);
+    }
+    
+    
+    /**
+     * JSP API: Easy way to get info from an uploaded metadata XML file.
+     * Note:  doMetadataUpload() must have been called first.
+     * @param elementName String containing the elemement name
+     * @throws javax.xml.xpath.XPathExpressionException Thrown by XPath
+     * @return String text content of the named element
+     */
+    public String getMetadataDocElement(String elementName) throws XPathExpressionException {
+        String                      result = null;
+        
+        if (metadataDoc != null) {
+            result = (String) xpath.evaluate(elementName, metadataDoc.getDocumentElement(), XPathConstants.STRING);
+            result = result.trim();
+        }
+        return(result);
+    }
+    
+    /**
+     * JSP API: A convenience method to be used by JSP or any other client code that requires
+     * the user to be logged in.  NOTE: setUser() must have been called first,
+     * otherwise it will always return false.
+     * @return boolean  true if user has logged in for this session, false otherwise.
+     */
+    public boolean isLoggedIn() {
+        return(user != null);
+    }
+    
+    /**
+     * JSP API: After calling "login(ldapUserName, pwd)", call this with the username
+     * and servers response message.  You can than use isLoggedIn() to determine if
+     * the user is logged in, getLoginResponseElement(), etc.  The user name will also
+     * used by calls to doMetadataUpload() for Document Id creation (scope).
+     * @param userName User name
+     * @param serverResponse XML login response sent from Metacat.
+     */
+    public void setUser(String userName, String serverResponse) {
+        if (serverResponse != null && serverResponse.contains("login"))
+            user = userName;
         else
-        {
-          int firstSemiCol = response.indexOf(";");
-          revision = response.substring(0, firstSemiCol);
+            user = null;
+    }
+    
+    /**
+     * JSP API:  Handles metadata file and data file uploads for inserting new
+     * Metacat data packages.  Note: if content type is not "multipart/form-data",
+     * nothing will happen; thus, it's safe to be (unintentionally) called by other
+     * types of form submissions.
+     * @param request HTTP request.
+     * @return A 1-line status message for the user.
+     */
+    public String doMetadataUpload(javax.servlet.http.HttpServletRequest request) {
+        String                      result = "", contentType, formatType;
+        String                      lastDocId, nextDocId, metaDocId;
+        StringBuilder               fileName = new StringBuilder();
+        Reader                      reader;
+        int                         sizeLimit;
+        MultipartParser             multipartParser;
+        InputStream                 inputStream;
+        Node                        newBranch, metaRootNode;
+        HashMap                     dataDocIDs;
+        
+        //*** Only process request if a file upload.
+        contentType = request.getContentType();
+        if (isLoggedIn() && contentType != null && contentType.contains("multipart/form-data")) {
+            try {
+                //*** Init the MultipartParser.
+                sizeLimit = (new Integer(MetaCatUtil.getOption("datafilesizelimit"))).intValue();
+                multipartParser = new MultipartParser(request, sizeLimit * 1024 * 1024);
+                
+                //*** Get the First file, which should be the metadata file.
+                inputStream = getNextInputStream(multipartParser, fileName);
+                if (fileName.toString().toLowerCase().endsWith(".xml")) {
+                    setMetadataDoc(inputStream);
+                    
+                    //*** Get the Metadata File's DOC ID.
+                    lastDocId = getLastDocid(user);
+                    metaDocId = lastDocId = nextDocId(lastDocId);
+                    
+                    if (isFGDC()) {
+                        //*** Loop thru all of the data files, get fileName and inputStream.
+                        dataDocIDs = new HashMap();
+                        fileName = new StringBuilder();
+                        while ((inputStream = getNextInputStream(multipartParser, fileName)) != null) {
+                            //*** Get the data file's DOC ID.
+                            nextDocId = nextDocId(lastDocId);
+                            //*** Set the file format (just using file extension for now).
+                            formatType = fileName.substring(fileName.lastIndexOf(".")+1).toUpperCase();
+                            dataDocIDs.put(nextDocId, formatType);
+                            //*** Upload the data file to metacat.
+                            upload(nextDocId, fileName.toString(), inputStream, sizeLimit);
+                            
+                            lastDocId = nextDocId;
+                            fileName = new StringBuilder();
+                        }
+                        
+                        //*** Store the User Name and Doc Id in the FGDC document.
+                        newBranch = getFGDCdisinfo(getLoginResponseElement("name"), metaDocId, dataDocIDs);
+                        System.out.println("MetacatClient.doMetadataUpload: " + XMLUtilities.getDOMTreeAsString(newBranch));
+                        metaRootNode = addDistInfoToFGDC(newBranch);
+                        
+                        //*** Upload the metadata file to metacat.
+                        reader = XMLUtilities.getDOMTreeAsReader(metadataDoc.getDocumentElement(), false);
+                        insert(metaDocId, reader, null);
+                        
+                        result = "MetaCat Package Inserted:  Metadata Doc ID #" + metaDocId;
+                        reader.close();
+                    } else {
+                        System.out.println("MetacatClient.doUpload: not an FGDC file = " + fileName);
+                        result = fileName + " is not an FGDC file.  Files not uploaded.";
+                        //TODO add other types of metadata grammars here...
+                    }
+                } else {
+                    result = "The first file must be an XML Metadata file.  Files not uploaded.";
+                }
+                if (inputStream != null)
+                    inputStream.close();
+            } catch (MetacatException ex)  {
+                result = ex.getMessage();
+                System.out.println("MetacatClient.doUpload: MetacatException = " + result);
+            } catch (IOException ex)  {
+                System.out.println("MetacatClient.doUpload: " + ex);
+            } catch (Exception ex) {
+                System.out.println("MetacatClient.doUpload: " + ex);
+            } catch (Error err) {
+                System.out.println("MetacatClient.doUpload: ERR - " + err.getCause());
+                result = "ERR: " + err.getMessage();
+            }
         }
-      } 
-      return revision;
+        return(result);
     }
+    
+    private InputStream getNextInputStream(MultipartParser multipartParser, StringBuilder fileName) throws IOException {
+        InputStream                     result = null;
+        Part                            part;
+        String                          parmName = null, value = null, fnam;
+        
+        while ((part = multipartParser.readNextPart()) != null) {
+            if (part.isParam()) {
+                parmName = part.getName();
+                value = ((ParamPart) part).getStringValue();
+                System.out.println("MetacatClient.doUpload: parmName = " + parmName + "  value = " + value);
+                
+            } else if (part.isFile()) {
+                fnam = ((FilePart) part).getFileName();
+                if (fnam != null && !fnam.equals("")) {
+                    //*** File name is passed back via StringBuilder fileName param.
+                    fileName.append(fnam);
+                    result = ((FilePart) part).getInputStream();
+                    System.out.println("MetacatClient.doUpload: fileName = " + fileName + "  inputStream = " + result.toString());
+                    break;
+                }
+            }
+        }
+        return(result);
+    }
+    
+    private String nextDocId(String lastDocId) {
+        String                      result = null, tokens[];
+        int                         vers;
+        String                      template = user.toLowerCase() + ".%1$d.%2$d";
+        
+        if(lastDocId != null && lastDocId.contains(".")) {
+            lastDocId = lastDocId.replace('.','~'); //*** This is necessary for the split to work.
+            tokens = lastDocId.split("~");
+            if(tokens.length > 1 && !tokens[1].equals("")) {
+                try {
+                    vers = Integer.parseInt(tokens[1]);
+                    result = String.format(template, 1 + vers, 1);
+                } catch (NumberFormatException ex) {
+                    //*** In case the lastDocId has something other than a number.
+                    result = String.format(template, 1, 1);
+                }
+            } else {
+                //*** In case the lastDocId ends with a '.'
+                result = String.format(template, 1, 1);
+            }
+        } else {
+            //*** In case there isn't any doc Id's with the user name.
+            result = String.format(template, 1, 1);
+        }
+        return(result);
+    }
+    
+    private boolean isFGDC() {
+        boolean                     result = false;
+        DocumentType                docType;
+        String                      sysId, title = null;
+        final String                FGDC_TEST_EXPRESSION = "/metadata/idinfo/citation/citeinfo/title";
+        
+        //*** First, try the rigid proper way of determining it.
+        if (metadataDoc != null) {
+            docType = metadataDoc.getDoctype();
+            if (docType != null) {
+                sysId = docType.getSystemId();
+                if (sysId != null)
+                    result = sysId.contains(FGDC_SYSTEM_ID);
+            }
+        }
+        //*** It might not have a doc type line, so try another method.
+        if (!result) {
+            try {
+                title = getMetadataDocElement(FGDC_TEST_EXPRESSION);
+            } catch (XPathExpressionException ex) {
+                ex.printStackTrace();
+            }
+            result = (title != null && !title.equals(""));
+        }
+        return(result);
+    }
+    
+    private Node getFGDCdisinfo(String contactName, String resourceDescription, HashMap dataDocIDs) throws IOException {
+        Node                        result = null, node, digformBranch, formname, stdorder;
+        Document                    doc;
+        Iterator                    iterIt;
+        String                      key, value;
+        
+        //*** This is a valid/minimal FGDC "distinfo" branch.
+        final String XML = "<distinfo>"
+                + "    <distrib>"
+                + "        <cntinfo>"
+                + "            <cntperp>"
+                + "                <cntper></cntper>"
+                + "            </cntperp>"
+                + "            <cntaddr>"
+                + "                <addrtype></addrtype>"
+                + "                <address></address>"
+                + "                <city></city>"
+                + "                <state></state>"
+                + "                <postal></postal>"
+                + "                <country></country>"
+                + "            </cntaddr>"
+                + "            <cntvoice></cntvoice>"
+                + "        </cntinfo>"
+                + "    </distrib>"
+                + "    <resdesc></resdesc>"
+                + "    <distliab></distliab>"
+                + "    <stdorder>"
+                + "        <digform>"
+                + "            <digtinfo>"
+                + "                <formname></formname>"
+                + "            </digtinfo>"
+                + "            <digtopt>"
+                + "                <onlinopt>"
+                + "                    <computer>"
+                + "                        <networka>"
+                + "                            <networkr></networkr>"
+                + "                        </networka>"
+                + "                    </computer>"
+                + "                </onlinopt>"
+                + "            </digtopt>"
+                + "        </digform>"
+                + "        <fees></fees>"
+                + "    </stdorder>"
+                + "</distinfo>";
+        
+        doc = XMLUtilities.getXMLReaderAsDOMDocument(new StringReader(XML));
+        result = doc.getDocumentElement();
+        try {
+            //*** Set the Contact Person.
+            node = (Node) xpath.evaluate("/distinfo/distrib/cntinfo/cntperp/cntper", result, XPathConstants.NODE);
+            node.setTextContent(contactName);
+            //*** Set the metadata Doc Id.
+            node = (Node) xpath.evaluate("/distinfo/resdesc", result, XPathConstants.NODE);
+            node.setTextContent(resourceDescription);
+            
+            //*** Loop thru the files, setting their format and Doc Id.
+            stdorder = (Node) xpath.evaluate("/distinfo/stdorder", result, XPathConstants.NODE);
+            digformBranch = (Node) xpath.evaluate("/distinfo/stdorder/digform", result, XPathConstants.NODE);
+            iterIt = dataDocIDs.keySet().iterator();
+            while(iterIt.hasNext()) {
+                //*** Save the data file Doc ID (required).
+                key = (String) iterIt.next();
+                node = (Node) xpath.evaluate("digtopt/onlinopt/computer/networka/networkr", digformBranch, XPathConstants.NODE);
+                node.setTextContent(key);
+                //*** Save the data file format (optional).
+                formname = (Node) xpath.evaluate("digtinfo/formname", digformBranch, XPathConstants.NODE);
+                if ((value = (String) dataDocIDs.get(key)) != null && !value.equals("")) {
+                    formname.setTextContent(value);
+                } else {
+                    //*** We did a deep clone of the branch, so clear prior contents.
+                    formname.setTextContent("");
+                }
+                
+                //*** Clone branch for next file.
+                if (iterIt.hasNext()) {
+                    digformBranch = digformBranch.cloneNode(true);
+                    stdorder.appendChild(digformBranch);
+                }
+            }
+        } catch (XPathExpressionException ex) {
+            ex.printStackTrace();
+        }
+        return(result);
+    }
+    
+    private Node addDistInfoToFGDC(Node newBranch) {
+        Node                        result = null, node;
+        
+        if (newBranch != null) {
+            result = metadataDoc.getDocumentElement();
+            try {
+                //*** Get a reference to the FGDC required "metainfo" node (only 1 allowed).
+                node = (Node) xpath.evaluate("/metadata/metainfo", result, XPathConstants.NODE);
+                if (node != null) {
+                    newBranch = metadataDoc.importNode(newBranch, true);
+                    //*** Add the new "distinfo" before it.
+                    result.insertBefore(newBranch, node);
+                }
+            } catch (XPathExpressionException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return(result);
+    }
+    
 }
