ZipProductHandler.java
/*
* ZipProductHandler
*/
package gov.usgs.earthquake.product.io;
import gov.usgs.earthquake.product.Content;
import gov.usgs.earthquake.product.Product;
import gov.usgs.earthquake.product.ProductId;
import gov.usgs.util.StreamUtils;
import java.io.OutputStream;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
/**
* Store a product to an OutputStream using ZIP.
*
* Accumulates entire product the same as ObjectProductOutput, then onEndProduct
* outputs a zip file containing "product.xml" as the first entry and all
* product content as other entries.
*
* Because the zip file is not written until after all content has been
* "received", all product content may result in in-memory buffering. This is
* not the case when dealing with File-backed products. If this is a concern,
* users are encouraged to use the Xml Input and Output classes, which always
* use streams and never buffer content.
*/
public class ZipProductHandler extends ObjectProductHandler {
/** The entry filename used for product metadata. */
public static final String PRODUCT_XML_ZIP_ENTRYNAME = "product.xml";
/** The output stream where zip content is written. */
private OutputStream out;
/**
* Construct a new ZipProductHandler object.
*
* @param out
* the output stream where zip content is written.
*/
public ZipProductHandler(final OutputStream out) {
this.out = out;
}
/**
* Creates and outputs the zip stream.
*/
public void onEndProduct(ProductId id) throws Exception {
super.onEndProduct(id);
Product product = getProduct();
// separate all contents that will be zip entries from product
Map<String, Content> contents = new HashMap<String, Content>(
product.getContents());
product.getContents().clear();
// check for inline content that won't have a zip entry
Content inlineContent = contents.get("");
if (inlineContent != null) {
product.getContents().put("", inlineContent);
}
// write zip stream
ZipOutputStream zos = null;
try {
zos = new ZipOutputStream(out);
// product xml entry
ZipEntry entry = new ZipEntry(PRODUCT_XML_ZIP_ENTRYNAME);
entry.setTime(product.getId().getUpdateTime().getTime());
zos.putNextEntry(entry);
new ObjectProductSource(product).streamTo(new XmlProductHandler(
new StreamUtils.UnclosableOutputStream(zos)));
// zos.closeEntry();
// all other content entries
Iterator<String> paths = contents.keySet().iterator();
while (paths.hasNext()) {
String path = paths.next();
if ("".equals(path)) {
// inline content doesn't get separate entry
continue;
}
Content content = contents.get(path);
entry = new ZipEntry(path);
entry.setTime(content.getLastModified().getTime());
entry.setComment(content.getContentType());
Long length = content.getLength();
if (length != null && length > 0) {
entry.setSize(content.getLength());
}
zos.putNextEntry(entry);
StreamUtils.transferStream(content.getInputStream(),
new StreamUtils.UnclosableOutputStream(zos));
// zos.closeEntry();
}
} finally {
// done
zos.finish();
zos.flush();
StreamUtils.closeStream(zos);
}
}
/**
* Free any resources associated with this handler.
*/
@Override
public void close() {
StreamUtils.closeStream(out);
}
}