Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/sql/rowset/serial/SerialBlob.java
38918 views
/*1* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.sql.rowset.serial;2627import java.sql.*;28import java.io.*;29import java.lang.reflect.*;30import java.util.Arrays;313233/**34* A serialized mapping in the Java programming language of an SQL35* <code>BLOB</code> value.36* <P>37* The <code>SerialBlob</code> class provides a constructor for creating38* an instance from a <code>Blob</code> object. Note that the39* <code>Blob</code>40* object should have brought the SQL <code>BLOB</code> value's data over41* to the client before a <code>SerialBlob</code> object42* is constructed from it. The data of an SQL <code>BLOB</code> value can43* be materialized on the client as an array of bytes (using the method44* <code>Blob.getBytes</code>) or as a stream of uninterpreted bytes45* (using the method <code>Blob.getBinaryStream</code>).46* <P>47* <code>SerialBlob</code> methods make it possible to make a copy of a48* <code>SerialBlob</code> object as an array of bytes or as a stream.49* They also make it possible to locate a given pattern of bytes or a50* <code>Blob</code> object within a <code>SerialBlob</code> object51* and to update or truncate a <code>Blob</code> object.52*53* <h3> Thread safety </h3>54*55* <p> A SerialBlob is not safe for use by multiple concurrent threads. If a56* SerialBlob is to be used by more than one thread then access to the SerialBlob57* should be controlled by appropriate synchronization.58*59* @author Jonathan Bruce60*/61public class SerialBlob implements Blob, Serializable, Cloneable {6263/**64* A serialized array of uninterpreted bytes representing the65* value of this <code>SerialBlob</code> object.66* @serial67*/68private byte[] buf;6970/**71* The internal representation of the <code>Blob</code> object on which this72* <code>SerialBlob</code> object is based.73*/74private Blob blob;7576/**77* The number of bytes in this <code>SerialBlob</code> object's78* array of bytes.79* @serial80*/81private long len;8283/**84* The original number of bytes in this <code>SerialBlob</code> object's85* array of bytes when it was first established.86* @serial87*/88private long origLen;8990/**91* Constructs a <code>SerialBlob</code> object that is a serialized version of92* the given <code>byte</code> array.93* <p>94* The new <code>SerialBlob</code> object is initialized with the data from the95* <code>byte</code> array, thus allowing disconnected <code>RowSet</code>96* objects to establish serialized <code>Blob</code> objects without97* touching the data source.98*99* @param b the <code>byte</code> array containing the data for the100* <code>Blob</code> object to be serialized101* @throws SerialException if an error occurs during serialization102* @throws SQLException if a SQL errors occurs103*/104public SerialBlob(byte[] b)105throws SerialException, SQLException {106107len = b.length;108buf = new byte[(int)len];109for(int i = 0; i < len; i++) {110buf[i] = b[i];111}112origLen = len;113}114115116/**117* Constructs a <code>SerialBlob</code> object that is a serialized118* version of the given <code>Blob</code> object.119* <P>120* The new <code>SerialBlob</code> object is initialized with the121* data from the <code>Blob</code> object; therefore, the122* <code>Blob</code> object should have previously brought the123* SQL <code>BLOB</code> value's data over to the client from124* the database. Otherwise, the new <code>SerialBlob</code> object125* will contain no data.126*127* @param blob the <code>Blob</code> object from which this128* <code>SerialBlob</code> object is to be constructed;129* cannot be null.130* @throws SerialException if an error occurs during serialization131* @throws SQLException if the <code>Blob</code> passed to this132* to this constructor is a <code>null</code>.133* @see java.sql.Blob134*/135public SerialBlob (Blob blob)136throws SerialException, SQLException {137138if (blob == null) {139throw new SQLException(140"Cannot instantiate a SerialBlob object with a null Blob object");141}142143len = blob.length();144buf = blob.getBytes(1, (int)len );145this.blob = blob;146origLen = len;147}148149/**150* Copies the specified number of bytes, starting at the given151* position, from this <code>SerialBlob</code> object to152* another array of bytes.153* <P>154* Note that if the given number of bytes to be copied is larger than155* the length of this <code>SerialBlob</code> object's array of156* bytes, the given number will be shortened to the array's length.157*158* @param pos the ordinal position of the first byte in this159* <code>SerialBlob</code> object to be copied;160* numbering starts at <code>1</code>; must not be less161* than <code>1</code> and must be less than or equal162* to the length of this <code>SerialBlob</code> object163* @param length the number of bytes to be copied164* @return an array of bytes that is a copy of a region of this165* <code>SerialBlob</code> object, starting at the given166* position and containing the given number of consecutive bytes167* @throws SerialException if the given starting position is out of bounds;168* if {@code free} had previously been called on this object169*/170public byte[] getBytes(long pos, int length) throws SerialException {171isValid();172if (length > len) {173length = (int)len;174}175176if (pos < 1 || len - pos < 0 ) {177throw new SerialException("Invalid arguments: position cannot be "178+ "less than 1 or greater than the length of the SerialBlob");179}180181pos--; // correct pos to array index182183byte[] b = new byte[length];184185for (int i = 0; i < length; i++) {186b[i] = this.buf[(int)pos];187pos++;188}189return b;190}191192/**193* Retrieves the number of bytes in this <code>SerialBlob</code>194* object's array of bytes.195*196* @return a <code>long</code> indicating the length in bytes of this197* <code>SerialBlob</code> object's array of bytes198* @throws SerialException if an error occurs;199* if {@code free} had previously been called on this object200*/201public long length() throws SerialException {202isValid();203return len;204}205206/**207* Returns this <code>SerialBlob</code> object as an input stream.208* Unlike the related method, <code>setBinaryStream</code>,209* a stream is produced regardless of whether the <code>SerialBlob</code>210* was created with a <code>Blob</code> object or a <code>byte</code> array.211*212* @return a <code>java.io.InputStream</code> object that contains213* this <code>SerialBlob</code> object's array of bytes214* @throws SerialException if an error occurs;215* if {@code free} had previously been called on this object216* @see #setBinaryStream217*/218public java.io.InputStream getBinaryStream() throws SerialException {219isValid();220InputStream stream = new ByteArrayInputStream(buf);221return stream;222}223224/**225* Returns the position in this <code>SerialBlob</code> object where226* the given pattern of bytes begins, starting the search at the227* specified position.228*229* @param pattern the pattern of bytes for which to search230* @param start the position of the byte in this231* <code>SerialBlob</code> object from which to begin232* the search; the first position is <code>1</code>;233* must not be less than <code>1</code> nor greater than234* the length of this <code>SerialBlob</code> object235* @return the position in this <code>SerialBlob</code> object236* where the given pattern begins, starting at the specified237* position; <code>-1</code> if the pattern is not found238* or the given starting position is out of bounds; position239* numbering for the return value starts at <code>1</code>240* @throws SerialException if an error occurs when serializing the blob;241* if {@code free} had previously been called on this object242* @throws SQLException if there is an error accessing the <code>BLOB</code>243* value from the database244*/245public long position(byte[] pattern, long start)246throws SerialException, SQLException {247248isValid();249if (start < 1 || start > len) {250return -1;251}252253int pos = (int)start-1; // internally Blobs are stored as arrays.254int i = 0;255long patlen = pattern.length;256257while (pos < len) {258if (pattern[i] == buf[pos]) {259if (i + 1 == patlen) {260return (pos + 1) - (patlen - 1);261}262i++; pos++; // increment pos, and i263} else if (pattern[i] != buf[pos]) {264pos++; // increment pos only265}266}267return -1; // not found268}269270/**271* Returns the position in this <code>SerialBlob</code> object where272* the given <code>Blob</code> object begins, starting the search at the273* specified position.274*275* @param pattern the <code>Blob</code> object for which to search;276* @param start the position of the byte in this277* <code>SerialBlob</code> object from which to begin278* the search; the first position is <code>1</code>;279* must not be less than <code>1</code> nor greater than280* the length of this <code>SerialBlob</code> object281* @return the position in this <code>SerialBlob</code> object282* where the given <code>Blob</code> object begins, starting283* at the specified position; <code>-1</code> if the pattern is284* not found or the given starting position is out of bounds;285* position numbering for the return value starts at <code>1</code>286* @throws SerialException if an error occurs when serializing the blob;287* if {@code free} had previously been called on this object288* @throws SQLException if there is an error accessing the <code>BLOB</code>289* value from the database290*/291public long position(Blob pattern, long start)292throws SerialException, SQLException {293isValid();294return position(pattern.getBytes(1, (int)(pattern.length())), start);295}296297/**298* Writes the given array of bytes to the <code>BLOB</code> value that299* this <code>Blob</code> object represents, starting at position300* <code>pos</code>, and returns the number of bytes written.301*302* @param pos the position in the SQL <code>BLOB</code> value at which303* to start writing. The first position is <code>1</code>;304* must not be less than <code>1</code> nor greater than305* the length of this <code>SerialBlob</code> object.306* @param bytes the array of bytes to be written to the <code>BLOB</code>307* value that this <code>Blob</code> object represents308* @return the number of bytes written309* @throws SerialException if there is an error accessing the310* <code>BLOB</code> value; or if an invalid position is set; if an311* invalid offset value is set;312* if {@code free} had previously been called on this object313* @throws SQLException if there is an error accessing the <code>BLOB</code>314* value from the database315* @see #getBytes316*/317public int setBytes(long pos, byte[] bytes)318throws SerialException, SQLException {319return setBytes(pos, bytes, 0, bytes.length);320}321322/**323* Writes all or part of the given <code>byte</code> array to the324* <code>BLOB</code> value that this <code>Blob</code> object represents325* and returns the number of bytes written.326* Writing starts at position <code>pos</code> in the <code>BLOB</code>327* value; <i>len</i> bytes from the given byte array are written.328*329* @param pos the position in the <code>BLOB</code> object at which330* to start writing. The first position is <code>1</code>;331* must not be less than <code>1</code> nor greater than332* the length of this <code>SerialBlob</code> object.333* @param bytes the array of bytes to be written to the <code>BLOB</code>334* value335* @param offset the offset in the <code>byte</code> array at which336* to start reading the bytes. The first offset position is337* <code>0</code>; must not be less than <code>0</code> nor greater338* than the length of the <code>byte</code> array339* @param length the number of bytes to be written to the340* <code>BLOB</code> value from the array of bytes <i>bytes</i>.341*342* @return the number of bytes written343* @throws SerialException if there is an error accessing the344* <code>BLOB</code> value; if an invalid position is set; if an345* invalid offset value is set; if number of bytes to be written346* is greater than the <code>SerialBlob</code> length; or the combined347* values of the length and offset is greater than the Blob buffer;348* if {@code free} had previously been called on this object349* @throws SQLException if there is an error accessing the <code>BLOB</code>350* value from the database.351* @see #getBytes352*/353public int setBytes(long pos, byte[] bytes, int offset, int length)354throws SerialException, SQLException {355356isValid();357if (offset < 0 || offset > bytes.length) {358throw new SerialException("Invalid offset in byte array set");359}360361if (pos < 1 || pos > this.length()) {362throw new SerialException("Invalid position in BLOB object set");363}364365if ((long)(length) > origLen) {366throw new SerialException("Buffer is not sufficient to hold the value");367}368369if ((length + offset) > bytes.length) {370throw new SerialException("Invalid OffSet. Cannot have combined offset " +371"and length that is greater that the Blob buffer");372}373374int i = 0;375pos--; // correct to array indexing376while ( i < length || (offset + i +1) < (bytes.length-offset) ) {377this.buf[(int)pos + i] = bytes[offset + i ];378i++;379}380return i;381}382383/**384* Retrieves a stream that can be used to write to the <code>BLOB</code>385* value that this <code>Blob</code> object represents. The stream begins386* at position <code>pos</code>. This method forwards the387* <code>setBinaryStream()</code> call to the underlying <code>Blob</code> in388* the event that this <code>SerialBlob</code> object is instantiated with a389* <code>Blob</code>. If this <code>SerialBlob</code> is instantiated with390* a <code>byte</code> array, a <code>SerialException</code> is thrown.391*392* @param pos the position in the <code>BLOB</code> value at which393* to start writing394* @return a <code>java.io.OutputStream</code> object to which data can395* be written396* @throws SQLException if there is an error accessing the397* <code>BLOB</code> value398* @throws SerialException if the SerialBlob in not instantiated with a399* <code>Blob</code> object that supports <code>setBinaryStream()</code>;400* if {@code free} had previously been called on this object401* @see #getBinaryStream402*/403public java.io.OutputStream setBinaryStream(long pos)404throws SerialException, SQLException {405406isValid();407if (this.blob != null) {408return this.blob.setBinaryStream(pos);409} else {410throw 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}415416/**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 be422* truncated423* @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 object426*/427public void truncate(long length) throws SerialException {428isValid();429if (length > len) {430throw new SerialException(431"Length more than what can be truncated");432} else if((int)length == 0) {433buf = new byte[0];434len = length;435} else {436len = length;437buf = this.getBytes(1, (int)len);438}439}440441442/**443* Returns an444* <code>InputStream</code> object that contains a partial445* {@code Blob} value, starting with the byte specified by pos, which is446* length bytes in length.447*448* @param pos the offset to the first byte of the partial value to be449* retrieved. The first byte in the {@code Blob} is at position 1450* @param length the length in bytes of the partial value to be retrieved451* @return452* <code>InputStream</code> through which the partial {@code Blob} value can453* be read.454* @throws SQLException if pos is less than 1 or if pos is greater than the455* number of bytes in the {@code Blob} or if pos + length is greater than456* the number of bytes in the {@code Blob}457* @throws SerialException if the {@code free} method had been previously458* called on this object459*460* @since 1.6461*/462public InputStream getBinaryStream(long pos, long length) throws SQLException {463isValid();464if (pos < 1 || pos > this.length()) {465throw new SerialException("Invalid position in BLOB object set");466}467if (length < 1 || length > len - pos + 1) {468throw new SerialException(469"length is < 1 or pos + length > total number of bytes");470}471return new ByteArrayInputStream(buf, (int) pos - 1, (int) length);472}473474475/**476* This method frees the {@code SeriableBlob} object and releases the477* resources that it holds. The object is invalid once the {@code free}478* method is called. <p> If {@code free} is called multiple times, the479* subsequent calls to {@code free} are treated as a no-op. </P>480*481* @throws SQLException if an error occurs releasing the Blob's resources482* @since 1.6483*/484public void free() throws SQLException {485if (buf != null) {486buf = null;487if (blob != null) {488blob.free();489}490blob = null;491}492}493494/**495* Compares this SerialBlob to the specified object. The result is {@code496* true} if and only if the argument is not {@code null} and is a {@code497* SerialBlob} object that represents the same sequence of bytes as this498* object.499*500* @param obj The object to compare this {@code SerialBlob} against501*502* @return {@code true} if the given object represents a {@code SerialBlob}503* equivalent to this SerialBlob, {@code false} otherwise504*505*/506public boolean equals(Object obj) {507if (this == obj) {508return true;509}510if (obj instanceof SerialBlob) {511SerialBlob sb = (SerialBlob)obj;512if (this.len == sb.len) {513return Arrays.equals(buf, sb.buf);514}515}516return false;517}518519/**520* Returns a hash code for this {@code SerialBlob}.521* @return a hash code value for this object.522*/523public int hashCode() {524return ((31 + Arrays.hashCode(buf)) * 31 + (int)len) * 31 + (int)origLen;525}526527/**528* Returns a clone of this {@code SerialBlob}. The copy will contain a529* reference to a clone of the internal byte array, not a reference530* to the original internal byte array of this {@code SerialBlob} object.531* The underlying {@code Blob} object will be set to null.532*533* @return a clone of this SerialBlob534*/535public Object clone() {536try {537SerialBlob sb = (SerialBlob) super.clone();538sb.buf = (buf != null) ? Arrays.copyOf(buf, (int)len) : null;539sb.blob = null;540return sb;541} catch (CloneNotSupportedException ex) {542// this shouldn't happen, since we are Cloneable543throw new InternalError();544}545}546547/**548* readObject is called to restore the state of the SerialBlob from549* a stream.550*/551private void readObject(ObjectInputStream s)552throws IOException, ClassNotFoundException {553554ObjectInputStream.GetField fields = s.readFields();555byte[] tmp = (byte[])fields.get("buf", null);556if (tmp == null)557throw new InvalidObjectException("buf is null and should not be!");558buf = tmp.clone();559len = fields.get("len", 0L);560if (buf.length != len)561throw new InvalidObjectException("buf is not the expected size");562origLen = fields.get("origLen", 0L);563blob = (Blob) fields.get("blob", null);564}565566/**567* writeObject is called to save the state of the SerialBlob568* to a stream.569*/570private void writeObject(ObjectOutputStream s)571throws IOException, ClassNotFoundException {572573ObjectOutputStream.PutField fields = s.putFields();574fields.put("buf", buf);575fields.put("len", len);576fields.put("origLen", origLen);577// Note: this check to see if it is an instance of Serializable578// is for backwards compatibiity579fields.put("blob", blob instanceof Serializable ? blob : null);580s.writeFields();581}582583/**584* Check to see if this object had previously had its {@code free} method585* called586*587* @throws SerialException588*/589private void isValid() throws SerialException {590if (buf == null) {591throw new SerialException("Error: You cannot call a method on a " +592"SerialBlob instance once free() has been called.");593}594}595596/**597* The identifier that assists in the serialization of this598* {@code SerialBlob} object.599*/600static final long serialVersionUID = -8144641928112860441L;601}602603604