1 /*
2  * Copyright (c) 2003, 2015, 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  * @since 1.5
62  */
63 public class SerialBlob implements Blob, Serializable, Cloneable {
64 
65     /**
66      * A serialized array of uninterpreted bytes representing the
67      * value of this <code>SerialBlob</code> object.
68      * @serial
69      */
70     private byte[] buf;
71 
72     /**
73      * The internal representation of the <code>Blob</code> object on which this
74      * <code>SerialBlob</code> object is based.
75      */
76     private Blob blob;
77 
78     /**
79      * The number of bytes in this <code>SerialBlob</code> object's
80      * array of bytes.
81      * @serial
82      */
83     private long len;
84 
85     /**
86      * The original number of bytes in this <code>SerialBlob</code> object's
87      * array of bytes when it was first established.
88      * @serial
89      */
90     private long origLen;
91 
92     /**
93      * Constructs a <code>SerialBlob</code> object that is a serialized version of
94      * the given <code>byte</code> array.
95      * <p>
96      * The new <code>SerialBlob</code> object is initialized with the data from the
97      * <code>byte</code> array, thus allowing disconnected <code>RowSet</code>
98      * objects to establish serialized <code>Blob</code> objects without
99      * touching the data source.
100      *
101      * @param b the <code>byte</code> array containing the data for the
102      *        <code>Blob</code> object to be serialized
103      * @throws SerialException if an error occurs during serialization
104      * @throws SQLException if a SQL errors occurs
105      */
SerialBlob(byte[] b)106     public SerialBlob(byte[] b)
107             throws SerialException, SQLException {
108 
109         len = b.length;
110         buf = new byte[(int)len];
111         for(int i = 0; i < len; i++) {
112             buf[i] = b[i];
113         }
114         origLen = len;
115     }
116 
117 
118     /**
119      * Constructs a <code>SerialBlob</code> object that is a serialized
120      * version of the given <code>Blob</code> object.
121      * <P>
122      * The new <code>SerialBlob</code> object is initialized with the
123      * data from the <code>Blob</code> object; therefore, the
124      * <code>Blob</code> object should have previously brought the
125      * SQL <code>BLOB</code> value's data over to the client from
126      * the database. Otherwise, the new <code>SerialBlob</code> object
127      * will contain no data.
128      *
129      * @param blob the <code>Blob</code> object from which this
130      *     <code>SerialBlob</code> object is to be constructed;
131      *     cannot be null.
132      * @throws SerialException if an error occurs during serialization
133      * @throws SQLException if the <code>Blob</code> passed to this
134      *     to this constructor is a <code>null</code>.
135      * @see java.sql.Blob
136      */
SerialBlob(Blob blob)137     public SerialBlob (Blob blob)
138             throws SerialException, SQLException {
139 
140         if (blob == null) {
141             throw new SQLException(
142                     "Cannot instantiate a SerialBlob object with a null Blob object");
143         }
144 
145         len = blob.length();
146         buf = blob.getBytes(1, (int)len );
147         this.blob = blob;
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      */
getBytes(long pos, int length)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      */
length()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      */
getBinaryStream()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      */
position(byte[] pattern, long start)247     public long position(byte[] pattern, long start)
248             throws SerialException, SQLException {
249 
250         isValid();
251         if (start < 1 || start > len) {
252             return -1;
253         }
254 
255         int pos = (int)start-1; // internally Blobs are stored as arrays.
256         int i = 0;
257         long patlen = pattern.length;
258 
259         while (pos < len) {
260             if (pattern[i] == buf[pos]) {
261                 if (i + 1 == patlen) {
262                     return (pos + 1) - (patlen - 1);
263                 }
264                 i++; pos++; // increment pos, and i
265             } else if (pattern[i] != buf[pos]) {
266                 pos++; // increment pos only
267             }
268         }
269         return -1; // not found
270     }
271 
272     /**
273      * Returns the position in this <code>SerialBlob</code> object where
274      * the given <code>Blob</code> object begins, starting the search at the
275      * specified position.
276      *
277      * @param pattern the <code>Blob</code> object for which to search;
278      * @param start the position of the byte in this
279      *              <code>SerialBlob</code> object from which to begin
280      *              the search; the first position is <code>1</code>;
281      *              must not be less than <code>1</code> nor greater than
282      *              the length of this <code>SerialBlob</code> object
283      * @return the position in this <code>SerialBlob</code> object
284      *         where the given <code>Blob</code> object begins, starting
285      *         at the specified position; <code>-1</code> if the pattern is
286      *         not found or the given starting position is out of bounds;
287      *         position numbering for the return value starts at <code>1</code>
288      * @throws SerialException if an error occurs when serializing the blob;
289      * if {@code free} had previously been called on this object
290      * @throws SQLException if there is an error accessing the <code>BLOB</code>
291      *         value from the database
292      */
position(Blob pattern, long start)293     public long position(Blob pattern, long start)
294             throws SerialException, SQLException {
295         isValid();
296         return position(pattern.getBytes(1, (int)(pattern.length())), start);
297     }
298 
299     /**
300      * Writes the given array of bytes to the <code>BLOB</code> value that
301      * this <code>Blob</code> object represents, starting at position
302      * <code>pos</code>, and returns the number of bytes written.
303      *
304      * @param pos the position in the SQL <code>BLOB</code> value at which
305      *     to start writing. The first position is <code>1</code>;
306      *     must not be less than <code>1</code> nor greater than
307      *     the length of this <code>SerialBlob</code> object.
308      * @param bytes the array of bytes to be written to the <code>BLOB</code>
309      *        value that this <code>Blob</code> object represents
310      * @return the number of bytes written
311      * @throws SerialException if there is an error accessing the
312      *     <code>BLOB</code> value; or if an invalid position is set; if an
313      *     invalid offset value is set;
314      * if {@code free} had previously been called on this object
315      * @throws SQLException if there is an error accessing the <code>BLOB</code>
316      *         value from the database
317      * @see #getBytes
318      */
setBytes(long pos, byte[] bytes)319     public int setBytes(long pos, byte[] bytes)
320             throws SerialException, SQLException {
321         return setBytes(pos, bytes, 0, bytes.length);
322     }
323 
324     /**
325      * Writes all or part of the given <code>byte</code> array to the
326      * <code>BLOB</code> value that this <code>Blob</code> object represents
327      * and returns the number of bytes written.
328      * Writing starts at position <code>pos</code> in the <code>BLOB</code>
329      * value; <i>len</i> bytes from the given byte array are written.
330      *
331      * @param pos the position in the <code>BLOB</code> object at which
332      *     to start writing. The first position is <code>1</code>;
333      *     must not be less than <code>1</code> nor greater than
334      *     the length of this <code>SerialBlob</code> object.
335      * @param bytes the array of bytes to be written to the <code>BLOB</code>
336      *     value
337      * @param offset the offset in the <code>byte</code> array at which
338      *     to start reading the bytes. The first offset position is
339      *     <code>0</code>; must not be less than <code>0</code> nor greater
340      *     than the length of the <code>byte</code> array
341      * @param length the number of bytes to be written to the
342      *     <code>BLOB</code> value from the array of bytes <i>bytes</i>.
343      *
344      * @return the number of bytes written
345      * @throws SerialException if there is an error accessing the
346      *     <code>BLOB</code> value; if an invalid position is set; if an
347      *     invalid offset value is set; if number of bytes to be written
348      *     is greater than the <code>SerialBlob</code> length; or the combined
349      *     values of the length and offset is greater than the Blob buffer;
350      * if {@code free} had previously been called on this object
351      * @throws SQLException if there is an error accessing the <code>BLOB</code>
352      *         value from the database.
353      * @see #getBytes
354      */
setBytes(long pos, byte[] bytes, int offset, int length)355     public int setBytes(long pos, byte[] bytes, int offset, int length)
356             throws SerialException, SQLException {
357 
358         isValid();
359         if (offset < 0 || offset > bytes.length) {
360             throw new SerialException("Invalid offset in byte array set");
361         }
362 
363         if (pos < 1 || pos > this.length()) {
364             throw new SerialException("Invalid position in BLOB object set");
365         }
366 
367         if ((long)(length) > origLen) {
368             throw new SerialException("Buffer is not sufficient to hold the value");
369         }
370 
371         if ((length + offset) > bytes.length) {
372             throw new SerialException("Invalid OffSet. Cannot have combined offset " +
373                     "and length that is greater that the Blob buffer");
374         }
375 
376         int i = 0;
377         pos--; // correct to array indexing
378         while ( i < length || (offset + i +1) < (bytes.length-offset) ) {
379             this.buf[(int)pos + i] = bytes[offset + i ];
380             i++;
381         }
382         return i;
383     }
384 
385     /**
386      * Retrieves a stream that can be used to write to the <code>BLOB</code>
387      * value that this <code>Blob</code> object represents.  The stream begins
388      * at position <code>pos</code>. This method forwards the
389      * <code>setBinaryStream()</code> call to the underlying <code>Blob</code> in
390      * the event that this <code>SerialBlob</code> object is instantiated with a
391      * <code>Blob</code>. If this <code>SerialBlob</code> is instantiated with
392      * a <code>byte</code> array, a <code>SerialException</code> is thrown.
393      *
394      * @param pos the position in the <code>BLOB</code> value at which
395      *        to start writing
396      * @return a <code>java.io.OutputStream</code> object to which data can
397      *         be written
398      * @throws SQLException if there is an error accessing the
399      *            <code>BLOB</code> value
400      * @throws SerialException if the SerialBlob in not instantiated with a
401      *     <code>Blob</code> object that supports <code>setBinaryStream()</code>;
402      * if {@code free} had previously been called on this object
403      * @see #getBinaryStream
404      */
setBinaryStream(long pos)405     public java.io.OutputStream setBinaryStream(long pos)
406             throws SerialException, SQLException {
407 
408         isValid();
409         if (this.blob != null) {
410             return this.blob.setBinaryStream(pos);
411         } else {
412             throw new SerialException("Unsupported operation. SerialBlob cannot " +
413                 "return a writable binary stream, unless instantiated with a Blob object " +
414                 "that provides a setBinaryStream() implementation");
415         }
416     }
417 
418     /**
419      * Truncates the <code>BLOB</code> value that this <code>Blob</code>
420      * object represents to be <code>len</code> bytes in length.
421      *
422      * @param length the length, in bytes, to which the <code>BLOB</code>
423      *        value that this <code>Blob</code> object represents should be
424      *        truncated
425      * @throws SerialException if there is an error accessing the Blob value;
426      *     or the length to truncate is greater that the SerialBlob length;
427      * if {@code free} had previously been called on this object
428      */
truncate(long length)429     public void truncate(long length) throws SerialException {
430         isValid();
431         if (length > len) {
432             throw new SerialException(
433                     "Length more than what can be truncated");
434         } else if((int)length == 0) {
435             buf = new byte[0];
436             len = length;
437         } else {
438             len = length;
439             buf = this.getBytes(1, (int)len);
440         }
441     }
442 
443 
444     /**
445      * Returns an
446      * <code>InputStream</code> object that contains a partial
447      * {@code Blob} value, starting with the byte specified by pos, which is
448      * length bytes in length.
449      *
450      * @param pos the offset to the first byte of the partial value to be
451      * retrieved. The first byte in the {@code Blob} is at position 1
452      * @param length the length in bytes of the partial value to be retrieved
453      * @return
454      * <code>InputStream</code> through which the partial {@code Blob} value can
455      * be read.
456      * @throws SQLException if pos is less than 1 or if pos is greater than the
457      * number of bytes in the {@code Blob} or if pos + length is greater than
458      * the number of bytes in the {@code Blob}
459      * @throws SerialException if the {@code free} method had been previously
460      * called on this object
461      *
462      * @since 1.6
463      */
getBinaryStream(long pos, long length)464     public InputStream getBinaryStream(long pos, long length) throws SQLException {
465         isValid();
466         if (pos < 1 || pos > this.length()) {
467             throw new SerialException("Invalid position in BLOB object set");
468         }
469         if (length < 1 || length > len - pos + 1) {
470             throw new SerialException(
471                     "length is < 1 or pos + length > total number of bytes");
472         }
473         return new ByteArrayInputStream(buf, (int) pos - 1, (int) length);
474     }
475 
476 
477     /**
478      * This method frees the {@code SerialBlob} object and releases the
479      * resources that it holds. The object is invalid once the {@code free}
480      * method is called. <p> If {@code free} is called multiple times, the
481      * subsequent calls to {@code free} are treated as a no-op. </P>
482      *
483      * @throws SQLException if an error occurs releasing the Blob's resources
484      * @since 1.6
485      */
free()486     public void free() throws SQLException {
487         if (buf != null) {
488             buf = null;
489             if (blob != null) {
490                 blob.free();
491             }
492             blob = null;
493         }
494     }
495 
496     /**
497      * Compares this SerialBlob to the specified object.  The result is {@code
498      * true} if and only if the argument is not {@code null} and is a {@code
499      * SerialBlob} object that represents the same sequence of bytes as this
500      * object.
501      *
502      * @param  obj The object to compare this {@code SerialBlob} against
503      *
504      * @return {@code true} if the given object represents a {@code SerialBlob}
505      *          equivalent to this SerialBlob, {@code false} otherwise
506      *
507      */
equals(Object obj)508     public boolean equals(Object obj) {
509         if (this == obj) {
510             return true;
511         }
512         if (obj instanceof SerialBlob) {
513             SerialBlob sb = (SerialBlob)obj;
514             if (this.len == sb.len) {
515                 return Arrays.equals(buf, sb.buf);
516             }
517         }
518         return false;
519     }
520 
521     /**
522      * Returns a hash code for this {@code SerialBlob}.
523      * @return  a hash code value for this object.
524      */
hashCode()525     public int hashCode() {
526        return ((31 + Arrays.hashCode(buf)) * 31 + (int)len) * 31 + (int)origLen;
527     }
528 
529     /**
530      * Returns a clone of this {@code SerialBlob}. The copy will contain a
531      * reference to a clone of the internal byte array, not a reference
532      * to the original internal byte array of this {@code SerialBlob} object.
533      * The underlying {@code Blob} object will be set to null.
534      *
535      * @return  a clone of this SerialBlob
536      */
clone()537     public Object clone() {
538         try {
539             SerialBlob sb = (SerialBlob) super.clone();
540             sb.buf = (buf != null) ? Arrays.copyOf(buf, (int)len) : null;
541             sb.blob = null;
542             return sb;
543         } catch (CloneNotSupportedException ex) {
544             // this shouldn't happen, since we are Cloneable
545             throw new InternalError();
546         }
547     }
548 
549     /**
550      * readObject is called to restore the state of the SerialBlob from
551      * a stream.
552      */
readObject(ObjectInputStream s)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      */
writeObject(ObjectOutputStream s)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 compatibility
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      */
isValid()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 }
604