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
26package javax.sql.rowset.serial;
27
28import java.sql.*;
29import java.io.*;
30import java.lang.reflect.*;
31import 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 */
63public 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     */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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 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     */
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