You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commons-dev@ws.apache.org by ve...@apache.org on 2010/05/24 10:42:37 UTC

svn commit: r947564 - in /webservices/commons/trunk/modules/axiom/modules/axiom-api/src: main/java/org/apache/axiom/util/blob/ test/java/org/apache/axiom/util/blob/

Author: veithen
Date: Mon May 24 08:42:37 2010
New Revision: 947564

URL: http://svn.apache.org/viewvc?rev=947564&view=rev
Log:
Some cleanup of the org.apache.axiom.util.blob API to give it a chance to remain stable in the next releases.

Added:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/package.html
Removed:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/Slice.java
Modified:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/Blob.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/MemoryBlob.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/OverflowBlob.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/WritableBlob.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/blob/WritableBlobTestBase.java

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/Blob.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/Blob.java?rev=947564&r1=947563&r2=947564&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/Blob.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/Blob.java Mon May 24 08:42:37 2010
@@ -23,6 +23,15 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import org.apache.axiom.ext.io.StreamCopyException;
+
+/**
+ * Stores binary data.
+ * <p>
+ * Not that blobs are not thread safe. While they support requesting multiple concurrent input
+ * streams, these streams must be used in the same thread, unless appropriate synchronization or
+ * locking is done.
+ */
 public interface Blob {
     /**
      * Get an input stream to read the data in the blob.
@@ -35,11 +44,14 @@ public interface Blob {
     /**
      * Write the data to a given output stream.
      * 
-     * @param out The output stream to write the data to. This method will
-     *            not close the stream.
-     * @throws IOException
+     * @param out
+     *            The output stream to write the data to. This method will not close the stream.
+     * @throws StreamCopyException
+     *             Thrown if there is an I/O when reading the data from the blob or when writing it
+     *             to the stream. {@link StreamCopyException#getOperation()} can be used to
+     *             determine whether the failed operation was a read or a write.
      */
-    void writeTo(OutputStream out) throws IOException;
+    void writeTo(OutputStream out) throws StreamCopyException;
 
     /**
      * Get the length of the data in the blob, i.e. the number of bytes.

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/MemoryBlob.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/MemoryBlob.java?rev=947564&r1=947563&r2=947564&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/MemoryBlob.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/MemoryBlob.java Mon May 24 08:42:37 2010
@@ -67,7 +67,6 @@ public class MemoryBlob implements Writa
     }
 
     class InputStreamImpl extends InputStream {
-        private final int size;
         private int i;
         private int currIndex;
         private int totalIndex;
@@ -76,8 +75,6 @@ public class MemoryBlob implements Writa
         private byte[] read_byte = new byte[1];
         
         public InputStreamImpl() {
-            size = (int)getLength();
-            currBuffer = (byte[]) data.get(0);
         }
 
         public int read() throws IOException {
@@ -91,7 +88,7 @@ public class MemoryBlob implements Writa
         }
 
         public int available() throws IOException {
-            return size - totalIndex;
+            return (int)getLength() - totalIndex;
         }
 
 
@@ -104,11 +101,15 @@ public class MemoryBlob implements Writa
         }
 
         public int read(byte[] b, int off, int len) throws IOException {
+            int size = (int)getLength();
             int total = 0;
             if (totalIndex >= size) {
                 return -1;
             }
             while (total < len && totalIndex < size) {
+                if (currBuffer == null) {
+                    currBuffer = (byte[]) data.get(i);
+                }
                 int copy = Math.min(len - total, BUFFER_SIZE - currIndex);
                 copy = Math.min(copy, size - totalIndex);
                 System.arraycopy(currBuffer, currIndex, b, off, copy);
@@ -118,13 +119,12 @@ public class MemoryBlob implements Writa
                 off += copy;
                 if (currIndex >= BUFFER_SIZE) {
                     if (i+1 < data.size()) {
-                        currBuffer = (byte[]) data.get(i+1);
                         i++;
                         currIndex = 0;
                     } else {
-                        currBuffer = null;
                         currIndex = BUFFER_SIZE;
                     } 
+                    currBuffer = null;
                 }
             }
             return total;
@@ -159,8 +159,16 @@ public class MemoryBlob implements Writa
         index = 0;
     }
     
+    public boolean isSupportingReadUncommitted() {
+        return true;
+    }
+
     public long getLength() {
-        return (BUFFER_SIZE * (data.size()-1)) + index;
+        if (data == null) {
+            return 0;
+        } else {
+            return (BUFFER_SIZE * (data.size()-1)) + index;
+        }
     }
 
     public BlobOutputStream getOutputStream() {
@@ -220,24 +228,28 @@ public class MemoryBlob implements Writa
     }
 
     public InputStream getInputStream() throws IOException {
-        if (!committed) {
-            throw new IllegalStateException();
-        } else {
-            return new InputStreamImpl();
-        }
+        return new InputStreamImpl();
     }
 
-    public void writeTo(OutputStream os) throws IOException {
+    public void writeTo(OutputStream os) throws StreamCopyException {
         int size = (int)getLength();
         if (data != null) {
-            int numBuffers = data.size();
-            for (int j = 0; j < numBuffers-1; j ++) {
-                os.write( (byte[]) data.get(j), 0, BUFFER_SIZE);
-            }
-            if (numBuffers > 0) {
-                int writeLimit = size - ((numBuffers-1) * BUFFER_SIZE);
-                os.write( (byte[]) data.get(numBuffers-1), 0, writeLimit);
+            try {
+                int numBuffers = data.size();
+                for (int j = 0; j < numBuffers-1; j ++) {
+                    os.write( (byte[]) data.get(j), 0, BUFFER_SIZE);
+                }
+                if (numBuffers > 0) {
+                    int writeLimit = size - ((numBuffers-1) * BUFFER_SIZE);
+                    os.write( (byte[]) data.get(numBuffers-1), 0, writeLimit);
+                }
+            } catch (IOException ex) {
+                throw new StreamCopyException(StreamCopyException.WRITE, ex);
             }
         }
     }
+
+    public void release() {
+        // no-op
+    }
 }

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/OverflowBlob.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/OverflowBlob.java?rev=947564&r1=947563&r2=947564&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/OverflowBlob.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/OverflowBlob.java Mon May 24 08:42:37 2010
@@ -256,6 +256,11 @@ public class OverflowBlob implements Wri
         chunks = new byte[numberOfChunks][];
     }
     
+    public boolean isSupportingReadUncommitted() {
+        // This is actually a limitation of the implementation, not an intrinsic limitation
+        return false;
+    }
+
     /**
      * Get the current chunk to write to, allocating it if necessary.
      * 
@@ -388,28 +393,53 @@ public class OverflowBlob implements Wri
         }
     }
     
-    public void writeTo(OutputStream out) throws IOException {
+    public void writeTo(OutputStream out) throws StreamCopyException {
         if (temporaryFile != null) {
-            FileInputStream in = new FileInputStream(temporaryFile);
+            FileInputStream in;
+            try {
+                in = new FileInputStream(temporaryFile);
+            } catch (IOException ex) {
+                throw new StreamCopyException(StreamCopyException.READ, ex);
+            }
             try {
                 if (out instanceof ReadFromSupport) {
                     ((ReadFromSupport)out).readFrom(in, -1);
                 } else {
                     byte[] buf = new byte[4096];
-                    int c;
-                    while ((c = in.read(buf)) != -1) {
-                        out.write(buf, 0, c);
+                    while (true) {
+                        int c;
+                        try {
+                            c = in.read(buf);
+                        } catch (IOException ex) {
+                            throw new StreamCopyException(StreamCopyException.READ, ex);
+                        }
+                        if (c == -1) {
+                            break;
+                        }
+                        try {
+                            out.write(buf, 0, c);
+                        } catch (IOException ex) {
+                            throw new StreamCopyException(StreamCopyException.WRITE, ex);
+                        }
                     }
                 }
             } finally {
-                in.close();
+                try {
+                    in.close();
+                } catch (IOException ex) {
+                    throw new StreamCopyException(StreamCopyException.READ, ex);
+                }
             }
         } else {
-            for (int i=0; i<chunkIndex; i++) {
-                out.write(chunks[i]);
-            }
-            if (chunkOffset > 0) {
-                out.write(chunks[chunkIndex], 0, chunkOffset);
+            try {
+                for (int i=0; i<chunkIndex; i++) {
+                    out.write(chunks[i]);
+                }
+                if (chunkOffset > 0) {
+                    out.write(chunks[chunkIndex], 0, chunkOffset);
+                }
+            } catch (IOException ex) {
+                throw new StreamCopyException(StreamCopyException.WRITE, ex);
             }
         }
     }

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/WritableBlob.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/WritableBlob.java?rev=947564&r1=947563&r2=947564&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/WritableBlob.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/WritableBlob.java Mon May 24 08:42:37 2010
@@ -40,9 +40,29 @@ import org.apache.axiom.ext.io.StreamCop
  * </dl>
  * If the blob is in state NEW or UNCOMMITTED, any call to a method defined by the {@link Blob}
  * superinterface will result in an {@link IllegalStateException}.
+ * <p>
+ * Blobs are not thread safe.
  */
 public interface WritableBlob extends Blob {
     /**
+     * Determine whether the blob supports reading in state NEW or UNCOMMITTED. If this method
+     * returns <code>false</code> and the blob is in state NEW or UNCOMMITTED, any call to a method
+     * defined by the {@link Blob} superinterface will result in an {@link IllegalStateException}.
+     * If this method returns <code>true</code>, then any data written to the blob will be
+     * immediately available for reading. This is also true for an input stream obtained from
+     * {@link Blob#getInputStream()} before the data is written. This implies that it is possible
+     * for the input stream to first report the end of the stream and later allow reading additional
+     * data. Therefore, a pair of streams obtained from {@link #getOutputStream()} and
+     * {@link Blob#getInputStream()} behaves differently than a {@link java.io.PipedOutputStream}
+     * and {@link java.io.PipedInputStream} pair, because in this situation
+     * {@link java.io.PipedInputStream} would block.
+     * 
+     * @return <code>true</code> if the blob allows reading the data in state NEW or UNCOMMITTED;
+     *         <code>false</code> if the blob allows read operations only in state COMMITTED
+     */
+    boolean isSupportingReadUncommitted();
+    
+    /**
      * Create an output stream to write data to the blob.
      * <p>
      * <em>Precondition:</em> The blob is in state NEW.
@@ -140,4 +160,9 @@ public interface WritableBlob extends Bl
      * @throws IllegalStateException if the blob is in state COMMITTED
      */
     long readFrom(InputStream in, long length) throws StreamCopyException;
+    
+    /**
+     * Release all resources held by this blob.
+     */
+    void release();
 }

Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/package.html
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/package.html?rev=947564&view=auto
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/package.html (added)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/util/blob/package.html Mon May 24 08:42:37 2010
@@ -0,0 +1,10 @@
+<html>
+<body>
+Contains utility classes to work with binary data. They are typically used to store
+data temporarily and often provide a better alternative than using
+{@link java.io.ByteArrayOutputStream} and {@link java.io.ByteArrayInputStream}.
+<p>
+Note that the API in this package is new in 1.2.9 and may undergo some changes in
+subsequent releases. Please use with care!
+</body>
+</html>
\ No newline at end of file

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/blob/WritableBlobTestBase.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/blob/WritableBlobTestBase.java?rev=947564&r1=947563&r2=947564&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/blob/WritableBlobTestBase.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/util/blob/WritableBlobTestBase.java Mon May 24 08:42:37 2010
@@ -177,10 +177,31 @@ public abstract class WritableBlobTestBa
     public void testGetInputStreamNew() throws Exception {
         WritableBlob blob = createBlob();
         try {
-            blob.getInputStream();
-            fail("Expected IllegalStateException");
-        } catch (IllegalStateException ex) {
-            // Expected
+            if (blob.isSupportingReadUncommitted()) {
+                // The order of instructions is important here: we first get
+                // the input stream (when the blob is still in state NEW) and
+                // only then we request the output stream (which will put the
+                // stream in state UNCOMMITTED).
+                InputStream in = blob.getInputStream();
+                OutputStream out = blob.getOutputStream();
+                assertEquals(-1, in.read());
+                // Check that any data written to the output stream immediately becomes available
+                // on the input stream.
+                byte[] data = new byte[1000];
+                random.nextBytes(data);
+                out.write(data);
+                assertTrue(Arrays.equals(data, IOUtils.toByteArray(in)));
+                random.nextBytes(data);
+                out.write(data);
+                assertTrue(Arrays.equals(data, IOUtils.toByteArray(in)));
+            } else {
+                try {
+                    blob.getInputStream();
+                    fail("Expected IllegalStateException");
+                } catch (IllegalStateException ex) {
+                    // Expected
+                }
+            }
         } finally {
             releaseBlob(blob);
         }
@@ -191,10 +212,24 @@ public abstract class WritableBlobTestBa
         try {
             OutputStream out = blob.getOutputStream();
             try {
-                blob.getInputStream();
-                fail("Expected IllegalStateException");
-            } catch (IllegalStateException ex) {
-                // Expected
+                byte[] data = new byte[1000];
+                random.nextBytes(data);
+                out.write(data);
+                if (blob.isSupportingReadUncommitted()) {
+                    InputStream in = blob.getInputStream();
+                    try {
+                        assertTrue(Arrays.equals(data, IOUtils.toByteArray(in)));
+                    } finally {
+                        in.close();
+                    }
+                } else {
+                    try {
+                        blob.getInputStream();
+                        fail("Expected IllegalStateException");
+                    } catch (IllegalStateException ex) {
+                        // Expected
+                    }
+                }
             } finally {
                 out.close();
             }