package org.cdlib.xtf.saxonExt.pipe; /* * Copyright (c) 2009, Regents of the University of California * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the University of California nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.cdlib.xtf.saxonExt.ElementWithContent; import org.cdlib.xtf.saxonExt.InstructionWithContent; import org.cdlib.xtf.servletBase.TextServlet; import org.cdlib.xtf.xslt.FileUtils; import net.sf.saxon.expr.Expression; import net.sf.saxon.expr.XPathContext; import net.sf.saxon.instruct.Executable; import net.sf.saxon.instruct.TailCall; import net.sf.saxon.trans.XPathException; /** * Pipes the contents of a file directly to the servlet request * output stream, bypassing any further stylesheet processing. */ public class PipeFileElement extends ElementWithContent { public void prepareAttributes() throws XPathException { String[] mandatoryAtts = { "path", "mimeType" }; String[] optionalAtts = { "fileName" }; parseAttributes(mandatoryAtts, optionalAtts); } public Expression compile(Executable exec) throws XPathException { return new PipeFileInstruction(attribs, compileContent(exec)); } /** Worker class for PipeFileElement */ private static class PipeFileInstruction extends InstructionWithContent { public PipeFileInstruction(Map<String, Expression> attribs, Expression content) { super("pipe:pipeFile", attribs, content); } /** * The real workhorse. */ @Override public TailCall processLeavingTail(XPathContext context) throws XPathException { // Build the full path. String path = attribs.get("path").evaluateAsString(context); File file = FileUtils.resolveFile(context, path); // Make sure it's readable. if (!file.canRead()) { dynamicError("Cannot read path '" + path + "' (resolved to '" + file.toString() + "'", "PIPE_FILE_001", context); } // Set the content length and type HttpServletResponse servletResponse = TextServlet.getCurResponse(); servletResponse.setHeader("Content-length", Long.toString(file.length())); servletResponse.setHeader("Content-type", attribs.get("mimeType").evaluateAsString(context)); // If file name specified, add the Content-disposition header. String fileName; if (attribs.containsKey("fileName")) { fileName = attribs.get("fileName").evaluateAsString(context); servletResponse.setHeader("Content-disposition", "attachment; filename=\"" + fileName + "\""); } // Now copy the file to the output stream. try { copyFileToStream(file, servletResponse.getOutputStream()); } catch (IOException e) { dynamicError("IO Error while piping file: " + e.toString(), "PIPE_FILE_002", context); } // All done. return null; } } /** Utility method to copy the contents of a file into an output stream */ public static void copyFileToStream(File inFilePath, OutputStream outStream) throws IOException { InputStream fileIn = null; byte[] buf = null; try { fileIn = new FileInputStream(inFilePath); buf = PipeBufferPool.allocBuffer(); int got; while ((got = fileIn.read(buf)) >= 0) outStream.write(buf, 0, got); outStream.flush(); } finally { // Clean up after ourselves. if (buf != null) PipeBufferPool.deallocBuffer(buf); if (fileIn != null) try { fileIn.close(); } catch (IOException e) { /* ignore */ } } } }