Product.java
- /*
- * Product
- */
- package gov.usgs.earthquake.product;
- import gov.usgs.util.CryptoUtils;
- import gov.usgs.util.XmlUtils;
- import gov.usgs.util.CryptoUtils.Version;
- import java.security.PublicKey;
- import java.security.PrivateKey;
- import java.math.BigDecimal;
- import java.net.URI;
- import java.net.URL;
- import java.util.Date;
- import java.util.List;
- import java.util.LinkedList;
- import java.util.Map;
- import java.util.HashMap;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- /**
- * One or more pieces of Content with metadata.
- *
- * <dl>
- * <dt><strong>ID</strong></dt>
- * <dd>
- * Products each have a unique {@link ProductId}.
- * </dd>
- *
- * <dt><strong>Versioning</strong></dt>
- * <dd>
- * It is possible to create multiple versions of the same product,
- * by reusing the same <code>source</code>, <code>type</code>, and
- * <code>code</code>, with a different <code>updateTime</code>.
- * <br>
- * More recent (newer) <code>updateTime</code>s <strong>supersede</strong>
- * Less recent (older) <code>updateTime</code>s.
- * </dd>
- *
- * <dt><strong>Status</strong></dt>
- * <dd>
- * To <strong>delete</strong> a product, create a new version (updateTime)
- * and set it's status to {@link STATUS_DELETE}. All other statuses
- * ({@link STATUS_UPDATE} by default) are considered updates, and any
- * value can be used in product-specific ways.
- * </dd>
- *
- * <dt><strong>Properties</strong></dt>
- * <dd>
- * Products have key/value attributes that are Strings.
- * These can be useful to convey summary information about a product,
- * so consumers can quickly decide whether to process before opening
- * any product contents.
- * </dd>
- *
- * <dt><strong>Links</strong></dt>
- * <dd>
- * Similar to properties, links allow a Product to specify a
- * <code>relation</code> and one or more <code>link</code> for each
- * relation type.
- * Links must be {@link java.net.URI}s, and may be {@link ProductId}s.
- * </dd>
- *
- * <dt><strong>Contents</strong></dt>
- * <dd>
- * Many Products start as a directory of files, and metadata is determined later.
- * It's also possible to create products without any Contents attached,
- * if all the necessary information can be encoded using Properties or Links.
- * <br>
- * One special "empty path" content, literally at the empty-string path,
- * is handled differently; since an empty path cannot be written to a file.
- * PDL typically reads this in from standard input, or delivers this on
- * standard input to external processes.
- * </dd>
- *
- * <dt><strong>Signature</strong></dt>
- * <dd>
- * A product can have a digital signature, based on a digest of all
- * product contents and metadata. These are required for most purposes.
- * {@link CryptoUtils} provides utilities for working with OpenSSH keypairs.
- * </dd>
- *
- * <dt><strong>Tracker URL (Deprecated)</strong></dt>
- * <dd>
- * Tracker URLs were initially used to track processing status as
- * distribution progressed. These are no longer supported, and often
- * introduced new problems.
- * </dd>
- * </dl>
- */
- public class Product {
- private static final Logger LOGGER = Logger.getLogger(Product.class
- .getName());
- /** The status message when a product is being updated. */
- public static final String STATUS_UPDATE = "UPDATE";
- /** The status message when a product is being deleted. */
- public static final String STATUS_DELETE = "DELETE";
- /** Property for eventsource */
- public static final String EVENTSOURCE_PROPERTY = "eventsource";
- /** Property for eventsourcecode */
- public static final String EVENTSOURCECODE_PROPERTY = "eventsourcecode";
- /** Property for eventtime */
- public static final String EVENTTIME_PROPERTY = "eventtime";
- /** Property for magnitude */
- public static final String MAGNITUDE_PROPERTY = "magnitude";
- /** Property for latitude */
- public static final String LATITUDE_PROPERTY = "latitude";
- /** Property for longitude */
- public static final String LONGITUDE_PROPERTY = "longitude";
- /** Property for depth */
- public static final String DEPTH_PROPERTY = "depth";
- /** Property for version */
- public static final String VERSION_PROPERTY = "version";
- /** A unique identifier for this product. */
- private ProductId id;
- /** A terse status message. */
- private String status;
- /** Properties of this product. */
- private Map<String, String> properties = new HashMap<String, String>();
- /** Links to other products and related resources. */
- private Map<String, List<URI>> links = new HashMap<String, List<URI>>();
- /** Product contents. Mapping from path to content. */
- private Map<String, Content> contents = new HashMap<String, Content>();
- /** A URL where status updates are sent. */
- private URL trackerURL = null;
- /** A signature generated by the product creator. */
- private String signature = null;
- /** Signature version. */
- private Version signatureVersion = Version.SIGNATURE_V1;
- /**
- * Construct a new Product with status "UPDATE".
- *
- * @param id
- * the product's unique Id.
- */
- public Product(final ProductId id) {
- this(id, STATUS_UPDATE);
- }
- /**
- * Construct a new Product.
- *
- * @param id
- * the product's unique Id.
- * @param status
- * the product's status.
- */
- public Product(final ProductId id, final String status) {
- setId(id);
- setStatus(status);
- }
- /**
- * Copy constructor.
- *
- * @param that
- * the product to copy.
- */
- public Product(final Product that) {
- this(new ProductId(that.getId().getSource(), that.getId().getType(),
- that.getId().getCode(), that.getId().getUpdateTime()), that
- .getStatus());
- this.setTrackerURL(that.getTrackerURL());
- this.setProperties(that.getProperties());
- this.setLinks(that.getLinks());
- this.setContents(that.getContents());
- this.setSignature(that.getSignature());
- }
- /**
- * @return the id
- */
- public ProductId getId() {
- return id;
- }
- /**
- * @param id
- * the id to set
- */
- public void setId(final ProductId id) {
- this.id = id;
- }
- /**
- * @return the status
- */
- public String getStatus() {
- return status;
- }
- /**
- * @param status
- * the status to set
- */
- public void setStatus(final String status) {
- this.status = status;
- }
- /**
- * Product.STATUS_DELETE.equalsIgnoreCase(status).
- *
- * @return whether this product is deleted
- */
- public boolean isDeleted() {
- if (STATUS_DELETE.equalsIgnoreCase(this.status)) {
- return true;
- } else {
- return false;
- }
- }
- /**
- * @return the properties
- */
- public Map<String, String> getProperties() {
- return properties;
- }
- /**
- * @param properties
- * the properties to set
- */
- public void setProperties(final Map<String, String> properties) {
- this.properties.putAll(properties);
- }
- /**
- * Returns a reference to the links map.
- *
- * @return the links
- */
- public Map<String, List<URI>> getLinks() {
- return links;
- }
- /**
- * Copies entries from provided map.
- *
- * @param links
- * the links to set
- */
- public void setLinks(final Map<String, List<URI>> links) {
- this.links.putAll(links);
- }
- /**
- * Add a link to a product.
- *
- * @param relation
- * how link is related to product.
- * @param href
- * actual link.
- */
- public void addLink(final String relation, final URI href) {
- List<URI> relationLinks = links.get(relation);
- if (relationLinks == null) {
- relationLinks = new LinkedList<URI>();
- links.put(relation, relationLinks);
- }
- relationLinks.add(href);
- }
- /**
- * Returns a reference to the contents map.
- *
- * @return the contents
- */
- public Map<String, Content> getContents() {
- return contents;
- }
- /**
- * Copies entries from provided map.
- *
- * @param contents
- * the contents to set
- */
- public void setContents(final Map<String, Content> contents) {
- this.contents.clear();
- this.contents.putAll(contents);
- }
- /**
- * @return the trackerURL
- */
- public URL getTrackerURL() {
- return trackerURL;
- }
- /**
- * @param trackerURL
- * the trackerURL to set
- */
- public void setTrackerURL(final URL trackerURL) {
- this.trackerURL = trackerURL;
- }
- /**
- * @return the signature
- */
- public String getSignature() {
- return signature;
- }
- /**
- * @return the signature
- */
- public Version getSignatureVersion() {
- return signatureVersion;
- }
- /**
- * @param signature
- * the signature to set
- */
- public void setSignature(final String signature) {
- this.signature = signature;
- }
- /**
- * @param version
- * the signature version to set
- */
- public void setSignatureVersion(final Version version) {
- this.signatureVersion = version;
- }
- /**
- * Sign this product using a PrivateKey and signature v1.
- * @param privateKey used to sign
- * @throws Exception if error occurs
- */
- public void sign(final PrivateKey privateKey) throws Exception {
- this.sign(privateKey, Version.SIGNATURE_V1);
- }
- /**
- * Sign this product using a PrivateKey.
- *
- * @param privateKey
- * a DSAPrivateKey or RSAPrivateKey.
- * @param version
- * the signature version to use.
- * @throws Exception if error occurs
- */
- public void sign(final PrivateKey privateKey, final Version version) throws Exception {
- setSignature(CryptoUtils.sign(
- privateKey,
- ProductDigest.digestProduct(this, version),
- version));
- setSignatureVersion(version);
- }
- /**
- * Verify this product's signature using Signature V1.
- * @param publicKeys Array of public keys to verify
- * @throws Exception if error occurs
- * @return true if valid, false otherwise.
- */
- public boolean verifySignature(final PublicKey[] publicKeys)
- throws Exception {
- return verifySignature(publicKeys, getSignatureVersion());
- }
- /**
- * Verify this product's signature.
- *
- * When a product has no signature, this method returns false. The array of
- * public keys corresponds to one or more keys that may have generated the
- * signature. If any of the keys verify, this method returns true.
- *
- * @param publicKeys
- * an array of publicKeys to test.
- * @param version
- * the signature version to use.
- * @return true if valid, false otherwise.
- * @throws Exception if error occurs
- */
- public boolean verifySignature(final PublicKey[] publicKeys, final Version version)
- throws Exception {
- return verifySignatureKey(publicKeys, version) != null;
- }
- /**
- * Try to verify using multiple candidate keys.
- * @param publicKeys an array of publicKeys to test
- * @param version the signature version to use.
- * @return true if valid, false otherwise.
- * @throws Exception if error occurs
- */
- public PublicKey verifySignatureKey(final PublicKey[] publicKeys, final Version version) throws Exception {
- if (signature == null) {
- return null;
- }
- byte[] digest = ProductDigest.digestProduct(this, version);
- for (PublicKey key : publicKeys) {
- try {
- if (CryptoUtils.verify(key, digest, getSignature(), version)) {
- return key;
- }
- } catch (Exception e) {
- LOGGER.log(Level.FINEST, "Exception while verifying signature",
- e);
- }
- }
- return null;
- }
- /**
- * Get the event id.
- *
- * The event id is the combination of event source and event source code.
- *
- * @return the event id, or null if either event source or event source code
- * is null.
- */
- public String getEventId() {
- String eventSource = getEventSource();
- String eventSourceCode = getEventSourceCode();
- if (eventSource == null && eventSourceCode == null) {
- return null;
- }
- return (eventSource + eventSourceCode).toLowerCase();
- }
- /**
- * Set both the network and networkId at the same time.
- *
- * @param source
- * the originating network.
- * @param sourceCode
- * the originating network's id.
- */
- public void setEventId(final String source, final String sourceCode) {
- setEventSource(source);
- setEventSourceCode(sourceCode);
- }
- /**
- * Get the event source property.
- *
- * @return the event source property, or null if no event source property
- * set.
- */
- public String getEventSource() {
- return this.properties.get(EVENTSOURCE_PROPERTY);
- }
- /**
- * Set the event source property.
- *
- * @param eventSource
- * the event source to set.
- */
- public void setEventSource(final String eventSource) {
- if (eventSource == null) {
- this.properties.remove(EVENTSOURCE_PROPERTY);
- } else {
- this.properties
- .put(EVENTSOURCE_PROPERTY, eventSource.toLowerCase());
- }
- }
- /**
- * Get the event source code property.
- *
- * @return the event source code property, or null if no event source code
- * property set.
- */
- public String getEventSourceCode() {
- return this.properties.get(EVENTSOURCECODE_PROPERTY);
- }
- /**
- * Set the event id property.
- *
- * @param eventSourceCode
- * the event id to set.
- */
- public void setEventSourceCode(final String eventSourceCode) {
- if (eventSourceCode == null) {
- this.properties.remove(EVENTSOURCECODE_PROPERTY);
- } else {
- this.properties.put(EVENTSOURCECODE_PROPERTY,
- eventSourceCode.toLowerCase());
- }
- }
- /**
- * Get the event time property as a date.
- *
- * @return the event time property as a date, or null if no date property
- * set.
- */
- public Date getEventTime() {
- String strDate = this.properties.get(EVENTTIME_PROPERTY);
- if (strDate == null) {
- return null;
- }
- return XmlUtils.getDate(strDate);
- }
- /**
- * Set the event time property as a date.
- *
- * @param eventTime
- * the event time to set.
- */
- public void setEventTime(final Date eventTime) {
- if (eventTime == null) {
- this.properties.remove(EVENTTIME_PROPERTY);
- } else {
- this.properties.put(EVENTTIME_PROPERTY,
- XmlUtils.formatDate(eventTime));
- }
- }
- /**
- * Get the magnitude property as a big decimal.
- *
- * @return the magnitude property as a big decimal, or null if no magnitude
- * property set.
- */
- public BigDecimal getMagnitude() {
- String strMag = this.properties.get(MAGNITUDE_PROPERTY);
- if (strMag == null) {
- return null;
- }
- return new BigDecimal(strMag);
- }
- /**
- * Set the magnitude property as a big decimal.
- *
- * @param magnitude
- * the magnitude to set.
- */
- public void setMagnitude(final BigDecimal magnitude) {
- if (magnitude == null) {
- this.properties.remove(MAGNITUDE_PROPERTY);
- } else {
- this.properties.put(MAGNITUDE_PROPERTY, magnitude.toPlainString());
- }
- }
- /**
- * Get the latitude property as a big decimal.
- *
- * @return latitude property as a big decimal, or null if no latitude
- * property set.
- */
- public BigDecimal getLatitude() {
- String strLat = this.properties.get(LATITUDE_PROPERTY);
- if (strLat == null) {
- return null;
- }
- return new BigDecimal(strLat);
- }
- /**
- * Set the latitude property as a big decimal.
- *
- * @param latitude
- * the latitude to set.
- */
- public void setLatitude(final BigDecimal latitude) {
- if (latitude == null) {
- this.properties.remove(LATITUDE_PROPERTY);
- } else {
- this.properties.put(LATITUDE_PROPERTY, latitude.toPlainString());
- }
- }
- /**
- * Get the longitude property as a big decimal.
- *
- * @return longitude property as a big decimal, or null if no longitude
- * property set.
- */
- public BigDecimal getLongitude() {
- String strLon = this.properties.get(LONGITUDE_PROPERTY);
- if (strLon == null) {
- return null;
- }
- return new BigDecimal(strLon);
- }
- /**
- * Set the longitude property as a big decimal.
- *
- * @param longitude
- * the longitude to set.
- */
- public void setLongitude(final BigDecimal longitude) {
- if (longitude == null) {
- this.properties.remove(LONGITUDE_PROPERTY);
- } else {
- this.properties.put(LONGITUDE_PROPERTY, longitude.toPlainString());
- }
- }
- /**
- * Get the depth property as a big decimal.
- *
- * @return depth property as big decimal, or null if no depth property set.
- */
- public BigDecimal getDepth() {
- String strDepth = this.properties.get(DEPTH_PROPERTY);
- if (strDepth == null) {
- return null;
- }
- return new BigDecimal(strDepth);
- }
- /**
- * Set the depth property as a big decimal.
- *
- * @param depth
- * the depth to set.
- */
- public void setDepth(final BigDecimal depth) {
- if (depth == null) {
- this.properties.remove(DEPTH_PROPERTY);
- } else {
- this.properties.put(DEPTH_PROPERTY, depth.toPlainString());
- }
- }
- /**
- * Get the version property.
- *
- * @return the version property, or null if no version property set.
- */
- public String getVersion() {
- return this.properties.get(VERSION_PROPERTY);
- }
- /**
- * Set the version property.
- *
- * @param version
- * the version to set.
- */
- public void setVersion(final String version) {
- if (version == null) {
- this.properties.remove(VERSION_PROPERTY);
- } else {
- this.properties.put(VERSION_PROPERTY, version);
- }
- }
- }