package com.occamlab.te.parsers; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.InputStream; import java.net.URLConnection; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.Random; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.occamlab.te.util.Utils; /** * Parses a zip file input by extracting the contents into the directory * specified by the value of the <code>java.io.tmpdir</code> system property. * The resulting manifest is structured as follows: * * <ctl:manifest xmlns:ctl="http://www.occamlab.com/ctl"> <ctl:file-entry * full-path="${java.io.tmpdir}/dir/doc.kml" size="2048" /> </ctl:manifest> * * @author jparrpearson */ public class ZipParser { public static final String PARSERS_NS = "http://www.occamlab.com/te/parsers"; public static final String CTL_NS = "http://www.occamlab.com/ctl"; // Add more mime types as necessary, if mime type not listed a default will // be given public static String[][] ApplicationMediaTypeMappings = { { "kml", "vnd.google-earth.kml+xml" }, { "kmz", "vnd.google-earth.kmz" }, { "xml", "application/xml" }, { "txt", "text/plain" }, { "jpg", "image/jpeg" }, { "jpeg", "image/jpeg" }, { "gif", "image/gif" }, { "png", "image/png" } }; private static Logger jlogger = Logger .getLogger("com.occamlab.te.parsers.ZipParser"); /** * Returns the mime media type value for the given extension * * @param ext * the filename extension to lookup * @return String the mime type for the given extension */ public static String getMediaType(String ext) { String mediaType = ""; // Find the media type value in the lookup table for (int i = 0; i < ApplicationMediaTypeMappings.length; i++) { if (ApplicationMediaTypeMappings[i][0].equals(ext.toLowerCase())) { mediaType = ApplicationMediaTypeMappings[i][1]; } } // Give the media type default of "application/octet-stream" if (mediaType.equals("")) { mediaType = "application/octet-stream"; } return mediaType; } /** * Parses the entity (a ZIP archive) obtained in response to submitting a * request to some URL. The resulting manifest is an XML document with * <ctl:manifest> as the document element. * * @param resp * the response to parse * @param instruction * a DOM Element representation of configuration information for * this parser * @param logger * the test logger * @return a DOM Document representing the manifest of items in the archive. * @throws Throwable */ /* * public static Document parse(HttpResponse resp, Element instruction, * PrintWriter logger) throws Throwable { return * parse(resp.getEntity().getContent(), instruction, logger); } */ public static Document parse(URLConnection uc, Element instruction, PrintWriter logger) throws Throwable { return parse(uc.getInputStream(), instruction, logger); } private static Document parse(InputStream is, Element instruction, PrintWriter logger) throws Throwable { // Create the response element, <ctl:manifest> DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.newDocument(); Element root = doc.createElementNS(CTL_NS, "manifest"); // Open the connection to the zip file ZipInputStream zis = new ZipInputStream(is); // Create the full directory path to store the zip entities Document d = instruction.getOwnerDocument(); NodeList nodes = d.getElementsByTagNameNS(CTL_NS, "SessionDir"); // Either use the given session directory, or make one in the java temp // directory String path = ""; if (nodes.getLength() > 0) { Element e = (Element) nodes.item(0); path = e.getTextContent(); } else { path = System.getProperty("java.io.tmpdir") + "/zipparser.temp"; } String randomStr = Utils.randomString(16, new Random()); path = path + "/work/" + randomStr; new File(path).mkdirs(); // Unzip the file to a temporary location (java temp) ZipEntry entry = null; while ((entry = zis.getNextEntry()) != null) { // Open the output file and get info from it String filename = entry.getName(); long size = entry.getSize(); String ext = filename.substring(filename.lastIndexOf(".") + 1); String mediaType = getMediaType(ext); // Make the temp directory and subdirectories if needed String subdir = ""; if (filename.lastIndexOf("/") != -1) subdir = filename.substring(0, filename.lastIndexOf("/")); else if (filename.lastIndexOf("\\") != -1) subdir = filename.substring(0, filename.lastIndexOf("\\")); new File(path + "/" + subdir).mkdirs(); File outFile = new File(path, filename); if (outFile.isDirectory()) continue; OutputStream out = new FileOutputStream(outFile); // Transfer bytes from the ZIP file to the output file byte[] buf = new byte[1024]; int len; while ((len = zis.read(buf)) > 0) { out.write(buf, 0, len); } // Add the file information to the document Element fileEntry = doc.createElementNS(CTL_NS, "file-entry"); fileEntry.setAttribute("full-path", outFile.getPath().replace('\\', '/')); fileEntry.setAttribute("media-type", mediaType); fileEntry.setAttribute("size", String.valueOf(size)); root.appendChild(fileEntry); } doc.appendChild(root); // Return the <ctl:manifest> document return doc; } /** * Extracts the local Zip file and saves to the working directory. The * resulting manifest is an XML document with <ctl:manifest> as the document * element. * * @param path * the full path to the local Zip file * @param instruction * a DOM Element representation of configuration information for * this parser * @return a DOM Document representing the manifest of items in the archive. */ public Document saveZipFile(String filepath, Document instruction) throws Exception { // Get a connection to the Zip file FileInputStream is = null; ZipInputStream zis = null; try { is = new FileInputStream(filepath); zis = new ZipInputStream(is); } catch (Exception e) { jlogger.log(Level.SEVERE, "saveZipFile", e); System.out.println("ERROR: " + e.getMessage()); return null; } // Create the response element, <ctl:manifest> DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.newDocument(); Element root = doc.createElementNS(CTL_NS, "manifest"); // Create the full directory path to store the zip entities Document d = instruction.getOwnerDocument(); NodeList nodes = d.getElementsByTagNameNS(CTL_NS, "SessionDir"); // Either use the given session directory, or make one in the java temp // directory String path = ""; if (nodes.getLength() > 0) { Element e = (Element) nodes.item(0); path = e.getTextContent(); } else { path = System.getProperty("java.io.tmpdir") + "/zipparser.temp"; } String randomStr = Utils.randomString(16, new Random()); path = path + "/work/" + randomStr; new File(path).mkdirs(); // Unzip the file to a temporary location (java temp) ZipEntry entry = null; while ((entry = zis.getNextEntry()) != null) { System.out.println("File: " + entry.getName()); // Open the output file and get info from it String filename = entry.getName(); long size = entry.getSize(); String ext = filename.substring(filename.lastIndexOf(".") + 1); String mediaType = getMediaType(ext); // Make the temp directory and subdirectories if needed String subdir = ""; if (filename.lastIndexOf("/") != -1) subdir = filename.substring(0, filename.lastIndexOf("/")); else if (filename.lastIndexOf("\\") != -1) subdir = filename.substring(0, filename.lastIndexOf("\\")); new File(path + "/" + subdir).mkdirs(); File outFile = new File(path, filename); if (outFile.isDirectory()) continue; OutputStream out = new FileOutputStream(outFile); // Transfer bytes from the ZIP file to the output file byte[] buf = new byte[1024]; int len; while ((len = zis.read(buf)) > 0) { out.write(buf, 0, len); } // Add the file information to the document Element fileEntry = doc.createElementNS(CTL_NS, "file-entry"); fileEntry.setAttribute("full-path", outFile.getPath().replace('\\', '/')); fileEntry.setAttribute("media-type", mediaType); fileEntry.setAttribute("size", String.valueOf(size)); root.appendChild(fileEntry); } doc.appendChild(root); // Return the <ctl:manifest> document return doc; } }