Author: [log in to unmask] Date: Wed Sep 16 17:31:20 2015 New Revision: 3620 Log: Updates to datacat client. 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/DatacatClientFactory.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/DatacatConstants.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/DatasetDataType.java java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetFileFormat.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/DatasetLocation.java java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetLocationImpl.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/test/java/org/hps/datacat/client/DatacatClientTest.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 Wed Sep 16 17:31:20 2015 @@ -6,89 +6,102 @@ /** * Interface to the SLAC SRS datacat system via HTTP/REST calls. - * + * * @author Jeremy McCormick, SLAC */ public interface DatacatClient { /** * 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 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, - Map<String, Object> metadata); - - /** - * Create a folder in the data catalog. - * - * @param folder the folder's path - * @return the HTTP status code from the request - */ - int makeFolder(String folder); - + int addDataset(String folder, DatasetDataType dataType, String resource, DatasetSite site, + DatasetFileFormat fileFormat, String name, Map<String, Object> metadata); + /** * 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> + * @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 */ int addMetadata(String folder, String datasetName, Map<String, Object> metaData); - - /** - * 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 - */ - int removeFolder(String folder); - + /** * 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 */ int deleteDataset(String path); - + + /** + * Return <code>true</code> if the path exists in the datacat. + * + * @param path the path in the datacat + * @return <code>true</code> if the path exists + */ + boolean exists(String path); + /** * 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 */ List<Dataset> findDatasets(String folder, String query, Set<String> showMetadata); - - // TODO: method to get dataset from path - // 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); - - // TODO: method to determine if folder or dataset exists - // http://localhost:8080/datacat-v0.4-SNAPSHOT/r/path.json/HPS/derp/derp - // will return - // {"message":"File doesn't exist","type":"NoSuchFileException","cause":"Unable to resolve /HPS/derp/derp in parent Name: derp\tPath: /HPS/derp\t"} - // boolean exists(String path); + + /** + * Get a dataset from its path. + * <p> + * Example URL: + * + * <pre> + * http://localhost:8080/datacat-v0.4-SNAPSHOT/r/path.json/HPS/data/hps_005772.evio.0;s=SLAC + * </pre> + * + * @param path the path in the data catalog + * @return the dataset + */ + Dataset getDataSet(String path, DatasetSite site); + + /** + * Return <code>true</code> if path is a folder in the data catalog. + * + * @return <code>true</code> if path is a folder in the data catalog + */ + boolean isFolder(String path); + + /** + * Create a folder in the data catalog. + * + * @param folder the folder's path + * @return the HTTP status code from the request + */ + int makeFolder(String folder); + + /** + * 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 + */ + int removeFolder(String folder); } Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientFactory.java ============================================================================= --- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientFactory.java (original) +++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatClientFactory.java Wed Sep 16 17:31:20 2015 @@ -1,11 +1,11 @@ package org.hps.datacat.client; /** - * Factory class for providing user access to interfaces with protected implementation classes. + * Factory class for providing user access to interfaces that have protected implementations. * * @author Jeremy McCormick, SLAC */ -public class DatacatClientFactory { +public final class DatacatClientFactory { /** * Create a datacat client. 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 Wed Sep 16 17:31:20 2015 @@ -13,54 +13,54 @@ import java.util.logging.Logger; import org.json.JSONObject; +import org.lcsim.util.log.DefaultLogFormatter; 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); + private static Logger LOGGER = LogUtil.create(DatacatClientImpl.class, new DefaultLogFormatter(), Level.ALL); + + /** + * The root directory (e.g. should be 'HPS'). + */ + private final String rootDir; + + /** + * The site (SLAC or JLAB). + */ + private final DatasetSite site; /** * 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; - + /** * Create client with default parameters. */ - DatacatClientImpl() { + DatacatClientImpl() { this(DatacatConstants.BASE_URL, DatasetSite.SLAC, DatacatConstants.ROOT_DIR); } - + /** * Create client. - * - * @param baseUrl - * @param site - * @param rootDir - */ - DatacatClientImpl(String url, DatasetSite site, String rootDir) { + * + * @param baseUrl the base URL of the data catalog application + * @param site the default site where physical files are located + * @param rootDir the root directory in the data catalog + */ + DatacatClientImpl(final String url, final DatasetSite site, final String rootDir) { try { this.url = new URL(url); - } catch (MalformedURLException e) { + } catch (final MalformedURLException e) { throw new IllegalArgumentException("The URL is bad.", e); } if (site == null) { @@ -71,147 +71,227 @@ throw new IllegalArgumentException("The root dir argument is null."); } this.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); + LOGGER.config("url: " + url + "; site: " + site + "rootDir: " + rootDir); + } + + /** + * Add a dataset to the data catalog. + * + * @param folder the logical folder in the datacat, which must already exist + * @param dataType the data type + * @param resource the resource (path on the file system) + * @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(final String folder, final DatasetDataType dataType, final String resource, + final DatasetSite site, final DatasetFileFormat fileFormat, final String name, + final Map<String, Object> metadata) { + final 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); + final JSONObject jsonDataset = JSONUtilities.createJSONDataset(parameters, metadata); + final 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()); + } + + /** + * 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(final String folder, final String name, final Map<String, Object> metaData) { + final JSONObject object = new JSONObject(); + object.put("versionMetadata", JSONUtilities.createJSONMetadataArray(metaData)); + final 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()); } /** * Delete a dataset from the catalog. * <p> - * This has no affect on the underlying resource (file). - * + * This has no affect on the underlying resource (file on disk). + * * @param path the path of the dataset * @return the HTTP status code from the reqest */ @Override - public int deleteDataset(String path) { - String urlLocation = url.toString() + "/r/datasets.json/" + this.rootDir + path; + public int deleteDataset(final String path) { + final String urlLocation = url.toString() + "/r/datasets.json/" + this.rootDir + path; LOGGER.info("deleting dataset: " + urlLocation); return HttpUtilities.doDelete(urlLocation); } - + + /** + * Return <code>true</code> if the path exists. + * + * @param path the path in the data catalog + */ + @Override + public boolean exists(final String path) { + if (path == null) { + throw new IllegalArgumentException("The path is null."); + } + if (path.length() == 0) { + throw new IllegalArgumentException("The path is a blank string."); + } + final String urlLocation = this.url + "/r/path.json/" + this.rootDir + "/" + path; + final StringBuffer output = new StringBuffer(); + final int status = HttpUtilities.doGet(urlLocation, output); + if (status > 400) { + throw new RuntimeException("HTTP GET returned error status: " + status); + } + final JSONObject jsonObject = new JSONObject(output.toString()); + return jsonObject.has("_type"); + } + + /** + * Find datasets in the catalog. + * <p> + * See <a href="http://docs.datacatalog.apiary.io/#search">Search Doc</a> for more details + * on search syntax. + * + * @param folder the folder path + * @param query the query statement to execute + * @return the HTTP status code from the request + */ + @Override + public List<Dataset> findDatasets(final String directory, final String query, final Set<String> showMetadata) { + + String urlLocation = this.url.toString() + "/r/search.json/" + this.rootDir + "/"; + if (directory != null) { + urlLocation += directory; + } + urlLocation += ";s=" + this.site.name() + "?"; + + // Encode query string so it will be a valid URL. + if (query != null) { + String encoded = null; + try { + encoded = URLEncoder.encode(query, "UTF-8"); + } catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + urlLocation += "filter=" + encoded; + } + + // Metadata fields to show. + if (showMetadata != null) { + for (final String metadataField : showMetadata) { + urlLocation += "&show=" + metadataField; + } + } + + LOGGER.info("findDatasets: " + urlLocation); + final StringBuffer outputBuffer = new StringBuffer(); + final int response = HttpUtilities.doGet(urlLocation, outputBuffer); + if (response >= 400) { + throw new RuntimeException("HTTP GET returned error code: " + response); + } + + // Build and return dataset list + final JSONObject searchResults = new JSONObject(outputBuffer.toString()); + LOGGER.info("returning search results: " + searchResults.toString()); + return DatasetUtilities.getDatasetsFromSearch(searchResults); + } + + /** + * Get a dataset from its path. + * + * @param path the path in the data catalog + * @return the dataset + */ + @Override + public Dataset getDataSet(final String path, final DatasetSite site) { + if (path == null) { + throw new IllegalArgumentException("The path is null."); + } + if (path.length() == 0) { + throw new IllegalArgumentException("The path is a blank string."); + } + if (site == null) { + throw new IllegalArgumentException("The site is null."); + } + String urlLocation = this.url + "/r/path.json/" + this.rootDir; + if (!path.startsWith("/")) { + urlLocation += "/"; + } + urlLocation += path + ";s=" + site.name(); + final StringBuffer output = new StringBuffer(); + HttpUtilities.doGet(urlLocation, output); + return new DatasetImpl(new JSONObject(output.toString())); + } + + /** + * Return <code>true</code> if path is a folder in the data catalog. + * + * @return <code>true</code> if path is a folder in the data catalog + */ + @Override + public boolean isFolder(final String path) { + if (path == null) { + throw new IllegalArgumentException("The path is null."); + } + if (path.length() == 0) { + throw new IllegalArgumentException("The path is a blank string."); + } + final String urlLocation = this.url + "/r/path.json/" + this.rootDir + "/" + path; + final StringBuffer output = new StringBuffer(); + final int status = HttpUtilities.doGet(urlLocation, output); + if (status > 400) { + throw new RuntimeException("HTTP GET query returned error status: " + status); + } + final JSONObject jsonObject = new JSONObject(output.toString()); + return jsonObject.has("_type") ? "folder".equals(jsonObject.getString("_type")) : false; + } + /** * 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>(); + public int makeFolder(final String path) { + final Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put("path", "/" + DatacatConstants.ROOT_DIR + "/" + path); - String name = new File(path).getName(); + final String name = new File(path).getName(); parameters.put("name", name); parameters.put("_type", "folder"); - JSONObject object = JSONUtilities.createJSONFromMap(parameters); - String urlLocation = url + "/r/folders.json/" + this.rootDir; + final JSONObject object = JSONUtilities.createJSONFromMap(parameters); + final 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 = 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()); - } - - /** - * Find datasets in the catalog. + + /** + * Remove a folder from the catalog. * <p> - * See <a href="http://docs.datacatalog.apiary.io/#search">Search Doc</a> for more details. - * + * It must be empty or an error will occur. + * * @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 urlLocation = this.url.toString() + "/r/search.json/" + this.rootDir + "/"; - if (directory != null) { - urlLocation += directory; - } - urlLocation += ";s=" + this.site.name() + "?"; - if (query != null) { - String encoded = null; - try { - encoded = URLEncoder.encode(query, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - 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(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); - } - - /** - * 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 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()); + * @return the HHTP status code from the request + */ + @Override + public int removeFolder(final String folder) { + final String fullUrl = url.toString() + "/r/folders.json/" + this.rootDir + folder; + LOGGER.info("removing folder: " + fullUrl); + return HttpUtilities.doDelete(fullUrl); } } Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatConstants.java ============================================================================= --- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatConstants.java (original) +++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatacatConstants.java Wed Sep 16 17:31:20 2015 @@ -15,6 +15,6 @@ /** * The base URL of the datacat server. */ - // FIXME: This needs to be more easily configurable and not hard-coded. + // FIXME: Development server location. public static final String BASE_URL = "http://localhost:8080/datacat-v0.4-SNAPSHOT"; } 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 Wed Sep 16 17:31:20 2015 @@ -4,9 +4,10 @@ import java.util.List; /** + * Representation of a dataset in the data catalog. + * * * @author Jeremy McCormick, SLAC - * */ public interface Dataset { @@ -18,7 +19,7 @@ String getName(); /** - * Get the path of the dataset e.g. "/HPS/folder/dataset01". + * Get the logical path of the dataset e.g. "/HPS/folder/dataset01". * * @return the path of the dataset */ Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetDataType.java ============================================================================= --- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetDataType.java (original) +++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetDataType.java Wed Sep 16 17:31:20 2015 @@ -1,14 +1,29 @@ package org.hps.datacat.client; /** + * Dataset types for HPS. * * @author Jeremy McCormick, SLAC - * */ public enum DatasetDataType { + /** + * Data quality management plots. + */ DQM, + /** + * Raw data (EVIO). + */ RAW, + /** + * Reconstructed data (usually LCIO). + */ RECON, + /** + * Digital Summary Tape files (ROOT). + */ DST, + /** + * Test type (don't use in production). + */ TEST; } Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetFileFormat.java ============================================================================= --- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetFileFormat.java (original) +++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetFileFormat.java Wed Sep 16 17:31:20 2015 @@ -1,14 +1,61 @@ package org.hps.datacat.client; + /** + * Dataset file formats for HPS. * * @author Jeremy McCormick, SLAC - * */ public enum DatasetFileFormat { - EVIO, - LCIO, - ROOT, - AIDA, - TEST; + + /** + * EVIO data format. + */ + EVIO(), + /** + * LCIO data format (note custom file extension). + */ + LCIO("slcio"), + /** + * ROOT files. + */ + ROOT(), + /** + * AIDA files. + */ + AIDA(), + /** + * Testing only (do not use in production). + */ + TEST(null); + + /** + * The file extension of the format. + */ + private String extension; + + /** + * Create a file format with an extension. + * + * @param extension the file's extension + */ + private DatasetFileFormat(String extension) { + this.extension = extension; + } + + /** + * Create a file format with default extension (lower case of enum name). + */ + private DatasetFileFormat() { + this.extension = this.name().toLowerCase(); + } + + /** + * Get the format's file extension. + * + * @return the format file extension + */ + public String extension() { + return extension; + } } 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 Wed Sep 16 17:31:20 2015 @@ -11,27 +11,106 @@ import org.json.JSONObject; /** + * Implementation of the {@link Dataset} interface. * * @author Jeremy McCormick, SLAC - * */ final class DatasetImpl implements Dataset { + /** + * The name of the dataset. + */ private String name; + + /** + * The path in the datacatalog which is folder + name. + */ private String path; + + /** + * The data type of the file. + */ private DatasetDataType dataType; + + /** + * The format of the file. + */ private DatasetFileFormat fileFormat; + + /** + * The list of file locations. + */ private List<DatasetLocation> locations = new ArrayList<DatasetLocation>(); + + /** + * The creation date. + */ private Date created; + + /** + * The dataset's metadata. + */ private DatasetMetadata metadata; + /** + * Parser for reading in dates from JSON. + */ private static final SimpleDateFormat DATE_PARSER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + /** + * Create a new dataset from JSON. + * + * @param jsonObject the JSON data + */ DatasetImpl(JSONObject jsonObject) { - parse(jsonObject); - } + if (!jsonObject.has("_type")) { + throw new IllegalArgumentException("JSON object is missing _type field."); + } + if (!jsonObject.getString("_type").startsWith("dataset")) { + throw new IllegalArgumentException("JSON _type field is not a dataset: " + jsonObject.getString("_type")); + } - private void parse(JSONObject jsonObject) { + if (jsonObject.getString("_type").equals("dataset#flat")) { + parseFlat(jsonObject); + } else if (jsonObject.getString("_type").equals("dataset#full")) { + parseFull(jsonObject); + } else { + throw new IllegalArgumentException("Unknown dataset type: " + jsonObject.getString("_type")); + } + } + + /** + * Parse the flat JSON representation. + * + * @param jsonObject the JSON object + */ + private void parseFlat(JSONObject jsonObject) { + name = jsonObject.getString("name"); + path = jsonObject.getString("path"); + DatasetLocationImpl location = new DatasetLocationImpl( + DatasetSite.valueOf(jsonObject.getString("site")), + jsonObject.getString("resource"), + jsonObject.getInt("size"), + ScanStatus.valueOf(jsonObject.getString("scanStatus"))); + location.setEventCount(jsonObject.getInt("eventCount")); + location.setRunMin(jsonObject.getInt("runMin")); + location.setRunMax(jsonObject.getInt("runMax")); + locations.add(location); + dataType = DatasetDataType.valueOf(jsonObject.getString("dataType")); + fileFormat = DatasetFileFormat.valueOf(jsonObject.getString("fileFormat")); + try { + created = DATE_PARSER.parse(jsonObject.getString("created")); + } catch (ParseException e) { + throw new IllegalArgumentException("Bad created value: " + jsonObject.getString("created"), e); + } + } + + /** + * Parse the full JSON representation. + * + * @param jsonObject the JSON object + */ + private void parseFull(JSONObject jsonObject) { if (!jsonObject.getString("_type").equals("dataset#full")) { throw new IllegalArgumentException("Wrong _type in JSON data: " + jsonObject.getString("_type")); } @@ -53,41 +132,81 @@ } } + /** + * Get the name of the dataset without the path component e.g. "dataset01". + * + * @return the name of the dataset + */ @Override public String getName() { return this.name; } + /** + * Get the logical path of the dataset e.g. "/HPS/folder/dataset01". + * + * @return the path of the dataset + */ @Override public String getPath() { return this.path; } + /** + * Get the dataset locations. + * + * @return the dataset locations + */ @Override public List<DatasetLocation> getLocations() { return Collections.unmodifiableList(this.locations); } + /** + * Get the file format e.g. EVIO, LCIO, etc. + * + * @return the dataset file format + */ @Override public DatasetFileFormat getFileFormat() { return this.fileFormat; } + /** + * Get the data type e.g. RAW, RECON, etc. + * + * @return the data type + */ @Override public DatasetDataType getDataType() { return this.dataType; } + /** + * Get the creation date. + * + * @return the creation date + */ @Override public Date getCreated() { return this.created; } + /** + * Get the dataset's metadata. + * + * @return the dataset's metadata + */ @Override public DatasetMetadata getMetadata() { return this.metadata; } + /** + * Convert this object to a string. + * + * @return this object converted to a string + */ 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/DatasetLocation.java ============================================================================= --- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetLocation.java (original) +++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetLocation.java Wed Sep 16 17:31:20 2015 @@ -1,23 +1,61 @@ package org.hps.datacat.client; /** + * Representation of a dataset location in the data catalog. * * @author Jeremy McCormick, SLAC - * */ public interface DatasetLocation { + /** + * Get the site of the dataset (JLAB or SLAC). + * + * @return the dataset site + */ DatasetSite getSite(); + /** + * Get the resource of the dataset location (file system path). + * + * @return the resource of the dataset location + */ String getResource(); + /** + * Get the scan status of the dataset location. + * + * @return the scan status + */ ScanStatus getScanStatus(); + /** + * The size of the file in bytes. + * + * @return the size of the file in bytes + */ long getSize(); + /** + * Get the minimum run number. + * + * @return the minimum run number + */ + // FIXME: Belongs in dataset metadata. int getRunMin(); + /** + * Get the maximum run number. + * + * @return the maximum run number + */ + // FIXME: Belongs in dataset metadata. int getRunMax(); + /** + * Get the event count. + * + * @return the event count + */ + // FIXME: Belongs in dataset metadata. int getEventCount(); } Modified: java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetLocationImpl.java ============================================================================= --- java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetLocationImpl.java (original) +++ java/trunk/datacat-client/src/main/java/org/hps/datacat/client/DatasetLocationImpl.java Wed Sep 16 17:31:20 2015 @@ -3,23 +3,76 @@ import org.json.JSONObject; /** + * Implementation of {@link DatasetLocation} interface. * * @author Jeremy McCormick, SLAC */ final class DatasetLocationImpl implements DatasetLocation { + /** + * The dataset's site. + */ private DatasetSite site; + + /** + * The resource on the file system. + */ private String resource; + + /** + * The scan status. + */ private ScanStatus scanStatus = ScanStatus.UNKNOWN; + + /** + * The size of the file in bytes. + */ private long size; + + /** + * The minimum run number. + */ private int runMin; + + /** + * The maximum run number. + */ private int runMax; + + /** + * The event count. + */ private int eventCount; + /** + * Create a dataset location. + * + * @param site the site of the dataset location + * @param resource the source on disk + * @param size the size of the file + * @param scanStatus the scan status + */ + DatasetLocationImpl(DatasetSite site, String resource, long size, ScanStatus scanStatus) { + this.site = site; + this.resource = resource; + this.scanStatus = scanStatus; + this.size = size; + } + + /** + * Create a dataset location from JSON. + * + * @param jsonObject the JSON object + */ DatasetLocationImpl(JSONObject jsonObject) { parse(jsonObject); } + /** + * Parse JSON data into this object. + * + * @param jsonObject the JSON data + */ private void parse(JSONObject jsonObject) { if (!jsonObject.getString("_type").equals("location")) { throw new IllegalArgumentException("Wrong _type in JSON data: " + jsonObject.getString("_type")); @@ -35,39 +88,100 @@ this.eventCount = jsonObject.getInt("eventCount"); } + /** + * Get the site of the dataset (JLAB or SLAC). + * + * @return the dataset site + */ @Override public DatasetSite getSite() { return this.site; } + /** + * Get the resource of the dataset location (file system path). + * + * @return the resource of the dataset location + */ @Override public String getResource() { return resource; } + /** + * Get the scan status of the dataset location. + * + * @return the scan status + */ @Override public ScanStatus getScanStatus() { return scanStatus; } + /** + * The size of the file in bytes. + * + * @return the size of the file in bytes + */ @Override public long getSize() { return this.size; } + /** + * Get the minimum run number. + * + * @return the minimum run number + */ @Override public int getRunMin() { return this.runMin; } + /** + * Get the maximum run number. + * + * @return the maximum run number + */ @Override public int getRunMax() { return this.runMax; } - + + /** + * Get the event count. + * + * @return the event count + */ @Override public int getEventCount() { return this.eventCount; } - + + /** + * Set the minimum run number. + * + * @param runMin the minimum run number + */ + void setRunMin(int runMin) { + this.runMin = runMin; + } + + /** + * Set the maximum run number. + * + * @param runMax the maximum run number + */ + void setRunMax(int runMax) { + this.runMax = runMax; + } + + /** + * Set the event count. + * + * @param eventCount the event count + */ + void setEventCount(int eventCount) { + this.eventCount = eventCount; + } } 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 Wed Sep 16 17:31:20 2015 @@ -1,8 +1,7 @@ package org.hps.datacat.client; - /** - * Dataset metadata which is keys and values that are double, integer or string. + * Dataset metadata which is string keys and values that are double, integer or string. * * @author Jeremy McCormick, SLAC */ 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 Wed Sep 16 17:31:20 2015 @@ -1,9 +1,9 @@ package org.hps.datacat.client; /** + * Site of a dataset (SLAC or JLAB). * * @author Jeremy McCormick, SLAC - * */ public enum DatasetSite { /** 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 Wed Sep 16 17:31:20 2015 @@ -7,7 +7,7 @@ import org.json.JSONObject; /** - * Dataset utilities. + * Dataset utilities for the crawler. * * @author Jeremy McCormick, SLAC */ 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 Wed Sep 16 17:31:20 2015 @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; -import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -19,50 +18,39 @@ import org.apache.http.util.EntityUtils; /** - * * @author Jeremy McCormick, SLAC */ final class HttpUtilities { /** - * Do an HTTP POST. - * + * Do an HTTP DELETE. + * * @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) { + static int doDelete(final String urlLocation) { int responseCode = 0; try { - URL url = new URL(urlLocation); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); + final URL url = new URL(urlLocation); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); - if (data != null) { - OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); - out.write(data); - out.close(); - } - System.out.println("url: " + urlLocation); - System.out.println("data: " + data); - System.out.println("response: " + connection.getResponseCode()); - System.out.println("message: " + connection.getResponseMessage()); + connection.setRequestMethod("DELETE"); + connection.connect(); responseCode = connection.getResponseCode(); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException(e); } 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) { + static int doGet(final String urlLocation, final StringBuffer stringBuffer) { HttpURLConnection connection = null; int response = 0; try { @@ -73,94 +61,83 @@ connection.setDoInput(true); connection.connect(); if (stringBuffer != null) { - String output = IOUtils.toString(connection.getInputStream(), "UTF-8"); + final String output = IOUtils.toString(connection.getInputStream(), "UTF-8"); stringBuffer.append(output); } response = connection.getResponseCode(); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException(e); } finally { connection.disconnect(); } 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(); + static int doPatch(final String urlLocation, final String data) { + int responseCode = 0; + final CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPatch httpPatch = null; try { - httpPatch = new HttpPatch(new URI(urlLocation)); - InputStreamEntity entity = - new InputStreamEntity( - new ByteArrayInputStream( - data.getBytes("UTF-8")), - -1, - ContentType.APPLICATION_JSON); - httpPatch.setEntity(entity); - CloseableHttpResponse response = httpClient.execute(httpPatch); + httpPatch = new HttpPatch(new URI(urlLocation)); + final InputStreamEntity entity = new InputStreamEntity(new ByteArrayInputStream(data.getBytes("UTF-8")), + -1, ContentType.APPLICATION_JSON); + httpPatch.setEntity(entity); + final CloseableHttpResponse response = httpClient.execute(httpPatch); try { EntityUtils.consume(response.getEntity()); } finally { response.close(); } responseCode = response.getStatusLine().getStatusCode(); - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { throw new IllegalArgumentException(e); - } catch(IOException e) { + } catch (final IOException e) { throw new RuntimeException(e); } finally { try { httpClient.close(); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException(e); } - } + } return responseCode; - } - + } + /** - * Do an HTTP DELETE. - * + * 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 doDelete(String urlLocation) { + static int doPost(final String urlLocation, final String data) { int responseCode = 0; try { - URL url = new URL(urlLocation); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + final URL url = new URL(urlLocation); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); connection.setDoOutput(true); - connection.setRequestMethod("DELETE"); - connection.connect(); + if (data != null) { + final OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); + out.write(data); + out.close(); + } + System.out.println("url: " + urlLocation); + System.out.println("data: " + data); + System.out.println("response: " + connection.getResponseCode()); + System.out.println("message: " + connection.getResponseMessage()); responseCode = connection.getResponseCode(); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException(e); } return responseCode; - } - - /* - static URL createURL(String... chunks) { - if (chunks.length == 0) { - throw new IllegalArgumentException("No arguments provided."); - } - String urlString = ""; - for (String chunk : chunks) { - urlString += chunk; - } - try { - return new URL(urlString); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Bad URL string: " + urlString); - } - } - */ + } } Modified: java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatClientTest.java ============================================================================= --- java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatClientTest.java (original) +++ java/trunk/datacat-client/src/test/java/org/hps/datacat/client/DatacatClientTest.java Wed Sep 16 17:31:20 2015 @@ -9,68 +9,68 @@ import junit.framework.TestCase; /** - * * @author Jeremy McCormick, SLAC - * */ public class DatacatClientTest extends TestCase { - + private static final String DATASET_NAME = "dummyDataset"; private static final String FOLDER = "dummyFolder"; private static final String RESOURCE = "/path/to/dummyDataset.ds"; - + public void testDatacat() throws Exception { // Datacat client with default parameters. - DatacatClient client = new DatacatClientImpl(); - + final DatacatClient client = new DatacatClientImpl(); + // Stores response from HTTP operations int response = -1; - + // Create dummy folder. response = client.makeFolder("dummyFolder"); - + + // TODO: check that folder exists + assertTrue(client.isFolder("dummyFolder")); + // Add dummy dataset. - Map<String, Object> dsMetadata = new HashMap<String, Object>(); + final 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); - + response = client.addDataset(FOLDER, DatasetDataType.TEST, RESOURCE, DatasetSite.SLAC, DatasetFileFormat.TEST, + DATASET_NAME, dsMetadata); + // Patch the dataset with some meta data. - Map<String, Object> metaData = new HashMap<String, Object>(); + final Map<String, Object> metaData = new HashMap<String, Object>(); metaData.put("testInt2", 1234); response = client.addMetadata(FOLDER, DATASET_NAME, metaData); - - // TODO: check that folder exists - - // TODO: check that dataset exists - + // TODO: get the full folder info - - // TODO: get the full dataset info - + + // Get the dataset info back. + final Dataset gimmeDataset = client.getDataSet(FOLDER + "/" + DATASET_NAME, DatasetSite.SLAC); + System.out.println("got dataset " + gimmeDataset.getName()); + // Find the dataset with a simple query. - Set<String> metadataFields = new HashSet<String>(); + final Set<String> metadataFields = new HashSet<String>(); metadataFields.add("testInt"); - metadataFields.add("testFloat"); + metadataFields.add("testFloat"); metadataFields.add("testDouble"); metadataFields.add("testString"); - List<Dataset> datasets = client.findDatasets(FOLDER, "testInt == 1", metadataFields); - for (Dataset dataset : datasets) { + final List<Dataset> datasets = client.findDatasets(FOLDER, "testInt == 1", metadataFields); + for (final Dataset dataset : datasets) { System.out.println("found dataset: " + dataset.getName()); System.out.println("metadata: " + dataset.getMetadata()); } - + // Delete the dataset. response = client.deleteDataset("/" + FOLDER + "/" + DATASET_NAME); System.out.println("deleteDataset: " + response); System.out.println(); - + // Remove the folder. client.removeFolder("/" + FOLDER); System.out.println("removeFolder: " + response); System.out.println(); - } + } }