XmlProductHandler.java
/*
* XmlProductHandler
*/
package gov.usgs.earthquake.product.io;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.util.Base64;
import gov.usgs.util.StreamUtils;
import gov.usgs.util.XmlUtils;
import gov.usgs.util.CryptoUtils.Version;
import gov.usgs.earthquake.product.Content;
import gov.usgs.earthquake.product.URLContent;
import gov.usgs.earthquake.product.ProductId;
/**
* Store a product to an OutputStream using XML.
*/
public class XmlProductHandler implements ProductHandler {
/** Declaration for XML and version */
public static final String XML_DECLARATION = "<?xml version=\"1.0\"?>\n";
/** Namespace for XML product */
public static final String PRODUCT_XML_NAMESPACE = "http://earthquake.usgs.gov/distribution/product";
/** Element for product */
public static final String PRODUCT_ELEMENT = "product";
/** Product Attribute for id */
public static final String PRODUCT_ATTRIBUTE_ID = "id";
/** Product Attribute for updateTime */
public static final String PRODUCT_ATTRIBUTE_UPDATED = "updateTime";
/** Product Attribute for status */
public static final String PRODUCT_ATTRIBUTE_STATUS = "status";
/** Product Attribute for trackerURL */
public static final String PRODUCT_ATTRIBUTE_TRACKER_URL = "trackerURL";
/** Element for property */
public static final String PROPERTY_ELEMENT = "property";
/** Property attribute for name */
public static final String PROPERTY_ATTRIBUTE_NAME = "name";
/** Property attribute for value */
public static final String PROPERTY_ATTRIBUTE_VALUE = "value";
/** Element for link */
public static final String LINK_ELEMENT = "link";
/** Link attribute for relation */
public static final String LINK_ATTRIBUTE_RELATION = "rel";
/** Link attribute for href */
public static final String LINK_ATTRIBUTE_HREF = "href";
/** Element for content */
public static final String CONTENT_ELEMENT = "content";
/** Content attribute for path */
public static final String CONTENT_ATTRIBUTE_PATH = "path";
/** Content attribute for type */
public static final String CONTENT_ATTRIBUTE_TYPE = "type";
/** Content attribute for length */
public static final String CONTENT_ATTRIBUTE_LENGTH = "length";
/** Content attribute for modified */
public static final String CONTENT_ATTRIBUTE_MODIFIED = "modified";
/** Used with URLContent. */
public static final String CONTENT_ATTRIBUTE_HREF = "href";
/** Content attribute for encoded */
public static final String CONTENT_ATTRIBUTE_ENCODED = "encoded";
/** Element for signature */
public static final String SIGNATURE_ELEMENT = "signature";
/** Signature attribute for version */
public static final String SIGNATURE_ATTRIBUTE_VERSION = "version";
/** The OutputStream where xml is written. */
private OutputStream out;
/** Controls whether the XML Declaration is output with the XML. */
private boolean includeDeclaration = true;
/** Signature version. */
private Version signatureVersion = Version.SIGNATURE_V1;
/**
* Create a new XmlProductHandler object.
*
* @param out
* the OutputStream where xml will be written.
*/
public XmlProductHandler(final OutputStream out) {
this.out = out;
}
/**
* Create a new XmlProductHandler object.
*
* @param out
* the OutputStream where xml will be written.
* @param includeDeclaration
* whether to include the XML declaration with output
*/
public XmlProductHandler(final OutputStream out, boolean includeDeclaration) {
this.out = out;
this.includeDeclaration = includeDeclaration;
}
/**
* Output the product root element.
*/
public void onBeginProduct(ProductId id, String status, URL trackerURL)
throws Exception {
StringBuffer buf = new StringBuffer();
if (includeDeclaration) {
buf.append(XML_DECLARATION);
}
buf.append("<").append(PRODUCT_ELEMENT);
buf.append(" xmlns=\"").append(PRODUCT_XML_NAMESPACE).append("\"");
buf.append(" ").append(PRODUCT_ATTRIBUTE_ID).append("=\"")
.append(XmlUtils.escape(id.toString())).append("\"");
buf.append(" ").append(PRODUCT_ATTRIBUTE_UPDATED).append("=\"")
.append(XmlUtils.formatDate(id.getUpdateTime())).append("\"");
buf.append(" ").append(PRODUCT_ATTRIBUTE_STATUS).append("=\"")
.append(XmlUtils.escape(status)).append("\"");
if (trackerURL != null) {
buf.append(" ").append(PRODUCT_ATTRIBUTE_TRACKER_URL).append("=\"")
.append(trackerURL.toExternalForm()).append("\"");
}
buf.append(">\n");
out.write(buf.toString().getBytes());
}
/**
* Output a content object as xml.
*/
public void onContent(ProductId id, String path, Content content)
throws Exception {
// open element
StringBuffer buf = new StringBuffer();
buf.append("\t<").append(CONTENT_ELEMENT);
buf.append(" ").append(CONTENT_ATTRIBUTE_PATH).append("=\"")
.append(XmlUtils.escape(path)).append("\"");
buf.append(" ").append(CONTENT_ATTRIBUTE_TYPE).append("=\"")
.append(XmlUtils.escape(content.getContentType())).append("\"");
buf.append(" ").append(CONTENT_ATTRIBUTE_LENGTH).append("=\"")
.append(content.getLength()).append("\"");
buf.append(" ").append(CONTENT_ATTRIBUTE_MODIFIED).append("=\"")
.append(XmlUtils.formatDate(content.getLastModified()))
.append("\"");
if (content instanceof URLContent) {
// URL CONTENT
buf.append(" ").append(CONTENT_ATTRIBUTE_HREF).append("=\"")
.append(((URLContent) content).getURL().toString())
.append("\"");
// close element early, url is alternative to embedded content
buf.append("/>");
out.write(buf.toString().getBytes());
return;
}
else {
// EMBEDDED CONTENT
buf.append(" ").append(CONTENT_ATTRIBUTE_ENCODED)
.append("=\"true\"");
buf.append(">");
out.write(buf.toString().getBytes());
InputStream in = null;
OutputStream base64out = null;
try {
in = content.getInputStream();
base64out = Base64.getEncoder().wrap(
new StreamUtils.UnclosableOutputStream(out));
// write element content
StreamUtils.transferStream(in, base64out);
} finally {
StreamUtils.closeStream(in);
StreamUtils.closeStream(base64out);
}
// close element
buf = new StringBuffer();
buf.append("</").append(CONTENT_ELEMENT).append(">\n");
out.write(buf.toString().getBytes());
}
}
/**
* Output the closing product element.
*/
public void onEndProduct(ProductId id) throws Exception {
StringBuffer buf = new StringBuffer();
buf.append("</").append(PRODUCT_ELEMENT).append(">\n");
out.write(buf.toString().getBytes());
out.flush();
out.close();
}
/**
* Output a link element as xml.
*/
public void onLink(ProductId id, String relation, URI href)
throws Exception {
StringBuffer buf = new StringBuffer();
buf.append("\t<").append(LINK_ELEMENT);
buf.append(" ").append(LINK_ATTRIBUTE_RELATION).append("=\"")
.append(XmlUtils.escape(relation)).append("\"");
buf.append(" ").append(LINK_ATTRIBUTE_HREF).append("=\"")
.append(XmlUtils.escape(href.toString())).append("\"");
buf.append("/>\n");
out.write(buf.toString().getBytes());
}
/**
* Output the property element as xml.
*/
public void onProperty(ProductId id, String name, String value)
throws Exception {
StringBuffer buf = new StringBuffer();
buf.append("\t<").append(PROPERTY_ELEMENT);
buf.append(" ").append(PROPERTY_ATTRIBUTE_NAME).append("=\"")
.append(XmlUtils.escape(name)).append("\"");
buf.append(" ").append(PROPERTY_ATTRIBUTE_VALUE).append("=\"")
.append(XmlUtils.escape(value)).append("\"");
buf.append("/>\n");
out.write(buf.toString().getBytes());
}
public void onSignatureVersion(ProductId id, Version version) throws Exception {
// save for output during onSignature
this.signatureVersion = version;
}
/**
* Output the signature element as xml.
*/
public void onSignature(ProductId id, String signature) throws Exception {
if (signature == null) {
return;
}
StringBuffer buf = new StringBuffer();
buf.append("\t<").append(SIGNATURE_ELEMENT);
buf.append(" ").append(SIGNATURE_ATTRIBUTE_VERSION).append("=\"")
.append(signatureVersion.toString()).append("\"");
buf.append(">");
buf.append(signature);
buf.append("</").append(SIGNATURE_ELEMENT).append(">\n");
out.write(buf.toString().getBytes());
}
/**
* Free any resources associated with this handler.
*/
@Override
public void close() {
StreamUtils.closeStream(out);
}
}