/** * Copyright (C) 2010 Orbeon, Inc. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.processor.zip; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItem; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.pipeline.api.PipelineContext; import org.orbeon.oxf.processor.BinaryTextSupport; import org.orbeon.oxf.processor.ProcessorImpl; import org.orbeon.oxf.processor.ProcessorInputOutputInfo; import org.orbeon.oxf.processor.ProcessorOutput; import org.orbeon.oxf.resources.ResourceManagerWrapper; import org.orbeon.oxf.resources.URLFactory; import org.orbeon.oxf.util.NetUtils; import org.orbeon.oxf.xml.XMLReceiver; import org.orbeon.oxf.xml.XMLReceiverAdapter; import org.orbeon.oxf.xml.dom4j.LocationData; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import java.io.*; import java.net.URL; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipProcessor extends ProcessorImpl { public ZipProcessor() { addInputInfo(new ProcessorInputOutputInfo(INPUT_DATA)); addOutputInfo(new ProcessorInputOutputInfo(OUTPUT_DATA)); } @Override public ProcessorOutput createOutput(String name) { final ProcessorOutput output = new ProcessorOutputImpl(ZipProcessor.this, name) { String fileName = null; int statusCode = -1; public void readImpl(PipelineContext context, XMLReceiver xmlReceiver) { try { // Create temporary zip file final FileItem fileItem = NetUtils.prepareFileItem(NetUtils.REQUEST_SCOPE, logger); fileItem.getOutputStream().close(); final File temporaryZipFile = ((DiskFileItem) fileItem).getStoreLocation(); temporaryZipFile.createNewFile(); final ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(temporaryZipFile)); try { // Read list of files and write to zip output stream as we go readInputAsSAX(context, INPUT_DATA, new XMLReceiverAdapter() { String name; StringBuilder uri; // Get the file name, store it @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if ("file".equals(localName)) { name = atts.getValue("name"); uri = new StringBuilder(); } else if ("files".equals(localName)) { fileName = atts.getValue("filename"); String value = atts.getValue("status-code"); if (value != null ) { statusCode = Integer.parseInt(value); } } } // Get the URI to the file, store it @Override public void characters(char ch[], int start, int length) throws SAXException { if (uri != null) uri.append(ch, start, length); } // Process file @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { try { if ("file".equals(localName)) { zipOutputStream.putNextEntry(new ZipEntry(name)); final LocationData locationData = getLocationData(); final String realPath; final URL fullURL = (locationData != null && locationData.file() != null) ? URLFactory.createURL(locationData.file(), uri.toString()) : URLFactory.createURL(uri.toString()); if (fullURL.getProtocol().equals("oxf")) { // Get real path to resource path if possible realPath = ResourceManagerWrapper.instance().getRealPath(fullURL.getFile()); if (realPath == null) throw new OXFException("Zip processor is unable to obtain the real path of the file using the oxf: protocol for the base-directory property: " + uri.toString()); } else if (fullURL.getProtocol().equals("file")) { String host = fullURL.getHost(); realPath = host + (host.length() > 0 ? ":" : "") + fullURL.getFile(); } else { throw new OXFException("Zip processor only supports the file: and oxf: protocols for the base-directory property: " + uri.toString()); } InputStream fileInputStream = new FileInputStream(new File(realPath)); try { NetUtils.copyStream(fileInputStream, zipOutputStream); } finally { fileInputStream.close(); } } } catch (IOException e) { throw new OXFException(e); } } }); } finally { zipOutputStream.close(); } // Generate an Orbeon binary document with the content of the zip file FileInputStream zipInputStream = new FileInputStream(temporaryZipFile); try { BinaryTextSupport.readBinary(zipInputStream, xmlReceiver, "multipart/x-gzip", null, statusCode, fileName, null); } finally { zipInputStream.close(); } } catch (FileNotFoundException e) { throw new OXFException(e); } catch (IOException e) { throw new OXFException(e); } } // We could assume that we cache if the input document hasn't changed. But this would be unsafe as some of // the files referenced from the input document could have changed. So to be on the safe side, and also // because the cases where caching could happen are rather rare, we just don't cache. }; addOutput(name, output); return output; } }