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