poi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From n...@apache.org
Subject svn commit: r629829 - in /poi/trunk/src: documentation/content/xdocs/changes.xml documentation/content/xdocs/status.xml java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
Date Thu, 21 Feb 2008 15:36:00 GMT
Author: nick
Date: Thu Feb 21 07:35:59 2008
New Revision: 629829

URL: http://svn.apache.org/viewvc?rev=629829&view=rev
Log:
Patch from Josh from bug #44366 - InputStreams passed to POIFSFileSystem are now automatically
closed. A warning is generated for people who might've relied on them not being closed before,
and a wrapper to restore the old behaviour is supplied

Added:
    poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java   (with
props)
Modified:
    poi/trunk/src/documentation/content/xdocs/changes.xml
    poi/trunk/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java

Modified: poi/trunk/src/documentation/content/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/changes.xml?rev=629829&r1=629828&r2=629829&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/changes.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/changes.xml Thu Feb 21 07:35:59 2008
@@ -36,6 +36,7 @@
 
 		<!-- Don't forget to update status.xml too! -->
         <release version="3.1-beta1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">44366 - InputStreams passed to POIFSFileSystem
are now automatically closed. A warning is generated for people who might've relied on them
not being closed before, and a wrapper to restore the old behaviour is supplied</action>
            <action dev="POI-DEVELOPERS" type="add">44371 - Support for the Offset function</action>
            <action dev="POI-DEVELOPERS" type="fix">38921 - Have HSSFPalette.findSimilar()
work properly</action>
            <action dev="POI-DEVELOPERS" type="fix">44456 - Fix the contrib SViewer
/ SViewerPanel to not fail on sheets with missing rows</action>

Modified: poi/trunk/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/status.xml?rev=629829&r1=629828&r2=629829&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/status.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/status.xml Thu Feb 21 07:35:59 2008
@@ -33,6 +33,7 @@
 	<!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.1-beta1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">44366 - InputStreams passed to POIFSFileSystem
are now automatically closed. A warning is generated for people who might've relied on them
not being closed before, and a wrapper to restore the old behaviour is supplied</action>
            <action dev="POI-DEVELOPERS" type="add">44371 - Support for the Offset function</action>
            <action dev="POI-DEVELOPERS" type="fix">38921 - Have HSSFPalette.findSimilar()
work properly</action>
            <action dev="POI-DEVELOPERS" type="fix">44456 - Fix the contrib SViewer
/ SViewerPanel to not fail on sheets with missing rows</action>

Modified: poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java?rev=629829&r1=629828&r2=629829&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java Thu Feb 21 07:35:59
2008
@@ -19,6 +19,7 @@
 
 package org.apache.poi.poifs.filesystem;
 
+import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -30,6 +31,8 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.poi.poifs.dev.POIFSViewable;
 import org.apache.poi.poifs.property.DirectoryProperty;
 import org.apache.poi.poifs.property.Property;
@@ -58,6 +61,33 @@
 public class POIFSFileSystem
     implements POIFSViewable
 {
+    private static final Log _logger = LogFactory.getLog(POIFSFileSystem.class);
+    
+    
+    private static final class CloseIgnoringInputStream extends InputStream {
+
+        private final InputStream _is;
+        public CloseIgnoringInputStream(InputStream is) {
+            _is = is;
+        }
+        public int read() throws IOException {
+            return _is.read();
+        }
+        public int read(byte[] b, int off, int len) throws IOException {
+            return _is.read(b, off, len);
+        }
+        public void close() {
+            // do nothing
+        }
+    }
+    
+    /**
+     * Convenience method for clients that want to avoid the auto-close behaviour of the
constructor.
+     */
+    public static InputStream createNonClosingInputStream(InputStream is) {
+        return new CloseIgnoringInputStream(is);
+    }
+    
     private PropertyTable _property_table;
     private List          _documents;
     private DirectoryNode _root;
@@ -74,23 +104,52 @@
     }
 
     /**
-     * Create a POIFSFileSystem from an InputStream
+     * Create a POIFSFileSystem from an <tt>InputStream</tt>.  Normally the stream
is read until
+     * EOF.  The stream is always closed.<p/>
+     * 
+     * Some streams are usable after reaching EOF (typically those that return <code>true</code>

+     * for <tt>markSupported()</tt>).  In the unlikely case that the caller has
such a stream 
+     * <i>and</i> needs to use it after this constructor completes, a work around
is to wrap the
+     * stream in order to trap the <tt>close()</tt> call.  A convenience method
(
+     * <tt>createNonClosingInputStream()</tt>) has been provided for this purpose:
+     * <pre>
+     * InputStream wrappedStream = POIFSFileSystem.createNonClosingInputStream(is);
+     * HSSFWorkbook wb = new HSSFWorkbook(wrappedStream);
+     * is.reset(); 
+     * doSomethingElse(is); 
+     * </pre>
+     * Note also the special case of <tt>ByteArrayInputStream</tt> for which
the <tt>close()</tt>
+     * method does nothing. 
+     * <pre>
+     * ByteArrayInputStream bais = ...
+     * HSSFWorkbook wb = new HSSFWorkbook(bais); // calls bais.close() !
+     * bais.reset(); // no problem
+     * doSomethingElse(bais);
+     * </pre>
      *
      * @param stream the InputStream from which to read the data
      *
      * @exception IOException on errors reading, or on invalid data
      */
 
-    public POIFSFileSystem(final InputStream stream)
+    public POIFSFileSystem(InputStream stream)
         throws IOException
     {
         this();
+        boolean success = false;
 
         // read the header block from the stream
-        HeaderBlockReader header_block_reader = new HeaderBlockReader(stream);
-
+        HeaderBlockReader header_block_reader;
         // read the rest of the stream into blocks
-        RawDataBlockList  data_blocks         = new RawDataBlockList(stream);
+        RawDataBlockList data_blocks;
+        try {
+            header_block_reader = new HeaderBlockReader(stream);
+            data_blocks = new RawDataBlockList(stream);
+            success = true;
+        } finally {
+            closeInputStream(stream, success);
+        }
+        
 
         // set up the block allocation table (necessary for the
         // data_blocks to be manageable
@@ -112,7 +171,32 @@
                     .getSBATStart()), data_blocks, properties.getRoot()
                         .getChildren(), null);
     }
-    
+    /**
+     * @param stream the stream to be closed
+     * @param success <code>false</code> if an exception is currently being thrown
in the calling method
+     */
+    private void closeInputStream(InputStream stream, boolean success) {
+        
+        if(stream.markSupported() && !(stream instanceof ByteArrayInputStream)) {
+            String msg = "POIFS is closing the supplied input stream of type (" 
+                    + stream.getClass().getName() + ") which supports mark/reset.  "
+                    + "This will be a problem for the caller if the stream will still be
used.  "
+                    + "If that is the case the caller should wrap the input stream to avoid
this close logic.  "
+                    + "This warning is only temporary and will not be present in future versions
of POI.";
+            _logger.warn(msg);
+        }
+        try {
+            stream.close();
+        } catch (IOException e) {
+            if(success) {
+                throw new RuntimeException(e);
+            }
+            // else not success? Try block did not complete normally 
+            // just print stack trace and leave original ex to be thrown
+            e.printStackTrace();
+        }
+    }
+
     /**
      * Checks that the supplied InputStream (which MUST
      *  support mark and reset, or be a PushbackInputStream) 
@@ -123,23 +207,23 @@
      * @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream

      */
     public static boolean hasPOIFSHeader(InputStream inp) throws IOException {
-    	// We want to peek at the first 8 bytes 
-    	inp.mark(8);
+        // We want to peek at the first 8 bytes 
+        inp.mark(8);
 
-    	byte[] header = new byte[8];
-    	IOUtils.readFully(inp, header);
+        byte[] header = new byte[8];
+        IOUtils.readFully(inp, header);
         LongField signature = new LongField(HeaderBlockConstants._signature_offset, header);
 
         // Wind back those 8 bytes
         if(inp instanceof PushbackInputStream) {
-        	PushbackInputStream pin = (PushbackInputStream)inp;
-        	pin.unread(header);
+            PushbackInputStream pin = (PushbackInputStream)inp;
+            pin.unread(header);
         } else {
-        	inp.reset();
+            inp.reset();
         }
-    	
-    	// Did it match the signature?
-    	return (signature.get() == HeaderBlockConstants._signature);
+        
+        // Did it match the signature?
+        return (signature.get() == HeaderBlockConstants._signature);
     }
 
     /**

Added: poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java?rev=629829&view=auto
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java (added)
+++ poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java Thu Feb
21 07:35:59 2008
@@ -0,0 +1,136 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.filesystem;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for POIFSFileSystem
+ * 
+ * @author Josh Micich
+ */
+public final class TestPOIFSFileSystem extends TestCase {
+
+	public TestPOIFSFileSystem(String testName) {
+		super(testName);
+	}
+	
+	/**
+	 * Mock exception used to ensure correct error handling
+	 */
+	private static final class MyEx extends RuntimeException {
+		public MyEx() {
+			// no fields to initialise
+		}
+	}
+	/**
+	 * Helps facilitate testing. Keeps track of whether close() was called.
+	 * Also can throw an exception at a specific point in the stream. 
+	 */
+	private static final class TestIS extends InputStream {
+
+		private final InputStream _is;
+		private final int _failIndex;
+		private int _currentIx;
+		private boolean _isClosed;
+
+		public TestIS(InputStream is, int failIndex) {
+			_is = is;
+			_failIndex = failIndex;
+			_currentIx = 0;
+			_isClosed = false;
+		}
+
+		public int read() throws IOException {
+			int result = _is.read();
+			if(result >=0) {
+				checkRead(1);
+			}
+			return result;
+		}
+		public int read(byte[] b, int off, int len) throws IOException {
+			int result = _is.read(b, off, len);
+			checkRead(result);
+			return result;
+		}
+
+		private void checkRead(int nBytes) {
+			_currentIx += nBytes;
+			if(_failIndex > 0 && _currentIx > _failIndex) {
+				throw new MyEx();
+			}
+		}
+		public void close() throws IOException {
+			_isClosed = true;
+			_is.close();
+		}
+		public boolean isClosed() {
+			return _isClosed;
+		}
+	}
+	
+	/**
+	 * Test for undesired behaviour observable as of svn revision 618865 (5-Feb-2008).
+	 * POIFSFileSystem was not closing the input stream.
+	 */
+	public void testAlwaysClose() {
+		
+		TestIS testIS;
+	
+		// Normal case - read until EOF and close
+		testIS = new TestIS(openSampleStream("13224.xls"), -1);
+		try {
+			new POIFSFileSystem(testIS);
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+		assertTrue("input stream was not closed", testIS.isClosed());
+		
+		// intended to crash after reading 10000 bytes
+		testIS = new TestIS(openSampleStream("13224.xls"), 10000);
+		try {
+			new POIFSFileSystem(testIS);
+			fail("ex should have been thrown");
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		} catch (MyEx e) {
+			// expected
+		}
+		assertTrue("input stream was not closed", testIS.isClosed()); // but still should close
+		
+	}
+
+	private static InputStream openSampleStream(String sampleName) {
+		String dataDirName = System.getProperty("HSSF.testdata.path");
+		if(dataDirName == null) {
+		    throw new RuntimeException("Missing system property '" + "HSSF.testdata.path" + "'");
+		}
+        File f = new File(dataDirName + "/" + sampleName);
+        try {
+            return new FileInputStream(f);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("Sample file '" + f.getAbsolutePath() + "' not found");
+        }
+	}
+}

Propchange: poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
------------------------------------------------------------------------------
    svn:executable = *



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org


Mime
View raw message