You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2014/02/03 13:41:56 UTC

svn commit: r1563862 - in /chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src: main/java/org/apache/chemistry/opencmis/server/shared/ test/java/org/apache/chemistry/opencmis/server/impl/

Author: fmui
Date: Mon Feb  3 12:41:56 2014
New Revision: 1563862

URL: http://svn.apache.org/r1563862
Log:
Corrected ThresholdOutputStream (the rewind and mark/reset behaviors of in-memory and temp file storage should be the same)

Added:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/ThresholdOutputStreamTest.java   (with props)
Modified:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/ThresholdOutputStream.java

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/ThresholdOutputStream.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/ThresholdOutputStream.java?rev=1563862&r1=1563861&r2=1563862&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/ThresholdOutputStream.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/ThresholdOutputStream.java Mon Feb  3 12:41:56 2014
@@ -78,8 +78,7 @@ public class ThresholdOutputStream exten
      * Constructor.
      * 
      * @param tempDir
-     *            temp directory or <code>null</code> for the default temp
-     *            directory
+     *            temp directory or {@code null} for the default temp directory
      * @param memoryThreshold
      *            memory threshold in bytes
      * @param maxContentSize
@@ -93,8 +92,7 @@ public class ThresholdOutputStream exten
      * Constructor.
      * 
      * @param tempDir
-     *            temp directory or <code>null</code> for the default temp
-     *            directory
+     *            temp directory or {@code null} for the default temp directory
      * @param memoryThreshold
      *            memory threshold in bytes
      * @param maxContentSize
@@ -110,8 +108,7 @@ public class ThresholdOutputStream exten
      * @param initSize
      *            initial internal buffer size
      * @param tempDir
-     *            temp directory or <code>null</code> for the default temp
-     *            directory
+     *            temp directory or {@code null} for the default temp directory
      * @param memoryThreshold
      *            memory threshold in bytes
      * @param maxContentSize
@@ -139,27 +136,7 @@ public class ThresholdOutputStream exten
 
         if (bufSize + nextBufferSize > memoryThreshold) {
             if (tmpStream == null) {
-                tempFile = File.createTempFile("opencmis", null, tempDir);
-                if (encrypt) {
-
-                    Cipher cipher;
-                    try {
-                        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
-                        keyGenerator.init(KEY_SIZE);
-                        key = keyGenerator.generateKey();
-
-                        cipher = Cipher.getInstance(TRANSFORMATION);
-                        cipher.init(Cipher.ENCRYPT_MODE, key);
-
-                        iv = cipher.getIV();
-                    } catch (Exception e) {
-                        throw new IOException("Cannot initialize encryption cipher!", e);
-                    }
-
-                    tmpStream = new BufferedOutputStream(new CipherOutputStream(new FileOutputStream(tempFile), cipher));
-                } else {
-                    tmpStream = new BufferedOutputStream(new FileOutputStream(tempFile));
-                }
+                openTempFile();
             }
             tmpStream.write(buf, 0, bufSize);
 
@@ -178,6 +155,30 @@ public class ThresholdOutputStream exten
         buf = newbuf;
     }
 
+    private void openTempFile() throws IOException {
+        tempFile = File.createTempFile("opencmis", null, tempDir);
+
+        if (encrypt) {
+            Cipher cipher;
+            try {
+                KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
+                keyGenerator.init(KEY_SIZE);
+                key = keyGenerator.generateKey();
+
+                cipher = Cipher.getInstance(TRANSFORMATION);
+                cipher.init(Cipher.ENCRYPT_MODE, key);
+
+                iv = cipher.getIV();
+            } catch (Exception e) {
+                throw new IOException("Cannot initialize encryption cipher!", e);
+            }
+
+            tmpStream = new BufferedOutputStream(new CipherOutputStream(new FileOutputStream(tempFile), cipher));
+        } else {
+            tmpStream = new BufferedOutputStream(new FileOutputStream(tempFile));
+        }
+    }
+
     public long getSize() {
         return size;
     }
@@ -212,7 +213,7 @@ public class ThresholdOutputStream exten
     @Override
     public void write(int oneByte) throws IOException {
         try {
-            if ((maxContentSize > -1) && (size + 1 > maxContentSize)) {
+            if (maxContentSize > -1 && size + 1 > maxContentSize) {
                 destroy();
                 throw new CmisConstraintException("Content too big!");
             }
@@ -231,6 +232,10 @@ public class ThresholdOutputStream exten
 
     @Override
     public void flush() throws IOException {
+        if (tmpStream == null && memoryThreshold < bufSize) {
+            openTempFile();
+        }
+
         if (tmpStream != null) {
             try {
                 if (bufSize > 0) {
@@ -299,16 +304,16 @@ public class ThresholdOutputStream exten
         /**
          * Returns if the data is stored in memory.
          * 
-         * @return <code>true</code> if the data is in memory and
-         *         <code>false</code> if the data resides in a temporary file
+         * @return {@code true} if the data is in memory and {@code false} if
+         *         the data resides in a temporary file
          */
         public abstract boolean isInMemory();
 
         /**
          * Gets the temporary file.
          * 
-         * @return the temporary file or <code>null</code> if the data is stored
-         *         in memory
+         * @return the temporary file or {@code null} if the data is stored in
+         *         memory
          */
         public File getTemporaryFile() {
             return null;
@@ -317,8 +322,11 @@ public class ThresholdOutputStream exten
         /**
          * Gets the byte buffer.
          * 
-         * @return the content in a byte array or <code>null</code> if the data
-         *         is stored in a file
+         * This the underlying byte buffer and might be bigger than then the
+         * total length of the stream.
+         * 
+         * @return the content in a byte array or {@code null} if the data is
+         *         stored in a file
          */
         public byte[] getBytes() {
             return null;
@@ -347,11 +355,17 @@ public class ThresholdOutputStream exten
         private int pos = 0;
         private int mark = -1;
 
+        @Override
         public boolean isInMemory() {
             return true;
         }
 
+        @Override
         public byte[] getBytes() {
+            if (buf == null) {
+                throw new IllegalStateException("Stream is already closed!");
+            }
+
             return buf;
         }
 
@@ -452,7 +466,9 @@ public class ThresholdOutputStream exten
     }
 
     /**
-     * InputStream for file data.
+     * InputStream for temp file data.
+     * 
+     * Call {@link #close()} to delete the temp file.
      */
     private class InternalTempFileInputStream extends ThresholdInputStream {
 
@@ -478,20 +494,31 @@ public class ThresholdOutputStream exten
             openStream();
         }
 
+        /**
+         * Opens the temp file stream.
+         */
         protected void openStream() throws FileNotFoundException {
+            int bufferSize = (memoryThreshold < 4 * 1024 ? 4 * 1024 : memoryThreshold);
+
             if (encrypt) {
                 stream = new BufferedInputStream(new CipherInputStream(new FileInputStream(tempFile), cipher),
-                        memoryThreshold);
+                        bufferSize);
             } else {
-                stream = new BufferedInputStream(new FileInputStream(tempFile), memoryThreshold);
+                stream = new BufferedInputStream(new FileInputStream(tempFile), bufferSize);
             }
         }
 
+        @Override
         public boolean isInMemory() {
             return false;
         }
 
+        @Override
         public File getTemporaryFile() {
+            if (isDeleted) {
+                throw new IllegalStateException("Temporary file is already deleted!");
+            }
+
             return tempFile;
         }
 
@@ -553,10 +580,6 @@ public class ThresholdOutputStream exten
 
             int b = stream.read();
 
-            if (b == -1) {
-                delete();
-            }
-
             return b;
         }
 
@@ -573,10 +596,6 @@ public class ThresholdOutputStream exten
 
             int n = super.read(b, off, len);
 
-            if (n == -1) {
-                delete();
-            }
-
             return n;
         }
 
@@ -585,6 +604,9 @@ public class ThresholdOutputStream exten
             delete();
         }
 
+        /**
+         * Closes the temp file stream and then deletes the temp file.
+         */
         protected void delete() {
             if (!isClosed) {
                 try {
@@ -598,7 +620,7 @@ public class ThresholdOutputStream exten
             if (!isDeleted) {
                 isDeleted = tempFile.delete();
                 if (!isDeleted) {
-                    LOG.warn("Temp file " + tempFile.getAbsolutePath() + " could not be deleted!");
+                    LOG.warn("Temp file {} could not be deleted!", tempFile.getAbsolutePath());
                 }
             }
         }

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/ThresholdOutputStreamTest.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/ThresholdOutputStreamTest.java?rev=1563862&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/ThresholdOutputStreamTest.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/ThresholdOutputStreamTest.java Mon Feb  3 12:41:56 2014
@@ -0,0 +1,184 @@
+/*
+ * 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.chemistry.opencmis.server.impl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+
+import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStream;
+import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStream.ThresholdInputStream;
+import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStreamFactory;
+import org.junit.Test;
+
+public class ThresholdOutputStreamTest {
+
+    private static final byte[] CONTENT = "Hello".getBytes();
+
+    @Test
+    public void testInMemory() throws Exception {
+        ThresholdOutputStreamFactory streamFactory = ThresholdOutputStreamFactory.newInstance(null, 1024, 1024, false);
+
+        // set content
+        ThresholdOutputStream tos = streamFactory.newOutputStream();
+        tos.write(CONTENT);
+        tos.close();
+
+        // get and check input stream
+        ThresholdInputStream tis = (ThresholdInputStream) tos.getInputStream();
+
+        assertTrue(tis.isInMemory());
+        assertNull(tis.getTemporaryFile());
+        assertTrue(tis.markSupported());
+        assertEquals(CONTENT.length, tis.length());
+        assertArrayEquals(CONTENT, getBytesFromArray(tis.getBytes(), (int) tis.length()));
+
+        // read stream
+        byte[] buffer = new byte[CONTENT.length];
+        int len = tis.read(buffer);
+        assertEquals(CONTENT.length, len);
+        assertArrayEquals(CONTENT, buffer);
+
+        // rewind and read again
+        tis.rewind();
+        len = tis.read(buffer);
+        assertEquals(CONTENT.length, len);
+        assertArrayEquals(CONTENT, buffer);
+
+        // mark and reset
+        tis.rewind();
+        tis.read();
+        tis.mark(1024);
+        tis.read();
+        tis.read();
+        tis.reset();
+        len = tis.read(buffer);
+        assertEquals(CONTENT.length - 1, len);
+
+        // close and check
+        tis.close();
+
+        assertEquals(-1, tis.read());
+
+        try {
+            tis.getBytes();
+            fail("IllegalStateException expected!");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testTempFile() throws Exception {
+        ThresholdOutputStreamFactory streamFactory = ThresholdOutputStreamFactory.newInstance(null, 0, 1024, false);
+
+        // set content
+        ThresholdOutputStream tos = streamFactory.newOutputStream();
+        tos.write(CONTENT);
+        tos.close();
+
+        // get and check input stream
+        ThresholdInputStream tis = (ThresholdInputStream) tos.getInputStream();
+
+        assertFalse(tis.isInMemory());
+        assertTrue(tis.markSupported());
+        assertNull(tis.getBytes());
+        assertEquals(CONTENT.length, tis.length());
+
+        assertTrue(tis.getTemporaryFile().exists());
+        assertEquals(CONTENT.length, tis.getTemporaryFile().length());
+
+        // read stream
+        byte[] buffer = new byte[CONTENT.length];
+        int len = tis.read(buffer);
+        assertEquals(CONTENT.length, len);
+        assertArrayEquals(CONTENT, buffer);
+        assertTrue(tis.getTemporaryFile().exists());
+
+        // rewind and read again
+        tis.rewind();
+        len = tis.read(buffer);
+        assertEquals(CONTENT.length, len);
+        assertArrayEquals(CONTENT, buffer);
+        assertTrue(tis.getTemporaryFile().exists());
+
+        // mark and reset
+        tis.rewind();
+        tis.read();
+        tis.mark(1024);
+        tis.read();
+        tis.read();
+        tis.reset();
+        len = tis.read(buffer);
+        assertEquals(CONTENT.length - 1, len);
+
+        File tempFile = tis.getTemporaryFile();
+
+        // close and check
+        tis.close();
+
+        assertEquals(-1, tis.read());
+        assertFalse(tempFile.exists());
+
+        try {
+            tis.getTemporaryFile();
+            fail("IllegalStateException expected!");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testThreshold() throws Exception {
+        int threshold = 8;
+
+        ThresholdOutputStreamFactory streamFactory = ThresholdOutputStreamFactory.newInstance(null, threshold, 1024,
+                false);
+
+        for (int i = 0; i < 20; i++) {
+            ThresholdOutputStream tos = streamFactory.newOutputStream();
+            for (int j = 0; j < i; j++) {
+                tos.write('0' + j);
+            }
+            tos.close();
+
+            ThresholdInputStream tis = (ThresholdInputStream) tos.getInputStream();
+            if (i > threshold) {
+                assertFalse(tis.isInMemory());
+            } else {
+                assertTrue(tis.isInMemory());
+            }
+
+            tos.close();
+        }
+    }
+
+    private byte[] getBytesFromArray(byte[] buffer, int len) {
+        byte[] result = new byte[len];
+
+        System.arraycopy(buffer, 0, result, 0, len);
+
+        return result;
+    }
+}

Propchange: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/ThresholdOutputStreamTest.java
------------------------------------------------------------------------------
    svn:eol-style = native