1 /*
  2  * Copyright (c) 2003, 2019, 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  * <h2> Thread safety </h2>
 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     @SuppressWarnings("serial") // Not statically typed as Serializable; checked in writeObject
 77     private Blob blob;
 78 
 79     /**
 80      * The number of bytes in this <code>SerialBlob</code> object's
 81      * array of bytes.
 82      * @serial
 83      */
 84     private long len;
 85 
 86     /**
 87      * The original number of bytes in this <code>SerialBlob</code> object's
 88      * array of bytes when it was first established.
 89      * @serial
 90      */
 91     private long origLen;
 92 
 93     /**
 94      * Constructs a <code>SerialBlob</code> object that is a serialized version of
 95      * the given <code>byte</code> array.
 96      * <p>
 97      * The new <code>SerialBlob</code> object is initialized with the data from the
 98      * <code>byte</code> array, thus allowing disconnected <code>RowSet</code>
 99      * objects to establish serialized <code>Blob</code> objects without
100      * touching the data source.
101      *
102      * @param b the <code>byte</code> array containing the data for the
103      *        <code>Blob</code> object to be serialized
104      * @throws SerialException if an error occurs during serialization
105      * @throws SQLException if a SQL errors occurs
106      */
107     public SerialBlob(byte[] b)
108             throws SerialException, SQLException {
109 
110         len = b.length;
111         buf = new byte[(int)len];
112         for(int i = 0; i < len; i++) {
113             buf[i] = b[i];
114         }
115         origLen = len;
116     }
117 
118 
119     /**
120      * Constructs a <code>SerialBlob</code> object that is a serialized
121      * version of the given <code>Blob</code> object.
122      * <P>
123      * The new <code>SerialBlob</code> object is initialized with the
124      * data from the <code>Blob</code> object; therefore, the
125      * <code>Blob</code> object should have previously brought the
126      * SQL <code>BLOB</code> value's data over to the client from
127      * the database. Otherwise, the new <code>SerialBlob</code> object
128      * will contain no data.
129      *
130      * @param blob the <code>Blob</code> object from which this
131      *     <code>SerialBlob</code> object is to be constructed;
132      *     cannot be null.
133      * @throws SerialException if an error occurs during serialization
134      * @throws SQLException if the <code>Blob</code> passed to this
135      *     to this constructor is a <code>null</code>.
136      * @see java.sql.Blob
137      */
138     public SerialBlob (Blob blob)
139             throws SerialException, SQLException {
140 
141         if (blob == null) {
142             throw new SQLException(
143                     "Cannot instantiate a SerialBlob object with a null Blob object");
144         }
145 
146         len = blob.length();
147         buf = blob.getBytes(1, (int)len );
148         this.blob = blob;
149         origLen = len;
150     }
151 
152     /**
153      * Copies the specified number of bytes, starting at the given
154      * position, from this <code>SerialBlob</code> object to
155      * another array of bytes.
156      * <P>
157      * Note that if the given number of bytes to be copied is larger than
158      * the length of this <code>SerialBlob</code> object's array of
159      * bytes, the given number will be shortened to the array's length.
160      *
161      * @param pos the ordinal position of the first byte in this
162      *            <code>SerialBlob</code> object to be copied;
163      *            numbering starts at <code>1</code>; must not be less
164      *            than <code>1</code> and must be less than or equal
165      *            to the length of this <code>SerialBlob</code> object
166      * @param length the number of bytes to be copied
167      * @return an array of bytes that is a copy of a region of this
168      *         <code>SerialBlob</code> object, starting at the given
169      *         position and containing the given number of consecutive bytes
170      * @throws SerialException if the given starting position is out of bounds;
171      * if {@code free} had previously been called on this object
172      */
173     public byte[] getBytes(long pos, int length) throws SerialException {
174         isValid();
175         if (length > len) {
176             length = (int)len;
177         }
178 
179         if (pos < 1 || len - pos < 0 ) {
180             throw new SerialException("Invalid arguments: position cannot be "
181                     + "less than 1 or greater than the length of the SerialBlob");
182         }
183 
184         pos--; // correct pos to array index
185 
186         byte[] b = new byte[length];
187 
188         for (int i = 0; i < length; i++) {
189             b[i] = this.buf[(int)pos];
190             pos++;
191         }
192         return b;
193     }
194 
195     /**
196      * Retrieves the number of bytes in this <code>SerialBlob</code>
197      * object's array of bytes.
198      *
199      * @return a <code>long</code> indicating the length in bytes of this
200      *         <code>SerialBlob</code> object's array of bytes
201      * @throws SerialException if an error occurs;
202      * if {@code free} had previously been called on this object
203      */
204     public long length() throws SerialException {
205         isValid();
206         return len;
207     }
208 
209     /**
210      * Returns this <code>SerialBlob</code> object as an input stream.
211      * Unlike the related method, <code>setBinaryStream</code>,
212      * a stream is produced regardless of whether the <code>SerialBlob</code>
213      * was created with a <code>Blob</code> object or a <code>byte</code> array.
214      *
215      * @return a <code>java.io.InputStream</code> object that contains
216      *         this <code>SerialBlob</code> object's array of bytes
217      * @throws SerialException if an error occurs;
218      * if {@code free} had previously been called on this object
219      * @see #setBinaryStream
220      */
221     public java.io.InputStream getBinaryStream() throws SerialException {
222         isValid();
223         InputStream stream = new ByteArrayInputStream(buf);
224         return stream;
225     }
226 
227     /**
228      * Returns the position in this <code>SerialBlob</code> object where
229      * the given pattern of bytes begins, starting the search at the
230      * specified position.
231      *
232      * @param pattern the pattern of bytes for which to search
233      * @param start the position of the byte in this
234      *              <code>SerialBlob</code> object from which to begin
235      *              the search; the first position is <code>1</code>;
236      *              must not be less than <code>1</code> nor greater than
237      *              the length of this <code>SerialBlob</code> object
238      * @return the position in this <code>SerialBlob</code> object
239      *         where the given pattern begins, starting at the specified
240      *         position; <code>-1</code> if the pattern is not found
241      *         or the given starting position is out of bounds; position
242      *         numbering for the return value starts at <code>1</code>
243      * @throws SerialException if an error occurs when serializing the blob;
244      * if {@code free} had previously been called on this object
245      * @throws SQLException if there is an error accessing the <code>BLOB</code>
246      *         value from the database
247      */
248     public long position(byte[] pattern, long start)
249             throws SerialException, SQLException {
250 
251         isValid();
252         if (start < 1 || start > len) {
253             return -1;
254         }
255 
256         int pos = (int)start-1; // internally Blobs are stored as arrays.
257         int i = 0;
258         long patlen = pattern.length;
259 
260         while (pos < len) {
261             if (pattern[i] == buf[pos]) {
262                 if (i + 1 == patlen) {
263                     return (pos + 1) - (patlen - 1);
264                 }
265                 i++; pos++; // increment pos, and i
266             } else if (pattern[i] != buf[pos]) {
267                 pos++; // increment pos only
268             }
269         }
270         return -1; // not found
271     }
272 
273     /**
274      * Returns the position in this <code>SerialBlob</code> object where
275      * the given <code>Blob</code> object begins, starting the search at the
276      * specified position.
277      *
278      * @param pattern the <code>Blob</code> object for which to search;
279      * @param start the position of the byte in this
280      *              <code>SerialBlob</code> object from which to begin
281      *              the search; the first position is <code>1</code>;
282      *              must not be less than <code>1</code> nor greater than
283      *              the length of this <code>SerialBlob</code> object
284      * @return the position in this <code>SerialBlob</code> object
285      *         where the given <code>Blob</code> object begins, starting
286      *         at the specified position; <code>-1</code> if the pattern is
287      *         not found or the given starting position is out of bounds;
288      *         position numbering for the return value starts at <code>1</code>
289      * @throws SerialException if an error occurs when serializing the blob;
290      * if {@code free} had previously been called on this object
291      * @throws SQLException if there is an error accessing the <code>BLOB</code>
292      *         value from the database
293      */
294     public long position(Blob pattern, long start)
295             throws SerialException, SQLException {
296         isValid();
297         return position(pattern.getBytes(1, (int)(pattern.length())), start);
298     }
299 
300     /**
301      * Writes the given array of bytes to the <code>BLOB</code> value that
302      * this <code>Blob</code> object represents, starting at position
303      * <code>pos</code>, and returns the number of bytes written.
304      *
305      * @param pos the position in the SQL <code>BLOB</code> value at which
306      *     to start writing. The first position is <code>1</code>;
307      *     must not be less than <code>1</code> nor greater than
308      *     the length of this <code>SerialBlob</code> object.
309      * @param bytes the array of bytes to be written to the <code>BLOB</code>
310      *        value that this <code>Blob</code> object represents
311      * @return the number of bytes written
312      * @throws SerialException if there is an error accessing the
313      *     <code>BLOB</code> value; or if an invalid position is set; if an
314      *     invalid offset value is set;
315      * if {@code free} had previously been called on this object
316      * @throws SQLException if there is an error accessing the <code>BLOB</code>
317      *         value from the database
318      * @see #getBytes
319      */
320     public int setBytes(long pos, byte[] bytes)
321             throws SerialException, SQLException {
322         return setBytes(pos, bytes, 0, bytes.length);
323     }
324 
325     /**
326      * Writes all or part of the given <code>byte</code> array to the
327      * <code>BLOB</code> value that this <code>Blob</code> object represents
328      * and returns the number of bytes written.
329      * Writing starts at position <code>pos</code> in the <code>BLOB</code>
330      * value; <i>len</i> bytes from the given byte array are written.
331      *
332      * @param pos the position in the <code>BLOB</code> object at which
333      *     to start writing. The first position is <code>1</code>;
334      *     must not be less than <code>1</code> nor greater than
335      *     the length of this <code>SerialBlob</code> object.
336      * @param bytes the array of bytes to be written to the <code>BLOB</code>
337      *     value
338      * @param offset the offset in the <code>byte</code> array at which
339      *     to start reading the bytes. The first offset position is
340      *     <code>0</code>; must not be less than <code>0</code> nor greater
341      *     than the length of the <code>byte</code> array
342      * @param length the number of bytes to be written to the
343      *     <code>BLOB</code> value from the array of bytes <i>bytes</i>.
344      *
345      * @return the number of bytes written
346      * @throws SerialException if there is an error accessing the
347      *     <code>BLOB</code> value; if an invalid position is set; if an
348      *     invalid offset value is set; if number of bytes to be written
349      *     is greater than the <code>SerialBlob</code> length; or the combined
350      *     values of the length and offset is greater than the Blob buffer;
351      * if {@code free} had previously been called on this object
352      * @throws SQLException if there is an error accessing the <code>BLOB</code>
353      *         value from the database.
354      * @see #getBytes
355      */
356     public int setBytes(long pos, byte[] bytes, int offset, int length)
357             throws SerialException, SQLException {
358 
359         isValid();
360         if (offset < 0 || offset > bytes.length) {
361             throw new SerialException("Invalid offset in byte array set");
362         }
363 
364         if (pos < 1 || pos > this.length()) {
365             throw new SerialException("Invalid position in BLOB object set");
366         }
367 
368         if ((long)(length) > origLen) {
369             throw new SerialException("Buffer is not sufficient to hold the value");
370         }
371 
372         if ((length + offset) > bytes.length) {
373             throw new SerialException("Invalid OffSet. Cannot have combined offset " +
374                     "and length that is greater that the Blob buffer");
375         }
376 
377         int i = 0;
378         pos--; // correct to array indexing
379         while ( i < length || (offset + i +1) < (bytes.length-offset) ) {
380             this.buf[(int)pos + i] = bytes[offset + i ];
381             i++;
382         }
383         return i;
384     }
385 
386     /**
387      * Retrieves a stream that can be used to write to the <code>BLOB</code>
388      * value that this <code>Blob</code> object represents.  The stream begins
389      * at position <code>pos</code>. This method forwards the
390      * <code>setBinaryStream()</code> call to the underlying <code>Blob</code> in
391      * the event that this <code>SerialBlob</code> object is instantiated with a
392      * <code>Blob</code>. If this <code>SerialBlob</code> is instantiated with
393      * a <code>byte</code> array, a <code>SerialException</code> is thrown.
394      *
395      * @param pos the position in the <code>BLOB</code> value at which
396      *        to start writing
397      * @return a <code>java.io.OutputStream</code> object to which data can
398      *         be written
399      * @throws SQLException if there is an error accessing the
400      *            <code>BLOB</code> value
401      * @throws SerialException if the SerialBlob in not instantiated with a
402      *     <code>Blob</code> object that supports <code>setBinaryStream()</code>;
403      * if {@code free} had previously been called on this object
404      * @see #getBinaryStream
405      */
406     public java.io.OutputStream setBinaryStream(long pos)
407             throws SerialException, SQLException {
408 
409         isValid();
410         if (this.blob != null) {
411             return this.blob.setBinaryStream(pos);
412         } else {
413             throw new SerialException("Unsupported operation. SerialBlob cannot " +
414                 "return a writable binary stream, unless instantiated with a Blob object " +
415                 "that provides a setBinaryStream() implementation");
416         }
417     }
418 
419     /**
420      * Truncates the <code>BLOB</code> value that this <code>Blob</code>
421      * object represents to be <code>len</code> bytes in length.
422      *
423      * @param length the length, in bytes, to which the <code>BLOB</code>
424      *        value that this <code>Blob</code> object represents should be
425      *        truncated
426      * @throws SerialException if there is an error accessing the Blob value;
427      *     or the length to truncate is greater that the SerialBlob length;
428      * if {@code free} had previously been called on this object
429      */
430     public void truncate(long length) throws SerialException {
431         isValid();
432         if (length > len) {
433             throw new SerialException(
434                     "Length more than what can be truncated");
435         } else if((int)length == 0) {
436             buf = new byte[0];
437             len = length;
438         } else {
439             len = length;
440             buf = this.getBytes(1, (int)len);
441         }
442     }
443 
444 
445     /**
446      * Returns an
447      * <code>InputStream</code> object that contains a partial
448      * {@code Blob} value, starting with the byte specified by pos, which is
449      * length bytes in length.
450      *
451      * @param pos the offset to the first byte of the partial value to be
452      * retrieved. The first byte in the {@code Blob} is at position 1
453      * @param length the length in bytes of the partial value to be retrieved
454      * @return
455      * <code>InputStream</code> through which the partial {@code Blob} value can
456      * be read.
457      * @throws SQLException if pos is less than 1 or if pos is greater than the
458      * number of bytes in the {@code Blob} or if pos + length is greater than
459      * the number of bytes in the {@code Blob}
460      * @throws SerialException if the {@code free} method had been previously
461      * called on this object
462      *
463      * @since 1.6
464      */
465     public InputStream getBinaryStream(long pos, long length) throws SQLException {
466         isValid();
467         if (pos < 1 || pos > this.length()) {
468             throw new SerialException("Invalid position in BLOB object set");
469         }
470         if (length < 1 || length > len - pos + 1) {
471             throw new SerialException(
472                     "length is < 1 or pos + length > total number of bytes");
473         }
474         return new ByteArrayInputStream(buf, (int) pos - 1, (int) length);
475     }
476 
477 
478     /**
479      * This method frees the {@code SerialBlob} object and releases the
480      * resources that it holds. The object is invalid once the {@code free}
481      * method is called. <p> If {@code free} is called multiple times, the
482      * subsequent calls to {@code free} are treated as a no-op. </P>
483      *
484      * @throws SQLException if an error occurs releasing the Blob's resources
485      * @since 1.6
486      */
487     public void free() throws SQLException {
488         if (buf != null) {
489             buf = null;
490             if (blob != null) {
491                 blob.free();
492             }
493             blob = null;
494         }
495     }
496 
497     /**
498      * Compares this SerialBlob to the specified object.  The result is {@code
499      * true} if and only if the argument is not {@code null} and is a {@code
500      * SerialBlob} object that represents the same sequence of bytes as this
501      * object.
502      *
503      * @param  obj The object to compare this {@code SerialBlob} against
504      *
505      * @return {@code true} if the given object represents a {@code SerialBlob}
506      *          equivalent to this SerialBlob, {@code false} otherwise
507      *
508      */
509     public boolean equals(Object obj) {
510         if (this == obj) {
511             return true;
512         }
513         if (obj instanceof SerialBlob) {
514             SerialBlob sb = (SerialBlob)obj;
515             if (this.len == sb.len) {
516                 return Arrays.equals(buf, sb.buf);
517             }
518         }
519         return false;
520     }
521 
522     /**
523      * Returns a hash code for this {@code SerialBlob}.
524      * @return  a hash code value for this object.
525      */
526     public int hashCode() {
527        return ((31 + Arrays.hashCode(buf)) * 31 + (int)len) * 31 + (int)origLen;
528     }
529 
530     /**
531      * Returns a clone of this {@code SerialBlob}. The copy will contain a
532      * reference to a clone of the internal byte array, not a reference
533      * to the original internal byte array of this {@code SerialBlob} object.
534      * The underlying {@code Blob} object will be set to null.
535      *
536      * @return  a clone of this SerialBlob
537      */
538     public Object clone() {
539         try {
540             SerialBlob sb = (SerialBlob) super.clone();
541             sb.buf = (buf != null) ? Arrays.copyOf(buf, (int)len) : null;
542             sb.blob = null;
543             return sb;
544         } catch (CloneNotSupportedException ex) {
545             // this shouldn't happen, since we are Cloneable
546             throw new InternalError();
547         }
548     }
549 
550     /**
551      * readObject is called to restore the state of the SerialBlob from
552      * a stream.
553      * @param s the {@code ObjectInputStream} to read from.
554      *
555      * @throws  ClassNotFoundException if the class of a serialized object
556      *          could not be found.
557      * @throws  IOException if an I/O error occurs.
558      */
559     private void readObject(ObjectInputStream s)
560             throws IOException, ClassNotFoundException {
561 
562         ObjectInputStream.GetField fields = s.readFields();
563         byte[] tmp = (byte[])fields.get("buf", null);
564         if (tmp == null)
565             throw new InvalidObjectException("buf is null and should not be!");
566         buf = tmp.clone();
567         len = fields.get("len", 0L);
568         if (buf.length != len)
569             throw new InvalidObjectException("buf is not the expected size");
570         origLen = fields.get("origLen", 0L);
571         blob = (Blob) fields.get("blob", null);
572     }
573 
574     /**
575      * writeObject is called to save the state of the SerialBlob
576      * to a stream.
577      * @param s the {@code ObjectOutputStream} to write to.
578      + @throws  IOException if I/O errors occur.
579      */
580     private void writeObject(ObjectOutputStream s)
581             throws IOException {
582 
583         ObjectOutputStream.PutField fields = s.putFields();
584         fields.put("buf", buf);
585         fields.put("len", len);
586         fields.put("origLen", origLen);
587         // Note: this check to see if it is an instance of Serializable
588         // is for backwards compatibility
589         fields.put("blob", blob instanceof Serializable ? blob : null);
590         s.writeFields();
591     }
592 
593     /**
594      * Check to see if this object had previously had its {@code free} method
595      * called
596      *
597      * @throws SerialException
598      */
599     private void isValid() throws SerialException {
600         if (buf == null) {
601             throw new SerialException("Error: You cannot call a method on a " +
602                     "SerialBlob instance once free() has been called.");
603         }
604     }
605 
606     /**
607      * The identifier that assists in the serialization of this
608      * {@code SerialBlob} object.
609      */
610     static final long serialVersionUID = -8144641928112860441L;
611 }