Print

Print


Author: [log in to unmask]
Date: Mon Aug 31 15:38:12 2015
New Revision: 3475

Log:
Allow metadata to be given when adding datasets; implement DatasetMetadata; add doc.

Added:
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetMetadataImpl.java
    java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatClientTest.java
      - copied, changed from r3470, java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatTest.java
Removed:
    java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatTest.java
Modified:
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClient.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientImpl.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/Dataset.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetImpl.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetMetadata.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetSite.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetUtilities.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/HttpUtilities.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/JSONUtilities.java
    java/trunk/datacat-client/src/main/java/org/hps/datacat/client/ScanStatus.java

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClient.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClient.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClient.java	Mon Aug 31 15:38:12 2015
@@ -2,6 +2,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Interface to the SLAC SRS datacat system via HTTP/REST calls.
@@ -19,9 +20,16 @@
      * @param site the site of the file 
      * @param fileFormat the file format
      * @param name the name of the dataset
+     * @param metadata metadata to assign to the dataset
      * @return the HTTP status code from the request
      */
-    int addDataset(String folder, DatasetDataType dataType, String resource, DatasetSite site, DatasetFileFormat fileFormat, String name);
+    int addDataset(String folder, 
+            DatasetDataType dataType,
+            String resource,
+            DatasetSite site,
+            DatasetFileFormat fileFormat,
+            String name,
+            Map<String, Object> metadata);
             
     /**
      * Create a folder in the data catalog.
@@ -70,10 +78,10 @@
      * @param query the query to execute
      * @return the HTTP status code from the request
      */
-    List<Dataset> findDatasets(String folder, String query);
+    List<Dataset> findDatasets(String folder, String query, Set<String> showMetadata);
     
     // TODO: method to get dataset from path
-    // to get all metadata need site
+    // to get all metadata need the site in query
     // http://localhost:8080/datacat-v0.4-SNAPSHOT/r/path.json/HPS/derp/herp01;s=SLAC 
     // use HTTP GET 
     // Dataset getDataSet(String path);

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientImpl.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientImpl.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientImpl.java	Mon Aug 31 15:38:12 2015
@@ -8,17 +8,39 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import org.json.JSONObject;
+import org.lcsim.util.log.LogUtil;
+import org.lcsim.util.log.MessageOnlyLogFormatter;
 
 /**
+ * Implementation of {@link DatacatClient} interface for working with SRS datacat REST API.
  * 
  * @author Jeremy McCormick, SLAC
  */
 final class DatacatClientImpl implements DatacatClient {
-
+    
+    /**
+     * Setup class logging.
+     */
+    private static Logger LOGGER = LogUtil.create(DatacatClientImpl.class, new MessageOnlyLogFormatter(), Level.ALL);
+
+    /**
+     * The base URL of the datacat server.
+     */
     private URL url;
+    
+    /**
+     * The site (SLAC or JLAB).
+     */
     private DatasetSite site;
+    
+    /**
+     * The root directory (e.g. should be 'HPS').
+     */
     private String rootDir;
     
     /**
@@ -49,23 +71,47 @@
             throw new IllegalArgumentException("The root dir argument is null.");
         }
         this.rootDir = rootDir;
-        System.out.println("url: " + url);
-        System.out.println("site: " + site);
-        System.out.println("rootDir: " + rootDir);
-    }
-    
+        LOGGER.config("url: "  + url);
+        LOGGER.config("site: " + site);
+        LOGGER.config("rootDir: " + rootDir);
+    }
+    
+    /**
+     * Remove a folder from the catalog.
+     * <p>
+     * It must be empty or an error will occur. 
+     * 
+     * @param folder the folder path
+     * @return the HHTP status code from the request
+     */
     @Override
     public int removeFolder(String folder) {
         String fullUrl = url.toString() + "/r/folders.json/" + this.rootDir + folder;
+        LOGGER.info("removing folder: " + fullUrl);
         return HttpUtilities.doDelete(fullUrl);
     }
 
+    /**
+     * Delete a dataset from the catalog.
+     * <p>
+     * This has no affect on the underlying resource (file).
+     * 
+     * @param path the path of the dataset
+     * @return the HTTP status code from the reqest
+     */
     @Override
     public int deleteDataset(String path) {
-        String fullUrl = url.toString() + "/r/datasets.json/" + this.rootDir + path;
-        return HttpUtilities.doDelete(fullUrl);
-    }
-        
+        String urlLocation = url.toString() + "/r/datasets.json/" + this.rootDir + path;
+        LOGGER.info("deleting dataset: " + urlLocation);
+        return HttpUtilities.doDelete(urlLocation);
+    }
+     
+    /**
+     * Create a folder in the data catalog.
+     * 
+     * @param folder the folder's path
+     * @return the HTTP status code from the request
+     */
     @Override
     public int makeFolder(String path) {
         Map<String, Object> parameters = new HashMap<String, Object>();
@@ -73,28 +119,47 @@
         String name = new File(path).getName();       
         parameters.put("name", name);
         parameters.put("_type", "folder");
-        JSONObject object = JSONUtilities.createJSONFromMap(parameters);
-        return HttpUtilities.doPost(url + "/r/folders.json/" + this.rootDir, object.toString());
-    }
-    
+        JSONObject object = JSONUtilities.createJSONFromMap(parameters);        
+        String urlLocation = url + "/r/folders.json/" + this.rootDir;
+        LOGGER.info("making folder: " + urlLocation);
+        LOGGER.info("folder JSON: " + object.toString());
+        return HttpUtilities.doPost(urlLocation, object.toString());
+    }
+    
+    /**
+     * Add metadata to an existing dataset.
+     * 
+     * @param folder the folder
+     * @param datasetName the name of the dataset
+     * @param metaData the map of metadata where values can be <code>String</code>, <code>Integer</code> or <code>Float</code>
+     * @return the HTTP status code from the request
+     */
     @Override
     public int addMetadata(String folder, String name, Map<String, Object> metaData) {
-        JSONObject object = JSONUtilities.createJSONMetaData(metaData);
+        JSONObject object = new JSONObject();
+        object.put("versionMetadata", JSONUtilities.createJSONMetadataArray(metaData));
         String patchUrl = this.url.toString() + "/r/datasets.json/" + this.rootDir + "/" + folder + "/" + name + ";v=current;s=" + this.site;
-                
+        LOGGER.info("addMetadata: " + patchUrl);
         return HttpUtilities.doPatch(patchUrl, object.toString());
     }      
 
-    // example
-    // http://localhost:8080/datacat-v0.4-SNAPSHOT/r/search.json/HPS/derp?filter=run+%3E+1000
-    @Override
-    public List<Dataset> findDatasets(String directory, String query) {
+    /**
+     * Find datasets in the catalog.
+     * <p>
+     * See <a href="http://docs.datacatalog.apiary.io/#search">Search Doc</a> for more details.
+     * 
+     * @param folder the folder path
+     * @param query the query to execute
+     * @return the HTTP status code from the request
+     */
+    @Override
+    public List<Dataset> findDatasets(String directory, String query, Set<String> showMetadata) {
         
-        String fullUrl = this.url.toString() + "/r/search.json/" + this.rootDir + "/";
+        String urlLocation = this.url.toString() + "/r/search.json/" + this.rootDir + "/";
         if (directory != null) {
-            fullUrl += directory;
-        }
-        fullUrl += ";s=" + this.site.name();
+            urlLocation += directory;
+        }
+        urlLocation += ";s=" + this.site.name() + "?";        
         if (query != null) {
             String encoded = null;        
             try {
@@ -102,28 +167,51 @@
             } catch (UnsupportedEncodingException e) {
                 throw new RuntimeException(e);
             }
-            fullUrl += "?filter=" + encoded;
-        }
-        System.out.println("query: " + fullUrl);
+            urlLocation += "filter=" + encoded;
+        }
+        if (showMetadata != null) {
+            for (String metadataField : showMetadata) {
+                urlLocation += "&show=" + metadataField;
+            }
+        }
+        LOGGER.info("findDatasets: " + urlLocation);
         StringBuffer outputBuffer = new StringBuffer();
-        int response = HttpUtilities.doGet(fullUrl, outputBuffer);
-        System.out.println("response: " + response);
-        System.out.println("output: " + outputBuffer.toString());
+        int response = HttpUtilities.doGet(urlLocation, outputBuffer);
+        if (response >= 400) {
+            throw new RuntimeException("HTTP GET failed with code: " + response);
+        }
         
         // Build and return dataset list
         JSONObject searchResults = new JSONObject(outputBuffer.toString());
+        LOGGER.info("returning search results: " + searchResults.toString());
         return DatasetUtilities.getDatasetsFromSearch(searchResults);
     }    
-    
-    @Override
-    public int addDataset(String folder, DatasetDataType dataType, String resource, DatasetSite site, DatasetFileFormat fileFormat, String name) {
+
+    /**
+     * Add a dataset to the data catalog.
+     * 
+     * @param folder the folder which must already exist
+     * @param dataType the data type
+     * @param resource the resource (path)
+     * @param site the site of the file 
+     * @param fileFormat the file format
+     * @param name the name of the dataset
+     * @param metadata metadata to assign to the dataset
+     * @return the HTTP status code from the request
+     */
+    @Override
+    public int addDataset(String folder, DatasetDataType dataType, String resource, DatasetSite site, 
+            DatasetFileFormat fileFormat, String name, Map<String, Object> metadata) {
         Map<String, Object> parameters = new HashMap<String, Object>();
         parameters.put("dataType", dataType.toString());
         parameters.put("resource", resource);
         parameters.put("site", DatasetSite.SLAC.name());
         parameters.put("fileFormat", fileFormat.toString());        
-        parameters.put("name", name);           
-        JSONObject dataset = JSONUtilities.createJSONDataset(parameters);
-        return HttpUtilities.doPost(url + "/r/datasets.json/" + this.rootDir + "/" + folder, dataset.toString());
+        parameters.put("name", name);
+        JSONObject jsonDataset = JSONUtilities.createJSONDataset(parameters, metadata);
+        String urlLocation = url + "/r/datasets.json/" + this.rootDir + "/" + folder;
+        LOGGER.info("addDataset: " + urlLocation);
+        LOGGER.info("dataset JSON: " + jsonDataset.toString());
+        return HttpUtilities.doPost(urlLocation, jsonDataset.toString());
     }
 }

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/Dataset.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/Dataset.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/Dataset.java	Mon Aug 31 15:38:12 2015
@@ -10,17 +10,52 @@
  */
 public interface Dataset {
     
+    /**
+     * Get the name of the dataset without the path component e.g. "dataset01".
+     * 
+     * @return the name of the dataset
+     */
     String getName();
     
+    /**
+     * Get the path of the dataset e.g. "/HPS/folder/dataset01".
+     * 
+     * @return the path of the dataset
+     */
     String getPath();
     
+    /**
+     * Get the dataset locations.
+     * 
+     * @return the dataset locations
+     */
     List<DatasetLocation> getLocations();
         
+    /**
+     * Get the file format e.g. EVIO, LCIO, etc.
+     * 
+     * @return the dataset file format
+     */
     DatasetFileFormat getFileFormat();
     
+    /**
+     * Get the data type e.g. RAW, RECON, etc.
+     * 
+     * @return the data type
+     */
     DatasetDataType getDataType();
     
+    /**
+     * Get the creation date.
+     * 
+     * @return the creation date
+     */
     Date getCreated();
     
-    //DatasetMetadata getMetadata();
+    /**
+     * Get the dataset's metadata.
+     * 
+     * @return the dataset's metadata
+     */
+    DatasetMetadata getMetadata();
 }

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetImpl.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetImpl.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetImpl.java	Mon Aug 31 15:38:12 2015
@@ -23,6 +23,7 @@
     private DatasetFileFormat fileFormat;
     private List<DatasetLocation> locations = new ArrayList<DatasetLocation>();
     private Date created;
+    private DatasetMetadata metadata;
     
     private static final SimpleDateFormat DATE_PARSER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
     
@@ -46,7 +47,10 @@
         JSONArray locationsArray = jsonObject.getJSONArray("locations");
         for (int i = 0; i < locationsArray.length(); i++) {
             this.locations.add(new DatasetLocationImpl(locationsArray.getJSONObject(i)));
-        }        
+        }
+        if (jsonObject.has("metadata")) {
+            metadata = new DatasetMetadataImpl(jsonObject.getJSONArray("metadata"));
+        }
     }
     
     @Override
@@ -78,4 +82,13 @@
     public Date getCreated() {
         return this.created;
     }
+    
+    @Override
+    public DatasetMetadata getMetadata() {
+        return this.metadata;
+    }
+    
+    public String toString() {
+        return "Dataset { name: " + name + ", path:" + path + ", " + "dataType: " + dataType.name() + "fileFormat: " + fileFormat.name() + ", created: " + created + " }"; 
+    }    
 }

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetMetadata.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetMetadata.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetMetadata.java	Mon Aug 31 15:38:12 2015
@@ -2,16 +2,41 @@
 
 
 /**
+ * Dataset metadata which is keys and values that are double, integer or string.
  * 
  * @author Jeremy McCormick, SLAC
  */
 public interface DatasetMetadata {
-        
-    float getFloat(String key);
     
-    int getInt(String key);
+    /**
+     * Get a double value.
+     * 
+     * @param key the key name
+     * @return the double value
+     */
+    double getDouble(String key);
+       
+    /**
+     * Get an integer value. 
+     * 
+     * @param key the key name
+     * @return the integer value
+     */
+    int getInteger(String key);
     
+    /**
+     * Get a string value.
+     * 
+     * @param key the key name
+     * @return the 
+     */
     String getString(String key);
     
+    /**
+     * Return <code>true</code> if key exists in metadata.
+     * 
+     * @param key the key name
+     * @return <code>true</code> if key exists in metadata
+     */
     boolean hasKey(String key);
 }

Added: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetMetadataImpl.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetMetadataImpl.java	(added)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetMetadataImpl.java	Mon Aug 31 15:38:12 2015
@@ -0,0 +1,115 @@
+package org.hps.datacat.client;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * Implementation of {@link DatasetMetadata).
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+final class DatasetMetadataImpl implements DatasetMetadata {
+
+    /**
+     * The mapping of keys to values.
+     */
+    private final Map<String, Object> metadataMap = new HashMap<String, Object>();
+    
+    /**
+     * Create a new metadata object.
+     * 
+     * @param jsonArray the JSON array with the metadata
+     */
+    DatasetMetadataImpl(JSONArray jsonArray) {
+        parse(jsonArray);
+    }
+    
+    /**
+     * Parse metadata from JSON array.
+     * 
+     * @param jsonArray the JSON array
+     */
+    private void parse(JSONArray jsonArray) {
+        for (int i = 0; i < jsonArray.length(); i++) {
+            JSONObject jsonObject = jsonArray.getJSONObject(i);
+            String type = jsonObject.getString("type");
+            Object value = null;
+            if (type.equals("string")) {
+                value = jsonObject.getString("value");
+            } else if (type.equals("decimal")) {
+                value = jsonObject.getDouble("value");
+            } else if (type.equals("integer")) {
+                value = jsonObject.getInt("value");
+            } else {
+                throw new IllegalArgumentException("Unknown type: " + type);
+            }
+            String key = jsonObject.getString("key");
+            metadataMap.put(key, value);
+        }
+    }
+    
+    /**
+     * Get a double value.
+     * 
+     * @param key the key name
+     * @return the double value
+     */
+    @Override
+    public double getDouble(String key) {
+        return Double.class.cast(metadataMap.get(key));
+    }
+
+    /**
+     * Get an integer value. 
+     * 
+     * @param key the key name
+     * @return the integer value
+     */
+    @Override
+    public int getInteger(String key) {
+        return Integer.class.cast(metadataMap.get(key));
+    }
+
+    /**
+     * Get a string value.
+     * 
+     * @param key the key name
+     * @return the 
+     */
+    @Override
+    public String getString(String key) {
+        return String.class.cast(metadataMap.get(key));
+    }
+
+    /**
+     * Return <code>true</code> if key exists in metadata.
+     * 
+     * @param key the key name
+     * @return <code>true</code> if key exists in metadata
+     */
+    @Override
+    public boolean hasKey(String key) {
+        return metadataMap.containsKey(key);
+    }
+    
+    /**
+     * Convert this object to a string.
+     * 
+     * @return this object converted to a string
+     */
+    @Override
+    public String toString() {
+        StringBuffer stringBuffer = new StringBuffer();
+        stringBuffer.append(" DatasetMetadata { ");
+        for (Entry<String, Object> entry : metadataMap.entrySet()) {
+            stringBuffer.append(entry.getKey() + ": " + entry.getValue() + ", ");
+        }
+        stringBuffer.setLength(stringBuffer.length() - 2);
+        stringBuffer.append(" }");
+        return stringBuffer.toString();
+    }
+}

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetSite.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetSite.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetSite.java	Mon Aug 31 15:38:12 2015
@@ -6,6 +6,12 @@
  *
  */
 public enum DatasetSite {
+    /** 
+     * SLAC site. 
+     */
     SLAC,
+    /** 
+     * JLAB site. 
+     */
     JLAB;
 }

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetUtilities.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetUtilities.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetUtilities.java	Mon Aug 31 15:38:12 2015
@@ -7,11 +7,18 @@
 import org.json.JSONObject;
 
 /**
+ * Dataset utilities.
  * 
  * @author Jeremy McCormick, SLAC
  */
 final class DatasetUtilities {
     
+    /**
+     * Create {@link Dataset} objects from JSON search results.
+     * 
+     * @param searchResults the JSON search results
+     * @return the list of {@link Dataset} objects
+     */
     static List<Dataset> getDatasetsFromSearch(JSONObject searchResults) {
         List<Dataset> datasets = new ArrayList<Dataset>();
         JSONArray resultsArray = searchResults.getJSONArray("results");
@@ -20,4 +27,11 @@
         }
         return datasets;
     }
+    
+    /**
+     * No class instantiation.
+     */
+    private DatasetUtilities() {
+        throw new RuntimeException("Do not instantiate this class.");
+    }
 }

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/HttpUtilities.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/HttpUtilities.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/HttpUtilities.java	Mon Aug 31 15:38:12 2015
@@ -24,6 +24,13 @@
  */
 final class HttpUtilities {
 
+    /**
+     * Do an HTTP POST.
+     * 
+     * @param urlLocation the URL location
+     * @param data the data to stream to the server
+     * @return the HTTP response code
+     */
     static int doPost(String urlLocation, String data) {
         int responseCode = 0;
         try {
@@ -48,11 +55,17 @@
         return responseCode;
     }
     
+    /**
+     * Do an HTTP get and return the output from the server in a <code>StringBuffer</code>.
+     * 
+     * @param urlLocation the URL location
+     * @param stringBuffer the string buffer with the server output
+     * @return the HTTP response
+     */
     static int doGet(String urlLocation, StringBuffer stringBuffer) {
         HttpURLConnection connection = null;
         int response = 0;
         try {
-            //System.out.println("doGet: " + urlLocation);
             connection = (HttpURLConnection) new URL(urlLocation).openConnection();
             connection.setRequestMethod("GET");
             connection.setRequestProperty("Content-Type", "application/json");
@@ -72,7 +85,13 @@
         return response;
     }    
     
-    
+    /**
+     * Do an HTTP patch.
+     * 
+     * @param urlLocation the URL location
+     * @param data the data to stream to the server
+     * @return the HTTP response code
+     */
     static int doPatch(String urlLocation, String data) {
         int responseCode = 0;        
         CloseableHttpClient httpClient = HttpClients.createDefault();
@@ -87,12 +106,12 @@
                                     ContentType.APPLICATION_JSON);
             httpPatch.setEntity(entity);            
             CloseableHttpResponse response = httpClient.execute(httpPatch);
-            System.out.println("status: " + response.getStatusLine());
             try {
                 EntityUtils.consume(response.getEntity());
             } finally {
                 response.close();
-            }            
+            }
+            responseCode = response.getStatusLine().getStatusCode();
         } catch (URISyntaxException e) {
             throw new IllegalArgumentException(e);
         } catch(IOException e) {
@@ -107,12 +126,16 @@
         return responseCode;
     }    
     
-    
-    static int doDelete(String fullUrl) {
+    /**
+     * Do an HTTP DELETE.
+     * 
+     * @param urlLocation the URL location
+     * @return the HTTP response code
+     */
+    static int doDelete(String urlLocation) {
         int responseCode = 0;
         try {
-            URL url = new URL(fullUrl);
-            System.out.println("deleting url: " + fullUrl);
+            URL url = new URL(urlLocation);
             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
             connection.setDoOutput(true);
             connection.setRequestMethod("DELETE");
@@ -124,6 +147,7 @@
         return responseCode;
     }
     
+    /*
     static URL createURL(String... chunks) {
         if (chunks.length == 0) {
             throw new IllegalArgumentException("No arguments provided.");
@@ -138,4 +162,5 @@
             throw new IllegalArgumentException("Bad URL string: " + urlString);
         }
     }
+    */
 }

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/JSONUtilities.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/JSONUtilities.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/JSONUtilities.java	Mon Aug 31 15:38:12 2015
@@ -6,12 +6,20 @@
 import org.json.JSONObject;
 
 /**
- * 
+ * JSON datacat utilities.
+ *  
  * @author Jeremy McCormick, SLAC
  */
 final class JSONUtilities {
     
-    static JSONObject createJSONDataset(Map<String, Object> parameters) {
+    /**
+     * Create a full JSON dataset object.
+     * 
+     * @param parameters the parameters of the object as a map
+     * @param metadata the metadata of the object as a map
+     * @return the created JSON dataset object
+     */
+    static JSONObject createJSONDataset(Map<String, Object> parameters, Map<String, Object> metadata) {
         JSONObject dataset = new JSONObject();
         dataset.put("dataType", parameters.get("dataType"));
         dataset.put("versionId", "new");
@@ -23,9 +31,19 @@
         dataset.put("locations", array);                
         dataset.put("fileFormat", parameters.get("fileFormat"));
         dataset.put("name", parameters.get("name"));
+        if (metadata != null) {
+            JSONArray jsonMetadata = createJSONMetadataArray(metadata);
+            dataset.put("versionMetadata", jsonMetadata);
+        }
         return dataset;
     }
     
+    /**
+     * Create a flat JSON object from a map of keys and values.
+     * 
+     * @param parameters the parameter map
+     * @return the JSON object
+     */
     static JSONObject createJSONFromMap(Map<String, Object> parameters) {
         JSONObject object = new JSONObject();
         for (Map.Entry<String, Object> entry : parameters.entrySet()) {
@@ -34,15 +52,31 @@
         return object;
     }
     
-    static JSONObject createJSONMetaData(Map<String, Object> metaData) {
-        JSONObject object = new JSONObject();
+    /**
+     * Create a JSON array of metadata from the map of keys and values.
+     * 
+     * @param metadata the metadata map 
+     * @return the JSON array
+     */
+    static JSONArray createJSONMetadataArray(Map<String, Object> metadata) {
         JSONArray array = new JSONArray();
-        for (Map.Entry<String, Object> entry : metaData.entrySet()) {
-            JSONObject value = new JSONObject();
-            value.put(entry.getKey(), entry.getValue());
-            array.put(value);
+        for (Map.Entry<String, Object> entry : metadata.entrySet()) {
+            JSONObject metadataObject = new JSONObject();
+            metadataObject.put(entry.getKey(), entry.getValue());
+            metadataObject.put("key", entry.getKey());
+            Object rawValue = entry.getValue();
+            if (rawValue instanceof String) {
+                metadataObject.put("type", "string");
+            } else if (rawValue instanceof Integer | rawValue instanceof Long) {
+                metadataObject.put("type", "integer");
+            } else if (rawValue instanceof Float | rawValue instanceof Double) {
+                metadataObject.put("type", "decimal");
+            } else {
+                throw new IllegalArgumentException("Do not know how to handle type: " + rawValue.getClass().getName());
+            }
+            metadataObject.put("value", entry.getValue());                      
+            array.put(metadataObject);
         }                
-        object.put("versionMetadata", array);
-        return object;
+        return array;        
     }
 }

Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/ScanStatus.java
 =============================================================================
--- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/ScanStatus.java	(original)
+++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/ScanStatus.java	Mon Aug 31 15:38:12 2015
@@ -1,8 +1,21 @@
 package org.hps.datacat.client;
 
-
+/**
+ * The scan status of {@link Dataset}.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
 public enum ScanStatus {
+    /**
+     * Scan status is not known.
+     */
     UNKNOWN,
+    /**
+     * Dataset has not been scanned.
+     */
     UNSCANNED,
+    /**
+     * Dataset has been scanned.
+     */
     OK
 }

Copied: java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatClientTest.java (from r3470, java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatTest.java)
 =============================================================================
--- java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatTest.java	(original)
+++ java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatClientTest.java	Mon Aug 31 15:38:12 2015
@@ -1,19 +1,19 @@
 package org.hps.datacat.client;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-
-import org.hps.datacat.client.DatacatClient;
-import org.hps.datacat.client.DatacatClientImpl;
-import org.hps.datacat.client.Dataset;
-import org.hps.datacat.client.DatasetDataType;
-import org.hps.datacat.client.DatasetFileFormat;
-import org.hps.datacat.client.DatasetSite;
+import java.util.Set;
 
 import junit.framework.TestCase;
 
-public class DatacatTest extends TestCase {
+/**
+ * 
+ * @author Jeremy McCormick, SLAC
+ *
+ */
+public class DatacatClientTest extends TestCase {
     
     private static final String DATASET_NAME = "dummyDataset";
     private static final String FOLDER = "dummyFolder";
@@ -21,31 +21,27 @@
     
     public void testDatacat() throws Exception {
 
-        // Datacat client with default parameters
+        // Datacat client with default parameters.
         DatacatClient client = new DatacatClientImpl();
         
         // Stores response from HTTP operations
         int response = -1;
         
-        // Create dummy folder
+        // Create dummy folder.
         response = client.makeFolder("dummyFolder");
-        System.out.println("makeFolder: " + response);
-        System.out.println();
                         
-        // Add dummy dataset
-        // TODO: should add some meta data here too
-        response = client.addDataset(FOLDER, DatasetDataType.TEST, RESOURCE, DatasetSite.SLAC, DatasetFileFormat.TEST, DATASET_NAME);
-        System.out.println("addDataset: " + response);
-        System.out.println();
+        // Add dummy dataset.
+        Map<String, Object> dsMetadata = new HashMap<String, Object>();
+        dsMetadata.put("testInt", 1);
+        dsMetadata.put("testFloat", 1.1f);
+        dsMetadata.put("testDouble", 1.2d);
+        dsMetadata.put("testString", "herpderp");
+        response = client.addDataset(FOLDER, DatasetDataType.TEST, RESOURCE, DatasetSite.SLAC, DatasetFileFormat.TEST, DATASET_NAME, dsMetadata);
         
-        // Patch the dataset with some meta data
+        // Patch the dataset with some meta data.
         Map<String, Object> metaData = new HashMap<String, Object>();
-        //metaData.put("someStringVar", "aStringValue");
-        metaData.put("someIntVar", 1234);
-        //metaData.put("someFloatVar", 1.234);
+        metaData.put("testInt2", 1234);
         response = client.addMetadata(FOLDER, DATASET_NAME, metaData);
-        System.out.println("patchDataset: " + response);
-        System.out.println();
         
         // TODO: check that folder exists
         
@@ -53,20 +49,26 @@
         
         // TODO: get the full folder info
                 
-        // TODO: get the full dataset info       
+        // TODO: get the full dataset info
                        
-        // Find the dataset with a simple query
-        List<Dataset> datasets = client.findDatasets(FOLDER, "someIntVar == 1234");
+        // Find the dataset with a simple query.
+        Set<String> metadataFields = new HashSet<String>();
+        metadataFields.add("testInt");
+        metadataFields.add("testFloat");        
+        metadataFields.add("testDouble");
+        metadataFields.add("testString");
+        List<Dataset> datasets = client.findDatasets(FOLDER, "testInt == 1", metadataFields);
         for (Dataset dataset : datasets) {
             System.out.println("found dataset: " + dataset.getName());
+            System.out.println("metadata: " + dataset.getMetadata());
         }
         
-        // Delete the dataset
+        // Delete the dataset.
         response = client.deleteDataset("/" + FOLDER + "/" + DATASET_NAME);
         System.out.println("deleteDataset: " + response);
         System.out.println();
         
-        // Remove the folder
+        // Remove the folder.
         client.removeFolder("/" + FOLDER);
         System.out.println("removeFolder: " + response);
         System.out.println();