View Javadoc
1   /*
2    * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package javax.sql.rowset.serial;
27  
28  import java.sql.*;
29  import java.io.*;
30  import java.lang.reflect.*;
31  import java.util.Arrays;
32  
33  
34  /**
35   * A serialized mapping in the Java programming language of an SQL
36   * <code>BLOB</code> value.
37   * <P>
38   * The <code>SerialBlob</code> class provides a constructor for creating
39   * an instance from a <code>Blob</code> object.  Note that the
40   * <code>Blob</code>
41   * object should have brought the SQL <code>BLOB</code> value's data over
42   * to the client before a <code>SerialBlob</code> object
43   * is constructed from it.  The data of an SQL <code>BLOB</code> value can
44   * be materialized on the client as an array of bytes (using the method
45   * <code>Blob.getBytes</code>) or as a stream of uninterpreted bytes
46   * (using the method <code>Blob.getBinaryStream</code>).
47   * <P>
48   * <code>SerialBlob</code> methods make it possible to make a copy of a
49   * <code>SerialBlob</code> object as an array of bytes or as a stream.
50   * They also make it possible to locate a given pattern of bytes or a
51   * <code>Blob</code> object within a <code>SerialBlob</code> object
52   * and to update or truncate a <code>Blob</code> object.
53   *
54   * <h3> Thread safety </h3>
55   *
56   * <p> A SerialBlob is not safe for use by multiple concurrent threads.  If a
57   * SerialBlob is to be used by more than one thread then access to the SerialBlob
58   * should be controlled by appropriate synchronization.
59   *
60   * @author Jonathan Bruce
61   */
62  public class SerialBlob implements Blob, Serializable, Cloneable {
63  
64      /**
65       * A serialized array of uninterpreted bytes representing the
66       * value of this <code>SerialBlob</code> object.
67       * @serial
68       */
69      private byte buf[];
70  
71      /**
72       * The internal representation of the <code>Blob</code> object on which this
73       * <code>SerialBlob</code> object is based.
74       */
75      private Blob blob;
76  
77      /**
78       * The number of bytes in this <code>SerialBlob</code> object's
79       * array of bytes.
80       * @serial
81       */
82      private long len;
83  
84      /**
85       * The original number of bytes in this <code>SerialBlob</code> object's
86       * array of bytes when it was first established.
87       * @serial
88       */
89      private long origLen;
90  
91      /**
92       * Constructs a <code>SerialBlob</code> object that is a serialized version of
93       * the given <code>byte</code> array.
94       * <p>
95       * The new <code>SerialBlob</code> object is initialized with the data from the
96       * <code>byte</code> array, thus allowing disconnected <code>RowSet</code>
97       * objects to establish serialized <code>Blob</code> objects without
98       * touching the data source.
99       *
100      * @param b the <code>byte</code> array containing the data for the
101      *        <code>Blob</code> object to be serialized
102      * @throws SerialException if an error occurs during serialization
103      * @throws SQLException if a SQL errors occurs
104      */
105     public SerialBlob(byte[] b) throws SerialException, SQLException {
106 
107         len = b.length;
108         buf = new byte[(int)len];
109         for(int i = 0; i < len; i++) {
110            buf[i] = b[i];
111         }
112         origLen = len;
113     }
114 
115 
116     /**
117      * Constructs a <code>SerialBlob</code> object that is a serialized
118      * version of the given <code>Blob</code> object.
119      * <P>
120      * The new <code>SerialBlob</code> object is initialized with the
121      * data from the <code>Blob</code> object; therefore, the
122      * <code>Blob</code> object should have previously brought the
123      * SQL <code>BLOB</code> value's data over to the client from
124      * the database. Otherwise, the new <code>SerialBlob</code> object
125      * will contain no data.
126      *
127      * @param blob the <code>Blob</code> object from which this
128      *     <code>SerialBlob</code> object is to be constructed;
129      *     cannot be null.
130      * @throws SerialException if an error occurs during serialization
131      * @throws SQLException if the <code>Blob</code> passed to this
132      *     to this constructor is a <code>null</code>.
133      * @see java.sql.Blob
134      */
135     public SerialBlob (Blob blob) throws SerialException, SQLException {
136 
137         if (blob == null) {
138             throw new SQLException("Cannot instantiate a SerialBlob " +
139                  "object with a null Blob object");
140         }
141 
142         len = blob.length();
143         buf = blob.getBytes(1, (int)len );
144         this.blob = blob;
145 
146          //if ( len < 10240000)
147          // len = 10240000;
148         origLen = len;
149     }
150 
151     /**
152      * Copies the specified number of bytes, starting at the given
153      * position, from this <code>SerialBlob</code> object to
154      * another array of bytes.
155      * <P>
156      * Note that if the given number of bytes to be copied is larger than
157      * the length of this <code>SerialBlob</code> object's array of
158      * bytes, the given number will be shortened to the array's length.
159      *
160      * @param pos the ordinal position of the first byte in this
161      *            <code>SerialBlob</code> object to be copied;
162      *            numbering starts at <code>1</code>; must not be less
163      *            than <code>1</code> and must be less than or equal
164      *            to the length of this <code>SerialBlob</code> object
165      * @param length the number of bytes to be copied
166      * @return an array of bytes that is a copy of a region of this
167      *         <code>SerialBlob</code> object, starting at the given
168      *         position and containing the given number of consecutive bytes
169      * @throws SerialException if the given starting position is out of bounds;
170      * if {@code free} had previously been called on this object
171      */
172     public byte[] getBytes(long pos, int length) throws SerialException {
173         isValid();
174         if (length > len) {
175             length = (int)len;
176         }
177 
178         if (pos < 1 || len - pos < 0 ) {
179             throw new SerialException("Invalid arguments: position cannot be "
180                     + "less than 1 or greater than the length of the SerialBlob");
181         }
182 
183         pos--; // correct pos to array index
184 
185         byte[] b = new byte[length];
186 
187         for (int i = 0; i < length; i++) {
188             b[i] = this.buf[(int)pos];
189             pos++;
190         }
191         return b;
192     }
193 
194     /**
195      * Retrieves the number of bytes in this <code>SerialBlob</code>
196      * object's array of bytes.
197      *
198      * @return a <code>long</code> indicating the length in bytes of this
199      *         <code>SerialBlob</code> object's array of bytes
200      * @throws SerialException if an error occurs;
201      * if {@code free} had previously been called on this object
202      */
203     public long length() throws SerialException {
204         isValid();
205         return len;
206     }
207 
208     /**
209      * Returns this <code>SerialBlob</code> object as an input stream.
210      * Unlike the related method, <code>setBinaryStream</code>,
211      * a stream is produced regardless of whether the <code>SerialBlob</code>
212      * was created with a <code>Blob</code> object or a <code>byte</code> array.
213      *
214      * @return a <code>java.io.InputStream</code> object that contains
215      *         this <code>SerialBlob</code> object's array of bytes
216      * @throws SerialException if an error occurs;
217      * if {@code free} had previously been called on this object
218      * @see #setBinaryStream
219      */
220     public java.io.InputStream getBinaryStream() throws SerialException {
221         isValid();
222         InputStream stream = new ByteArrayInputStream(buf);
223         return stream;
224     }
225 
226     /**
227      * Returns the position in this <code>SerialBlob</code> object where
228      * the given pattern of bytes begins, starting the search at the
229      * specified position.
230      *
231      * @param pattern the pattern of bytes for which to search
232      * @param start the position of the byte in this
233      *              <code>SerialBlob</code> object from which to begin
234      *              the search; the first position is <code>1</code>;
235      *              must not be less than <code>1</code> nor greater than
236      *              the length of this <code>SerialBlob</code> object
237      * @return the position in this <code>SerialBlob</code> object
238      *         where the given pattern begins, starting at the specified
239      *         position; <code>-1</code> if the pattern is not found
240      *         or the given starting position is out of bounds; position
241      *         numbering for the return value starts at <code>1</code>
242      * @throws SerialException if an error occurs when serializing the blob;
243      * if {@code free} had previously been called on this object
244      * @throws SQLException if there is an error accessing the <code>BLOB</code>
245      *         value from the database
246      */
247     public long position(byte[] pattern, long start)
248                 throws SerialException, SQLException {
249         isValid();
250         if (start < 1 || start > len) {
251             return -1;
252         }
253 
254         int pos = (int)start-1; // internally Blobs are stored as arrays.
255         int i = 0;
256         long patlen = pattern.length;
257 
258         while (pos < len) {
259             if (pattern[i] == buf[pos]) {
260                 if (i + 1 == patlen) {
261                     return (pos + 1) - (patlen - 1);
262                 }
263                 i++; pos++; // increment pos, and i
264             } else if (pattern[i] != buf[pos]) {
265                 pos++; // increment pos only
266             }
267         }
268         return -1; // not found
269     }
270 
271     /**
272      * Returns the position in this <code>SerialBlob</code> object where
273      * the given <code>Blob</code> object begins, starting the search at the
274      * specified position.
275      *
276      * @param pattern the <code>Blob</code> object for which to search;
277      * @param start the position of the byte in this
278      *              <code>SerialBlob</code> object from which to begin
279      *              the search; the first position is <code>1</code>;
280      *              must not be less than <code>1</code> nor greater than
281      *              the length of this <code>SerialBlob</code> object
282      * @return the position in this <code>SerialBlob</code> object
283      *         where the given <code>Blob</code> object begins, starting
284      *         at the specified position; <code>-1</code> if the pattern is
285      *         not found or the given starting position is out of bounds;
286      *         position numbering for the return value starts at <code>1</code>
287      * @throws SerialException if an error occurs when serializing the blob;
288      * if {@code free} had previously been called on this object
289      * @throws SQLException if there is an error accessing the <code>BLOB</code>
290      *         value from the database
291      */
292     public long position(Blob pattern, long start)
293        throws SerialException, SQLException {
294         isValid();
295         return position(pattern.getBytes(1, (int)(pattern.length())), start);
296     }
297 
298     /**
299      * Writes the given array of bytes to the <code>BLOB</code> value that
300      * this <code>Blob</code> object represents, starting at position
301      * <code>pos</code>, and returns the number of bytes written.
302      *
303      * @param pos the position in the SQL <code>BLOB</code> value at which
304      *     to start writing. The first position is <code>1</code>;
305      *     must not be less than <code>1</code> nor greater than
306      *     the length of this <code>SerialBlob</code> object.
307      * @param bytes the array of bytes to be written to the <code>BLOB</code>
308      *        value that this <code>Blob</code> object represents
309      * @return the number of bytes written
310      * @throws SerialException if there is an error accessing the
311      *     <code>BLOB</code> value; or if an invalid position is set; if an
312      *     invalid offset value is set;
313      * if {@code free} had previously been called on this object
314      * @throws SQLException if there is an error accessing the <code>BLOB</code>
315      *         value from the database
316      * @see #getBytes
317      */
318     public int setBytes(long pos, byte[] bytes)
319         throws SerialException, SQLException {
320         return (setBytes(pos, bytes, 0, bytes.length));
321     }
322 
323     /**
324      * Writes all or part of the given <code>byte</code> array to the
325      * <code>BLOB</code> value that this <code>Blob</code> object represents
326      * and returns the number of bytes written.
327      * Writing starts at position <code>pos</code> in the <code>BLOB</code>
328      * value; <i>len</i> bytes from the given byte array are written.
329      *
330      * @param pos the position in the <code>BLOB</code> object at which
331      *     to start writing. The first position is <code>1</code>;
332      *     must not be less than <code>1</code> nor greater than
333      *     the length of this <code>SerialBlob</code> object.
334      * @param bytes the array of bytes to be written to the <code>BLOB</code>
335      *     value
336      * @param offset the offset in the <code>byte</code> array at which
337      *     to start reading the bytes. The first offset position is
338      *     <code>0</code>; must not be less than <code>0</code> nor greater
339      *     than the length of the <code>byte</code> array
340      * @param length the number of bytes to be written to the
341      *     <code>BLOB</code> value from the array of bytes <i>bytes</i>.
342      *
343      * @return the number of bytes written
344      * @throws SerialException if there is an error accessing the
345      *     <code>BLOB</code> value; if an invalid position is set; if an
346      *     invalid offset value is set; if number of bytes to be written
347      *     is greater than the <code>SerialBlob</code> length; or the combined
348      *     values of the length and offset is greater than the Blob buffer;
349      * if {@code free} had previously been called on this object
350      * @throws SQLException if there is an error accessing the <code>BLOB</code>
351      *         value from the database.
352      * @see #getBytes
353      */
354     public int setBytes(long pos, byte[] bytes, int offset, int length)
355         throws SerialException, SQLException {
356 
357         isValid();
358         if (offset < 0 || offset > bytes.length) {
359             throw new SerialException("Invalid offset in byte array set");
360         }
361 
362         if (pos < 1 || pos > this.length()) {
363             throw new SerialException("Invalid position in BLOB object set");
364         }
365 
366         if ((long)(length) > origLen) {
367             throw new SerialException("Buffer is not sufficient to hold the value");
368         }
369 
370         if ((length + offset) > bytes.length) {
371             throw new SerialException("Invalid OffSet. Cannot have combined offset " +
372                 "and length that is greater that the Blob buffer");
373         }
374 
375         int i = 0;
376         pos--; // correct to array indexing
377         while ( i < length || (offset + i +1) < (bytes.length-offset) ) {
378             this.buf[(int)pos + i] = bytes[offset + i ];
379             i++;
380         }
381         return i;
382     }
383 
384     /**
385      * Retrieves a stream that can be used to write to the <code>BLOB</code>
386      * value that this <code>Blob</code> object represents.  The stream begins
387      * at position <code>pos</code>. This method forwards the
388      * <code>setBinaryStream()</code> call to the underlying <code>Blob</code> in
389      * the event that this <code>SerialBlob</code> object is instantiated with a
390      * <code>Blob</code>. If this <code>SerialBlob</code> is instantiated with
391      * a <code>byte</code> array, a <code>SerialException</code> is thrown.
392      *
393      * @param pos the position in the <code>BLOB</code> value at which
394      *        to start writing
395      * @return a <code>java.io.OutputStream</code> object to which data can
396      *         be written
397      * @throws SQLException if there is an error accessing the
398      *            <code>BLOB</code> value
399      * @throws SerialException if the SerialBlob in not instantiated with a
400      *     <code>Blob</code> object that supports <code>setBinaryStream()</code>;
401      * if {@code free} had previously been called on this object
402      * @see #getBinaryStream
403      */
404     public java.io.OutputStream setBinaryStream(long pos)
405         throws SerialException, SQLException {
406         isValid();
407         if (this.blob != null) {
408             return this.blob.setBinaryStream(pos);
409         } else {
410             throw new SerialException("Unsupported operation. SerialBlob cannot " +
411                 "return a writable binary stream, unless instantiated with a Blob object " +
412                 "that provides a setBinaryStream() implementation");
413         }
414     }
415 
416     /**
417      * Truncates the <code>BLOB</code> value that this <code>Blob</code>
418      * object represents to be <code>len</code> bytes in length.
419      *
420      * @param length the length, in bytes, to which the <code>BLOB</code>
421      *        value that this <code>Blob</code> object represents should be
422      *        truncated
423      * @throws SerialException if there is an error accessing the Blob value;
424      *     or the length to truncate is greater that the SerialBlob length;
425      * if {@code free} had previously been called on this object
426      */
427     public void truncate(long length) throws SerialException {
428 
429         isValid();
430         if (length > len) {
431            throw new SerialException
432               ("Length more than what can be truncated");
433         } else if((int)length == 0) {
434              buf = new byte[0];
435              len = length;
436         } else {
437              len = length;
438              buf = this.getBytes(1, (int)len);
439         }
440     }
441 
442 
443     /**
444      * Returns an
445      * <code>InputStream</code> object that contains a partial
446      * {@code Blob} value, starting with the byte specified by pos, which is
447      * length bytes in length.
448      *
449      * @param pos the offset to the first byte of the partial value to be
450      * retrieved. The first byte in the {@code Blob} is at position 1
451      * @param length the length in bytes of the partial value to be retrieved
452      * @return
453      * <code>InputStream</code> through which the partial {@code Blob} value can
454      * be read.
455      * @throws SQLException if pos is less than 1 or if pos is greater than the
456      * number of bytes in the {@code Blob} or if pos + length is greater than
457      * the number of bytes in the {@code Blob}
458      * @throws SerialException if the {@code free} method had been previously
459      * called on this object
460      *
461      * @since 1.6
462      */
463     public InputStream getBinaryStream(long pos, long length) throws SQLException {
464         isValid();
465         if (pos < 1 || pos > this.length()) {
466             throw new SerialException("Invalid position in BLOB object set");
467         }
468         if (length < 1 || length > len - pos + 1) {
469             throw new SerialException("length is < 1 or pos + length >"
470                     + "total number of bytes");
471         }
472         return new ByteArrayInputStream(buf, (int) pos - 1, (int) length);
473     }
474 
475 
476     /**
477      * This method frees the {@code SeriableBlob} object and releases the
478      * resources that it holds. The object is invalid once the {@code free}
479      * method is called. <p> If {@code free} is called multiple times, the
480      * subsequent calls to {@code free} are treated as a no-op. </P>
481      *
482      * @throws SQLException if an error occurs releasing the Blob's resources
483      * @since 1.6
484      */
485     public void free() throws SQLException {
486         if (buf != null) {
487             buf = null;
488             if (blob != null) {
489                 blob.free();
490             }
491             blob = null;
492         }
493     }
494 
495     /**
496      * Compares this SerialBlob to the specified object.  The result is {@code
497      * true} if and only if the argument is not {@code null} and is a {@code
498      * SerialBlob} object that represents the same sequence of bytes as this
499      * object.
500      *
501      * @param  obj The object to compare this {@code SerialBlob} against
502      *
503      * @return {@code true} if the given object represents a {@code SerialBlob}
504      *          equivalent to this SerialBlob, {@code false} otherwise
505      *
506      */
507     public boolean equals(Object obj) {
508         if (this == obj) {
509             return true;
510         }
511         if (obj instanceof SerialBlob) {
512             SerialBlob sb = (SerialBlob)obj;
513             if (this.len == sb.len) {
514                 return Arrays.equals(buf, sb.buf);
515             }
516         }
517         return false;
518     }
519 
520     /**
521      * Returns a hash code for this {@code SerialBlob}.
522      * @return  a hash code value for this object.
523      */
524     public int hashCode() {
525        return ((31 + Arrays.hashCode(buf)) * 31 + (int)len) * 31 + (int)origLen;
526     }
527 
528     /**
529      * Returns a clone of this {@code SerialBlob}. The copy will contain a
530      * reference to a clone of the internal byte array, not a reference
531      * to the original internal byte array of this {@code SerialBlob} object.
532      * The underlying {@code Blob} object will be set to null.
533      *
534      * @return  a clone of this SerialBlob
535      */
536     public Object clone() {
537         try {
538             SerialBlob sb = (SerialBlob) super.clone();
539             sb.buf =  (buf != null) ? Arrays.copyOf(buf, (int)len) : null;
540             sb.blob = null;
541             return sb;
542         } catch (CloneNotSupportedException ex) {
543             // this shouldn't happen, since we are Cloneable
544             throw new InternalError();
545         }
546 
547     }
548 
549     /**
550      * readObject is called to restore the state of the SerialBlob from
551      * a stream.
552      */
553     private void readObject(ObjectInputStream s)
554             throws IOException, ClassNotFoundException {
555 
556         ObjectInputStream.GetField fields = s.readFields();
557        byte[] tmp = (byte[])fields.get("buf", null);
558        if (tmp == null)
559            throw new InvalidObjectException("buf is null and should not be!");
560        buf = tmp.clone();
561        len = fields.get("len", 0L);
562        if (buf.length != len)
563            throw new InvalidObjectException("buf is not the expected size");
564        origLen = fields.get("origLen", 0L);
565        blob = (Blob) fields.get("blob", null);
566     }
567 
568     /**
569      * writeObject is called to save the state of the SerialBlob
570      * to a stream.
571      */
572     private void writeObject(ObjectOutputStream s)
573             throws IOException, ClassNotFoundException {
574 
575         ObjectOutputStream.PutField fields = s.putFields();
576         fields.put("buf", buf);
577         fields.put("len", len);
578         fields.put("origLen", origLen);
579         // Note: this check to see if it is an instance of Serializable
580         // is for backwards compatibiity
581         fields.put("blob", blob instanceof Serializable ? blob : null);
582         s.writeFields();
583     }
584 
585     /**
586      * Check to see if this object had previously had its {@code free} method
587      * called
588      *
589      * @throws SerialException
590      */
591     private void isValid() throws SerialException {
592         if (buf == null) {
593             throw new SerialException("Error: You cannot call a method on a "
594                     + "SerialBlob instance once free() has been called.");
595         }
596     }
597 
598     /**
599      * The identifier that assists in the serialization of this
600      * {@code SerialBlob} object.
601      */
602     static final long serialVersionUID = -8144641928112860441L;
603 }