URLProductStorage.java

/*
 * URLProductStorage
 */
package gov.usgs.earthquake.distribution;

import gov.usgs.earthquake.product.ProductId;

import gov.usgs.earthquake.product.io.BinaryProductHandler;
import gov.usgs.earthquake.product.io.BinaryProductSource;
import gov.usgs.earthquake.product.io.JsonProductHandler;
import gov.usgs.earthquake.product.io.JsonProductSource;
import gov.usgs.earthquake.product.io.ProductSource;
import gov.usgs.earthquake.product.io.ProductHandler;
import gov.usgs.earthquake.product.io.XmlProductSource;
import gov.usgs.earthquake.product.io.XmlProductHandler;
import gov.usgs.util.Config;
import gov.usgs.util.StreamUtils;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;

import java.net.URL;
import java.util.logging.Logger;

/**
 * Store products in a file system which is also available at a URL.
 */
public class URLProductStorage extends FileProductStorage {

  /** Different types of formats */
  public enum Format {
    /** Enum for BINARY/bin format */
    BINARY("bin"),
    /** Enum for JSON format */
    JSON("json"),
    /** Enum for XML format */
    XML("xml");

    private String value;

    private Format(final String value) {
      this.value = value;
    }

    public String toString() {
      return this.value;
    }

    /**
     * Takes a string value and returns ENUM of its format
     *
     * @param value String
     * @return Format ENUM
     */
    public static Format fromString(final String value) {
      if (BINARY.value.equals(value)) {
        return BINARY;
      } else if (JSON.value.equals(value)) {
        return JSON;
      } else if (XML.value.equals(value)) {
        return XML;
      } else {
        throw new IllegalArgumentException("Invalid format");
      }
    }
  };

  private static final Logger LOGGER = Logger.getLogger(URLProductStorage.class.getName());

  /** Property name representing base URL. */
  public static final String URL_PROPERTY_NAME = "url";

  /** The URL which corresponds to baseDirectory. */
  private URL baseURL;

  /** Property for storageFormat */
  public static final String STORAGE_FORMAT_PROPERTY = "storageFormat";

  /** Property for storagePath */
  public static final String STORAGE_PATH_PROPERTY = "storagePath";
  /** Sets up default storage path */
  public static final String DEFAULT_STORAGE_PATH = "{source}_{type}_{code}_{updateTime}.{format}";

  /**
   * (Deprecated, use STORAGE_PATH) Property name to configure binary or xml
   * format.
   */
  public static final String BINARY_FORMAT_PROPERTY = "binaryFormat";
  /** Default value for whether to use binary format. */
  public static final String BINARY_FORMAT_DEFAULT = "false";

  private Format storageFormat = Format.XML;
  private String storagePath = DEFAULT_STORAGE_PATH;

  /**
   * Constructor for the Configurable interface.
   */
  public URLProductStorage() {
  }

  /**
   * Construct a new ProductStorage object
   *
   * @param baseDirectory the storage directory where products are stored.
   * @param baseURL       the url where storage directory is available.
   */
  public URLProductStorage(final File baseDirectory, final URL baseURL) {
    super(baseDirectory);
    this.baseURL = baseURL;
  }

  /**
   * Load the baseURL from configuration.
   *
   * @param config the configuration object.
   */
  public void configure(final Config config) throws Exception {
    super.configure(config);

    String urlString = config.getProperty(URL_PROPERTY_NAME);
    if (urlString == null) {
      throw new ConfigurationException("[" + getName() + "] 'url' is a required configuration property");
    }
    baseURL = new URL(urlString);
    LOGGER.config("[" + getName() + "] base url is '" + baseURL.toString() + "'");

    String format = config.getProperty(STORAGE_FORMAT_PROPERTY);
    if (format != null) {
      storageFormat = Format.fromString(format);
    } else {
      if (Boolean.valueOf(config.getProperty(BINARY_FORMAT_PROPERTY, BINARY_FORMAT_DEFAULT))) {
        storageFormat = Format.BINARY;
      } else {
        storageFormat = Format.XML;
      }
    }
    LOGGER.config("[" + getName() + "] using format " + storageFormat);

    storagePath = config.getProperty(STORAGE_PATH_PROPERTY, DEFAULT_STORAGE_PATH);
    LOGGER.config("[" + getName() + "] using path " + storagePath);
  }

  /**
   * Compute the URL to a product.
   *
   * @param id which product.
   * @return the URL to a product.
   * @throws Exception if error occurs
   */
  public URL getProductURL(final ProductId id) throws Exception {
    return new URL(baseURL, getProductPath(id));
  }

  /**
   * A method for subclasses to override the storage path.
   *
   * The returned path is appended to the base directory when storing and
   * retrieving products.
   *
   * @param id the product id to convert.
   * @return the directory used to store id.
   */
  @Override
  public String getProductPath(final ProductId id) {
    String path = storagePath;
    path = path.replace("{source}", id.getSource());
    path = path.replace("{type}", id.getType());
    path = path.replace("{code}", id.getCode());
    path = path.replace("{updateTime}", Long.toString(id.getUpdateTime().getTime()));
    path = path.replace("{format}", storageFormat.toString());
    return path;
  }

  /**
   * A method for subclasses to override the storage format.
   *
   * When overriding this method, the method getProductInputForFile should also be
   * overridden.
   *
   * @param file a file that should be converted into a ProductOutput.
   * @return the ProductOutput.
   */
  protected ProductHandler getProductHandlerFormat(final File file) throws Exception {
    OutputStream out = StreamUtils.getOutputStream(file);
    if (storageFormat == Format.BINARY) {
      return new BinaryProductHandler(out);
    } else if (storageFormat == Format.JSON) {
      return new JsonProductHandler(out);
    } else {
      return new XmlProductHandler(out);
    }
  }

  /**
   * A method for subclasses to override the storage format.
   *
   * When overriding this method, the method getProductOutputForFile should also
   * be overridden.
   *
   * @param file a file that should be converted into a ProductInput.
   * @return the ProductInput.
   */
  protected ProductSource getProductSourceFormat(final File file) throws Exception {
    InputStream in = StreamUtils.getInputStream(file);
    if (storageFormat == Format.BINARY) {
      return new BinaryProductSource(in);
    } else if (storageFormat == Format.JSON) {
      return new JsonProductSource(in);
    } else {
      return new XmlProductSource(in);
    }
  }

  /** @return storageFormat */
  public Format getStorageFormat() {
    return this.storageFormat;
  }

  /** @param format set a storageFormat */
  public void setStorageFormat(final Format format) {
    this.storageFormat = format;
  }

  /** @return storagePath */
  public String getStoragePath() {
    return this.storagePath;
  }

  /** @param path set a string as the storagePath */
  public void setStoragePath(final String path) {
    this.storagePath = path;
  }

}