jdk/src/share/classes/javax/sql/rowset/serial/SerialBlob.java
changeset 14781 701d0765f75f
parent 14337 a003c6a54cb6
child 18564 f9db68ff2cbb
--- a/jdk/src/share/classes/javax/sql/rowset/serial/SerialBlob.java	Thu Dec 13 08:11:38 2012 +0800
+++ b/jdk/src/share/classes/javax/sql/rowset/serial/SerialBlob.java	Wed Dec 12 20:57:45 2012 -0500
@@ -51,6 +51,12 @@
  * <code>Blob</code> object within a <code>SerialBlob</code> object
  * and to update or truncate a <code>Blob</code> object.
  *
+ * <h4> Thread safety </h4>
+ *
+ * <p> A SerialBlob is not safe for use by multiple concurrent threads.  If a
+ * SerialBlob is to be used by more than one thread then access to the SerialBlob
+ * should be controlled by appropriate synchronization.
+ *
  * @author Jonathan Bruce
  */
 public class SerialBlob implements Blob, Serializable, Cloneable {
@@ -76,7 +82,7 @@
     private long len;
 
     /**
-     * The orginal number of bytes in this <code>SerialBlob</code> object's
+     * The original number of bytes in this <code>SerialBlob</code> object's
      * array of bytes when it was first established.
      * @serial
      */
@@ -160,9 +166,11 @@
      * @return an array of bytes that is a copy of a region of this
      *         <code>SerialBlob</code> object, starting at the given
      *         position and containing the given number of consecutive bytes
-     * @throws SerialException if the given starting position is out of bounds
+     * @throws SerialException if the given starting position is out of bounds;
+     * if {@code free} had previously been called on this object
      */
     public byte[] getBytes(long pos, int length) throws SerialException {
+        isValid();
         if (length > len) {
             length = (int)len;
         }
@@ -189,9 +197,11 @@
      *
      * @return a <code>long</code> indicating the length in bytes of this
      *         <code>SerialBlob</code> object's array of bytes
-     * @throws SerialException if an error occurs
+     * @throws SerialException if an error occurs;
+     * if {@code free} had previously been called on this object
      */
     public long length() throws SerialException {
+        isValid();
         return len;
     }
 
@@ -203,12 +213,14 @@
      *
      * @return a <code>java.io.InputStream</code> object that contains
      *         this <code>SerialBlob</code> object's array of bytes
-     * @throws SerialException if an error occurs
+     * @throws SerialException if an error occurs;
+     * if {@code free} had previously been called on this object
      * @see #setBinaryStream
      */
     public java.io.InputStream getBinaryStream() throws SerialException {
-         InputStream stream = new ByteArrayInputStream(buf);
-         return stream;
+        isValid();
+        InputStream stream = new ByteArrayInputStream(buf);
+        return stream;
     }
 
     /**
@@ -227,12 +239,14 @@
      *         position; <code>-1</code> if the pattern is not found
      *         or the given starting position is out of bounds; position
      *         numbering for the return value starts at <code>1</code>
-     * @throws SerialException if an error occurs when serializing the blob
+     * @throws SerialException if an error occurs when serializing the blob;
+     * if {@code free} had previously been called on this object
      * @throws SQLException if there is an error accessing the <code>BLOB</code>
      *         value from the database
      */
     public long position(byte[] pattern, long start)
                 throws SerialException, SQLException {
+        isValid();
         if (start < 1 || start > len) {
             return -1;
         }
@@ -270,12 +284,14 @@
      *         at the specified position; <code>-1</code> if the pattern is
      *         not found or the given starting position is out of bounds;
      *         position numbering for the return value starts at <code>1</code>
-     * @throws SerialException if an error occurs when serializing the blob
+     * @throws SerialException if an error occurs when serializing the blob;
+     * if {@code free} had previously been called on this object
      * @throws SQLException if there is an error accessing the <code>BLOB</code>
      *         value from the database
      */
     public long position(Blob pattern, long start)
        throws SerialException, SQLException {
+        isValid();
         return position(pattern.getBytes(1, (int)(pattern.length())), start);
     }
 
@@ -293,7 +309,8 @@
      * @return the number of bytes written
      * @throws SerialException if there is an error accessing the
      *     <code>BLOB</code> value; or if an invalid position is set; if an
-     *     invalid offset value is set
+     *     invalid offset value is set;
+     * if {@code free} had previously been called on this object
      * @throws SQLException if there is an error accessing the <code>BLOB</code>
      *         value from the database
      * @see #getBytes
@@ -328,7 +345,8 @@
      *     <code>BLOB</code> value; if an invalid position is set; if an
      *     invalid offset value is set; if number of bytes to be written
      *     is greater than the <code>SerialBlob</code> length; or the combined
-     *     values of the length and offset is greater than the Blob buffer
+     *     values of the length and offset is greater than the Blob buffer;
+     * if {@code free} had previously been called on this object
      * @throws SQLException if there is an error accessing the <code>BLOB</code>
      *         value from the database.
      * @see #getBytes
@@ -336,6 +354,7 @@
     public int setBytes(long pos, byte[] bytes, int offset, int length)
         throws SerialException, SQLException {
 
+        isValid();
         if (offset < 0 || offset > bytes.length) {
             throw new SerialException("Invalid offset in byte array set");
         }
@@ -378,11 +397,13 @@
      * @throws SQLException if there is an error accessing the
      *            <code>BLOB</code> value
      * @throws SerialException if the SerialBlob in not instantiated with a
-     *     <code>Blob</code> object that supports <code>setBinaryStream()</code>
+     *     <code>Blob</code> object that supports <code>setBinaryStream()</code>;
+     * if {@code free} had previously been called on this object
      * @see #getBinaryStream
      */
     public java.io.OutputStream setBinaryStream(long pos)
         throws SerialException, SQLException {
+        isValid();
         if (this.blob != null) {
             return this.blob.setBinaryStream(pos);
         } else {
@@ -400,54 +421,75 @@
      *        value that this <code>Blob</code> object represents should be
      *        truncated
      * @throws SerialException if there is an error accessing the Blob value;
-     *     or the length to truncate is greater that the SerialBlob length
+     *     or the length to truncate is greater that the SerialBlob length;
+     * if {@code free} had previously been called on this object
      */
     public void truncate(long length) throws SerialException {
 
-         if (length > len) {
-            throw new SerialException
-               ("Length more than what can be truncated");
-         } else if((int)length == 0) {
-              buf = new byte[0];
-              len = length;
-         } else {
-              len = length;
-              buf = this.getBytes(1, (int)len);
-         }
+        isValid();
+        if (length > len) {
+           throw new SerialException
+              ("Length more than what can be truncated");
+        } else if((int)length == 0) {
+             buf = new byte[0];
+             len = length;
+        } else {
+             len = length;
+             buf = this.getBytes(1, (int)len);
+        }
     }
 
 
     /**
-     * Returns an <code>InputStream</code> object that contains a partial <code>Blob</code> value,
-     * starting  with the byte specified by pos, which is length bytes in length.
+     * Returns an
+     * <code>InputStream</code> object that contains a partial
+     * {@code Blob} value, starting with the byte specified by pos, which is
+     * length bytes in length.
      *
-     * @param pos the offset to the first byte of the partial value to be retrieved.
-     *  The first byte in the <code>Blob</code> is at position 1
+     * @param pos the offset to the first byte of the partial value to be
+     * retrieved. The first byte in the {@code Blob} is at position 1
      * @param length the length in bytes of the partial value to be retrieved
-     * @return <code>InputStream</code> through which the partial <code>Blob</code> value can be read.
-     * @throws SQLException if pos is less than 1 or if pos is greater than the number of bytes
-     * in the <code>Blob</code> or if pos + length is greater than the number of bytes
-     * in the <code>Blob</code>
+     * @return
+     * <code>InputStream</code> through which the partial {@code Blob} value can
+     * be read.
+     * @throws SQLException if pos is less than 1 or if pos is greater than the
+     * number of bytes in the {@code Blob} or if pos + length is greater than
+     * the number of bytes in the {@code Blob}
+     * @throws SerialException if the {@code free} method had been previously
+     * called on this object
      *
      * @since 1.6
      */
-    public InputStream getBinaryStream(long pos,long length) throws SQLException {
-        throw new java.lang.UnsupportedOperationException("Not supported");
+    public InputStream getBinaryStream(long pos, long length) throws SQLException {
+        isValid();
+        if (pos < 1 || pos > this.length()) {
+            throw new SerialException("Invalid position in BLOB object set");
+        }
+        if (length < 1 || length > len - pos + 1) {
+            throw new SerialException("length is < 1 or pos + length >"
+                    + "total number of bytes");
+        }
+        return new ByteArrayInputStream(buf, (int) pos - 1, (int) length);
     }
 
 
     /**
-     * This method frees the <code>Blob</code> object and releases the resources that it holds.
-     * <code>Blob</code> object. The object is invalid once the <code>free</code>
-     * method is called. If <code>free</code> is called multiple times, the subsequent
-     * calls to <code>free</code> are treated as a no-op.
+     * This method frees the {@code SeriableBlob} object and releases the
+     * resources that it holds. The object is invalid once the {@code free}
+     * method is called. <p> If {@code free} is called multiple times, the
+     * subsequent calls to {@code free} are treated as a no-op. </P>
      *
-     * @throws SQLException if an error occurs releasing
-     * the Blob's resources
+     * @throws SQLException if an error occurs releasing the Blob's resources
      * @since 1.6
      */
     public void free() throws SQLException {
-        throw new java.lang.UnsupportedOperationException("Not supported");
+        if (buf != null) {
+            buf = null;
+            if (blob != null) {
+                blob.free();
+            }
+            blob = null;
+        }
     }
 
     /**
@@ -494,7 +536,7 @@
     public Object clone() {
         try {
             SerialBlob sb = (SerialBlob) super.clone();
-            sb.buf = Arrays.copyOf(buf, (int)len);
+            sb.buf =  (buf != null) ? Arrays.copyOf(buf, (int)len) : null;
             sb.blob = null;
             return sb;
         } catch (CloneNotSupportedException ex) {
@@ -541,9 +583,21 @@
     }
 
     /**
-         * The identifier that assists in the serialization of this <code>SerialBlob</code>
-     * object.
+     * Check to see if this object had previously had its {@code free} method
+     * called
+     *
+     * @throws SerialException
      */
+    private void isValid() throws SerialException {
+        if (buf == null) {
+            throw new SerialException("Error: You cannot call a method on a "
+                    + "SerialBlob instance once free() has been called.");
+        }
+    }
 
+    /**
+     * The identifier that assists in the serialization of this
+     * {@code SerialBlob} object.
+     */
     static final long serialVersionUID = -8144641928112860441L;
 }