1 /* 2 * Copyright (c) 2003, 2013, 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.util.Arrays; 31 32 /** 33 * A serialized mapping in the Java programming language of an SQL 34 * <code>CLOB</code> value. 35 * <P> 36 * The <code>SerialClob</code> class provides a constructor for creating 37 * an instance from a <code>Clob</code> object. Note that the <code>Clob</code> 38 * object should have brought the SQL <code>CLOB</code> value's data over 39 * to the client before a <code>SerialClob</code> object 40 * is constructed from it. The data of an SQL <code>CLOB</code> value can 41 * be materialized on the client as a stream of Unicode characters. 42 * <P> 43 * <code>SerialClob</code> methods make it possible to get a substring 44 * from a <code>SerialClob</code> object or to locate the start of 45 * a pattern of characters. 46 * 47 * <h3> Thread safety </h3> 48 * 49 * <p> A SerialClob is not safe for use by multiple concurrent threads. If a 50 * SerialClob is to be used by more than one thread then access to the SerialClob 51 * should be controlled by appropriate synchronization. 52 * @author Jonathan Bruce 53 */ 54 public class SerialClob implements Clob, Serializable, Cloneable { 55 56 /** 57 * A serialized array of characters containing the data of the SQL 58 * <code>CLOB</code> value that this <code>SerialClob</code> object 59 * represents. 60 * 61 * @serial 62 */ 63 private char buf[]; 64 65 /** 66 * Internal Clob representation if SerialClob is initialized with a 67 * Clob. Null if SerialClob is initialized with a char[]. 68 */ 69 private Clob clob; 70 71 /** 72 * The length in characters of this <code>SerialClob</code> object's 73 * internal array of characters. 74 * 75 * @serial 76 */ 77 private long len; 78 79 /** 80 * The original length in characters of this <code>SerialClob</code> 81 * object's internal array of characters. 82 * 83 * @serial 84 */ 85 private long origLen; 86 87 /** 88 * Constructs a <code>SerialClob</code> object that is a serialized version of 89 * the given <code>char</code> array. 90 * <p> 91 * The new <code>SerialClob</code> object is initialized with the data from the 92 * <code>char</code> array, thus allowing disconnected <code>RowSet</code> 93 * objects to establish a serialized <code>Clob</code> object without touching 94 * the data source. 95 * 96 * @param ch the char array representing the <code>Clob</code> object to be 97 * serialized 98 * @throws SerialException if an error occurs during serialization 99 * @throws SQLException if a SQL error occurs 100 */ 101 public SerialClob(char ch[]) throws SerialException, SQLException { 102 103 // %%% JMB. Agreed. Add code here to throw a SQLException if no 104 // support is available for locatorsUpdateCopy=false 105 // Serializing locators is not supported. 106 107 len = ch.length; 108 buf = new char[(int)len]; 109 for (int i = 0; i < len ; i++){ 110 buf[i] = ch[i]; 111 } 112 origLen = len; 113 clob = null; 114 } 115 116 /** 117 * Constructs a <code>SerialClob</code> object that is a serialized 118 * version of the given <code>Clob</code> object. 119 * <P> 120 * The new <code>SerialClob</code> object is initialized with the 121 * data from the <code>Clob</code> object; therefore, the 122 * <code>Clob</code> object should have previously brought the 123 * SQL <code>CLOB</code> value's data over to the client from 124 * the database. Otherwise, the new <code>SerialClob</code> object 125 * object will contain no data. 126 * <p> 127 * Note: The <code>Clob</code> object supplied to this constructor must 128 * return non-null for both the <code>Clob.getCharacterStream()</code> 129 * and <code>Clob.getAsciiStream</code> methods. This <code>SerialClob</code> 130 * constructor cannot serialize a <code>Clob</code> object in this instance 131 * and will throw an <code>SQLException</code> object. 132 * 133 * @param clob the <code>Clob</code> object from which this 134 * <code>SerialClob</code> object is to be constructed; cannot be null 135 * @throws SerialException if an error occurs during serialization 136 * @throws SQLException if a SQL error occurs in capturing the CLOB; 137 * if the <code>Clob</code> object is a null; or if either of the 138 * <code>Clob.getCharacterStream()</code> and <code>Clob.getAsciiStream()</code> 139 * methods on the <code>Clob</code> returns a null 140 * @see java.sql.Clob 141 */ 142 public SerialClob(Clob clob) throws SerialException, SQLException { 143 144 if (clob == null) { 145 throw new SQLException("Cannot instantiate a SerialClob " + 146 "object with a null Clob object"); 147 } 148 len = clob.length(); 149 this.clob = clob; 150 buf = new char[(int)len]; 151 int read = 0; 152 int offset = 0; 153 154 try (Reader charStream = clob.getCharacterStream()) { 155 if (charStream == null) { 156 throw new SQLException("Invalid Clob object. The call to getCharacterStream " + 157 "returned null which cannot be serialized."); 158 } 159 160 // Note: get an ASCII stream in order to null-check it, 161 // even though we don't do anything with it. 162 try (InputStream asciiStream = clob.getAsciiStream()) { 163 if (asciiStream == null) { 164 throw new SQLException("Invalid Clob object. The call to getAsciiStream " + 165 "returned null which cannot be serialized."); 166 } 167 } 168 169 try (Reader reader = new BufferedReader(charStream)) { 170 do { 171 read = reader.read(buf, offset, (int)(len - offset)); 172 offset += read; 173 } while (read > 0); 174 } 175 } catch (java.io.IOException ex) { 176 throw new SerialException("SerialClob: " + ex.getMessage()); 177 } 178 179 origLen = len; 180 } 181 182 /** 183 * Retrieves the number of characters in this <code>SerialClob</code> 184 * object's array of characters. 185 * 186 * @return a <code>long</code> indicating the length in characters of this 187 * <code>SerialClob</code> object's array of character 188 * @throws SerialException if an error occurs; 189 * if {@code free} had previously been called on this object 190 */ 191 public long length() throws SerialException { 192 isValid(); 193 return len; 194 } 195 196 /** 197 * Returns this <code>SerialClob</code> object's data as a stream 198 * of Unicode characters. Unlike the related method, <code>getAsciiStream</code>, 199 * a stream is produced regardless of whether the <code>SerialClob</code> object 200 * was created with a <code>Clob</code> object or a <code>char</code> array. 201 * 202 * @return a <code>java.io.Reader</code> object containing this 203 * <code>SerialClob</code> object's data 204 * @throws SerialException if an error occurs; 205 * if {@code free} had previously been called on this object 206 */ 207 public java.io.Reader getCharacterStream() throws SerialException { 208 isValid(); 209 return (java.io.Reader) new CharArrayReader(buf); 210 } 211 212 /** 213 * Retrieves the <code>CLOB</code> value designated by this <code>SerialClob</code> 214 * object as an ascii stream. This method forwards the <code>getAsciiStream</code> 215 * call to the underlying <code>Clob</code> object in the event that this 216 * <code>SerialClob</code> object is instantiated with a <code>Clob</code> 217 * object. If this <code>SerialClob</code> object is instantiated with 218 * a <code>char</code> array, a <code>SerialException</code> object is thrown. 219 * 220 * @return a <code>java.io.InputStream</code> object containing 221 * this <code>SerialClob</code> object's data 222 * @throws SerialException if this {@code SerialClob} object was not 223 * instantiated with a <code>Clob</code> object; 224 * if {@code free} had previously been called on this object 225 * @throws SQLException if there is an error accessing the 226 * <code>CLOB</code> value represented by the <code>Clob</code> object 227 * that was used to create this <code>SerialClob</code> object 228 */ 229 public java.io.InputStream getAsciiStream() throws SerialException, SQLException { 230 isValid(); 231 if (this.clob != null) { 232 return this.clob.getAsciiStream(); 233 } else { 234 throw new SerialException("Unsupported operation. SerialClob cannot " + 235 "return a the CLOB value as an ascii stream, unless instantiated " + 236 "with a fully implemented Clob object."); 237 } 238 } 239 240 /** 241 * Returns a copy of the substring contained in this 242 * <code>SerialClob</code> object, starting at the given position 243 * and continuing for the specified number or characters. 244 * 245 * @param pos the position of the first character in the substring 246 * to be copied; the first character of the 247 * <code>SerialClob</code> object is at position 248 * <code>1</code>; must not be less than <code>1</code>, 249 * and the sum of the starting position and the length 250 * of the substring must be less than the length of this 251 * <code>SerialClob</code> object 252 * @param length the number of characters in the substring to be 253 * returned; must not be greater than the length of 254 * this <code>SerialClob</code> object, and the 255 * sum of the starting position and the length 256 * of the substring must be less than the length of this 257 * <code>SerialClob</code> object 258 * @return a <code>String</code> object containing a substring of 259 * this <code>SerialClob</code> object beginning at the 260 * given position and containing the specified number of 261 * consecutive characters 262 * @throws SerialException if either of the arguments is out of bounds; 263 * if {@code free} had previously been called on this object 264 */ 265 public String getSubString(long pos, int length) throws SerialException { 266 267 isValid(); 268 if (pos < 1 || pos > this.length()) { 269 throw new SerialException("Invalid position in SerialClob object set"); 270 } 271 272 if ((pos-1) + length > this.length()) { 273 throw new SerialException("Invalid position and substring length"); 274 } 275 276 try { 277 return new String(buf, (int)pos - 1, length); 278 279 } catch (StringIndexOutOfBoundsException e) { 280 throw new SerialException("StringIndexOutOfBoundsException: " + 281 e.getMessage()); 282 } 283 284 } 285 286 /** 287 * Returns the position in this <code>SerialClob</code> object 288 * where the given <code>String</code> object begins, starting 289 * the search at the specified position. This method returns 290 * <code>-1</code> if the pattern is not found. 291 * 292 * @param searchStr the <code>String</code> object for which to 293 * search 294 * @param start the position in this <code>SerialClob</code> object 295 * at which to start the search; the first position is 296 * <code>1</code>; must not be less than <code>1</code> nor 297 * greater than the length of this <code>SerialClob</code> object 298 * @return the position at which the given <code>String</code> object 299 * begins, starting the search at the specified position; 300 * <code>-1</code> if the given <code>String</code> object is 301 * not found or the starting position is out of bounds; position 302 * numbering for the return value starts at <code>1</code> 303 * @throws SerialException if the {@code free} method had been 304 * previously called on this object 305 * @throws SQLException if there is an error accessing the Clob value 306 * from the database. 307 */ 308 public long position(String searchStr, long start) 309 throws SerialException, SQLException { 310 isValid(); 311 if (start < 1 || start > len) { 312 return -1; 313 } 314 315 char pattern[] = searchStr.toCharArray(); 316 317 int pos = (int)start-1; 318 int i = 0; 319 long patlen = pattern.length; 320 321 while (pos < len) { 322 if (pattern[i] == buf[pos]) { 323 if (i + 1 == patlen) { 324 return (pos + 1) - (patlen - 1); 325 } 326 i++; pos++; // increment pos, and i 327 328 } else if (pattern[i] != buf[pos]) { 329 pos++; // increment pos only 330 } 331 } 332 return -1; // not found 333 } 334 335 /** 336 * Returns the position in this <code>SerialClob</code> object 337 * where the given <code>Clob</code> signature begins, starting 338 * the search at the specified position. This method returns 339 * <code>-1</code> if the pattern is not found. 340 * 341 * @param searchStr the <code>Clob</code> object for which to search 342 * @param start the position in this <code>SerialClob</code> object 343 * at which to begin the search; the first position is 344 * <code>1</code>; must not be less than <code>1</code> nor 345 * greater than the length of this <code>SerialClob</code> object 346 * @return the position at which the given <code>Clob</code> 347 * object begins in this <code>SerialClob</code> object, 348 * at or after the specified starting position 349 * @throws SerialException if an error occurs locating the Clob signature; 350 * if the {@code free} method had been previously called on this object 351 * @throws SQLException if there is an error accessing the Clob value 352 * from the database 353 */ 354 public long position(Clob searchStr, long start) 355 throws SerialException, SQLException { 356 isValid(); 357 return position(searchStr.getSubString(1,(int)searchStr.length()), start); 358 } 359 360 /** 361 * Writes the given Java <code>String</code> to the <code>CLOB</code> 362 * value that this <code>SerialClob</code> object represents, at the position 363 * <code>pos</code>. 364 * 365 * @param pos the position at which to start writing to the <code>CLOB</code> 366 * value that this <code>SerialClob</code> object represents; the first 367 * position is <code>1</code>; must not be less than <code>1</code> nor 368 * greater than the length of this <code>SerialClob</code> object 369 * @param str the string to be written to the <code>CLOB</code> 370 * value that this <code>SerialClob</code> object represents 371 * @return the number of characters written 372 * @throws SerialException if there is an error accessing the 373 * <code>CLOB</code> value; if an invalid position is set; if an 374 * invalid offset value is set; if number of bytes to be written 375 * is greater than the <code>SerialClob</code> length; or the combined 376 * values of the length and offset is greater than the Clob buffer; 377 * if the {@code free} method had been previously called on this object 378 */ 379 public int setString(long pos, String str) throws SerialException { 380 return (setString(pos, str, 0, str.length())); 381 } 382 383 /** 384 * Writes <code>len</code> characters of <code>str</code>, starting 385 * at character <code>offset</code>, to the <code>CLOB</code> value 386 * that this <code>Clob</code> represents. 387 * 388 * @param pos the position at which to start writing to the <code>CLOB</code> 389 * value that this <code>SerialClob</code> object represents; the first 390 * position is <code>1</code>; must not be less than <code>1</code> nor 391 * greater than the length of this <code>SerialClob</code> object 392 * @param str the string to be written to the <code>CLOB</code> 393 * value that this <code>Clob</code> object represents 394 * @param offset the offset into <code>str</code> to start reading 395 * the characters to be written 396 * @param length the number of characters to be written 397 * @return the number of characters written 398 * @throws SerialException if there is an error accessing the 399 * <code>CLOB</code> value; if an invalid position is set; if an 400 * invalid offset value is set; if number of bytes to be written 401 * is greater than the <code>SerialClob</code> length; or the combined 402 * values of the length and offset is greater than the Clob buffer; 403 * if the {@code free} method had been previously called on this object 404 */ 405 public int setString(long pos, String str, int offset, int length) 406 throws SerialException { 407 isValid(); 408 String temp = str.substring(offset); 409 char cPattern[] = temp.toCharArray(); 410 411 if (offset < 0 || offset > str.length()) { 412 throw new SerialException("Invalid offset in byte array set"); 413 } 414 415 if (pos < 1 || pos > this.length()) { 416 throw new SerialException("Invalid position in Clob object set"); 417 } 418 419 if ((long)(length) > origLen) { 420 throw new SerialException("Buffer is not sufficient to hold the value"); 421 } 422 423 if ((length + offset) > str.length()) { 424 // need check to ensure length + offset !> bytes.length 425 throw new SerialException("Invalid OffSet. Cannot have combined offset " + 426 " and length that is greater that the Blob buffer"); 427 } 428 429 int i = 0; 430 pos--; //values in the array are at position one less 431 while ( i < length || (offset + i +1) < (str.length() - offset ) ) { 432 this.buf[(int)pos + i ] = cPattern[offset + i ]; 433 i++; 434 } 435 return i; 436 } 437 438 /** 439 * Retrieves a stream to be used to write Ascii characters to the 440 * <code>CLOB</code> value that this <code>SerialClob</code> object represents, 441 * starting at position <code>pos</code>. This method forwards the 442 * <code>setAsciiStream()</code> call to the underlying <code>Clob</code> object in 443 * the event that this <code>SerialClob</code> object is instantiated with a 444 * <code>Clob</code> object. If this <code>SerialClob</code> object is instantiated 445 * with a <code>char</code> array, a <code>SerialException</code> object is thrown. 446 * 447 * @param pos the position at which to start writing to the 448 * <code>CLOB</code> object 449 * @return the stream to which ASCII encoded characters can be written 450 * @throws SerialException if SerialClob is not instantiated with a 451 * Clob object; 452 * if the {@code free} method had been previously called on this object 453 * @throws SQLException if there is an error accessing the 454 * <code>CLOB</code> value 455 * @see #getAsciiStream 456 */ 457 public java.io.OutputStream setAsciiStream(long pos) 458 throws SerialException, SQLException { 459 isValid(); 460 if (this.clob != null) { 461 return this.clob.setAsciiStream(pos); 462 } else { 463 throw new SerialException("Unsupported operation. SerialClob cannot " + 464 "return a writable ascii stream\n unless instantiated with a Clob object " + 465 "that has a setAsciiStream() implementation"); 466 } 467 } 468 469 /** 470 * Retrieves a stream to be used to write a stream of Unicode characters 471 * to the <code>CLOB</code> value that this <code>SerialClob</code> object 472 * represents, at position <code>pos</code>. This method forwards the 473 * <code>setCharacterStream()</code> call to the underlying <code>Clob</code> 474 * object in the event that this <code>SerialClob</code> object is instantiated with a 475 * <code>Clob</code> object. If this <code>SerialClob</code> object is instantiated with 476 * a <code>char</code> array, a <code>SerialException</code> is thrown. 477 * 478 * @param pos the position at which to start writing to the 479 * <code>CLOB</code> value 480 * 481 * @return a stream to which Unicode encoded characters can be written 482 * @throws SerialException if the SerialClob is not instantiated with 483 * a Clob object; 484 * if the {@code free} method had been previously called on this object 485 * @throws SQLException if there is an error accessing the 486 * <code>CLOB</code> value 487 * @see #getCharacterStream 488 */ 489 public java.io.Writer setCharacterStream(long pos) 490 throws SerialException, SQLException { 491 isValid(); 492 if (this.clob != null) { 493 return this.clob.setCharacterStream(pos); 494 } else { 495 throw new SerialException("Unsupported operation. SerialClob cannot " + 496 "return a writable character stream\n unless instantiated with a Clob object " + 497 "that has a setCharacterStream implementation"); 498 } 499 } 500 501 /** 502 * Truncates the <code>CLOB</code> value that this <code>SerialClob</code> 503 * object represents so that it has a length of <code>len</code> 504 * characters. 505 * <p> 506 * Truncating a <code>SerialClob</code> object to length 0 has the effect of 507 * clearing its contents. 508 * 509 * @param length the length, in bytes, to which the <code>CLOB</code> 510 * value should be truncated 511 * @throws SerialException if there is an error accessing the 512 * <code>CLOB</code> value; 513 * if the {@code free} method had been previously called on this object 514 */ 515 public void truncate(long length) throws SerialException { 516 isValid(); 517 if (length > len) { 518 throw new SerialException 519 ("Length more than what can be truncated"); 520 } else { 521 len = length; 522 // re-size the buffer 523 524 if (len == 0) { 525 buf = new char[] {}; 526 } else { 527 buf = (this.getSubString(1, (int)len)).toCharArray(); 528 } 529 } 530 } 531 532 533 /** 534 * Returns a {@code Reader} object that contains a partial 535 * {@code SerialClob} value, starting 536 * with the character specified by pos, which is length characters in length. 537 * 538 * @param pos the offset to the first character of the partial value to 539 * be retrieved. The first character in the {@code SerialClob} is at position 1. 540 * @param length the length in characters of the partial value to be retrieved. 541 * @return {@code Reader} through which the partial {@code SerialClob} 542 * value can be read. 543 * @throws SQLException if pos is less than 1 or if pos is greater than the 544 * number of characters in the {@code SerialClob} or if pos + length 545 * is greater than the number of characters in the {@code SerialClob}; 546 * @throws SerialException if the {@code free} method had been previously 547 * called on this object 548 * @since 1.6 549 */ 550 public Reader getCharacterStream(long pos, long length) throws SQLException { 551 isValid(); 552 if (pos < 1 || pos > len) { 553 throw new SerialException("Invalid position in Clob object set"); 554 } 555 556 if ((pos-1) + length > len) { 557 throw new SerialException("Invalid position and substring length"); 558 } 559 if (length <= 0) { 560 throw new SerialException("Invalid length specified"); 561 } 562 return new CharArrayReader(buf, (int)pos, (int)length); 563 } 564 565 /** 566 * This method frees the {@code SeriableClob} object and releases the 567 * resources that it holds. 568 * The object is invalid once the {@code free} method is called. 569 * <p> 570 * If {@code free} is called multiple times, the subsequent 571 * calls to {@code free} are treated as a no-op. 572 * </P> 573 * @throws SQLException if an error occurs releasing 574 * the Clob's resources 575 * @since 1.6 576 */ 577 public void free() throws SQLException { 578 if (buf != null) { 579 buf = null; 580 if (clob != null) { 581 clob.free(); 582 } 583 clob = null; 584 } 585 } 586 587 /** 588 * Compares this SerialClob to the specified object. The result is {@code 589 * true} if and only if the argument is not {@code null} and is a {@code 590 * SerialClob} object that represents the same sequence of characters as this 591 * object. 592 * 593 * @param obj The object to compare this {@code SerialClob} against 594 * 595 * @return {@code true} if the given object represents a {@code SerialClob} 596 * equivalent to this SerialClob, {@code false} otherwise 597 * 598 */ 599 public boolean equals(Object obj) { 600 if (this == obj) { 601 return true; 602 } 603 if (obj instanceof SerialClob) { 604 SerialClob sc = (SerialClob)obj; 605 if (this.len == sc.len) { 606 return Arrays.equals(buf, sc.buf); 607 } 608 } 609 return false; 610 } 611 612 /** 613 * Returns a hash code for this {@code SerialClob}. 614 * @return a hash code value for this object. 615 */ 616 public int hashCode() { 617 return ((31 + Arrays.hashCode(buf)) * 31 + (int)len) * 31 + (int)origLen; 618 } 619 620 /** 621 * Returns a clone of this {@code SerialClob}. The copy will contain a 622 * reference to a clone of the internal character array, not a reference 623 * to the original internal character array of this {@code SerialClob} object. 624 * The underlying {@code Clob} object will be set to null. 625 * 626 * @return a clone of this SerialClob 627 */ 628 public Object clone() { 629 try { 630 SerialClob sc = (SerialClob) super.clone(); 631 sc.buf = (buf != null) ? Arrays.copyOf(buf, (int)len) : null; 632 sc.clob = null; 633 return sc; 634 } catch (CloneNotSupportedException ex) { 635 // this shouldn't happen, since we are Cloneable 636 throw new InternalError(); 637 } 638 } 639 640 /** 641 * readObject is called to restore the state of the SerialClob from 642 * a stream. 643 */ 644 private void readObject(ObjectInputStream s) 645 throws IOException, ClassNotFoundException { 646 647 ObjectInputStream.GetField fields = s.readFields(); 648 char[] tmp = (char[])fields.get("buf", null); 649 if (tmp == null) 650 throw new InvalidObjectException("buf is null and should not be!"); 651 buf = tmp.clone(); 652 len = fields.get("len", 0L); 653 if (buf.length != len) 654 throw new InvalidObjectException("buf is not the expected size"); 655 origLen = fields.get("origLen", 0L); 656 clob = (Clob) fields.get("clob", null); 657 } 658 659 /** 660 * writeObject is called to save the state of the SerialClob 661 * to a stream. 662 */ 663 private void writeObject(ObjectOutputStream s) 664 throws IOException, ClassNotFoundException { 665 666 ObjectOutputStream.PutField fields = s.putFields(); 667 fields.put("buf", buf); 668 fields.put("len", len); 669 fields.put("origLen", origLen); 670 // Note: this check to see if it is an instance of Serializable 671 // is for backwards compatibiity 672 fields.put("clob", clob instanceof Serializable ? clob : null); 673 s.writeFields(); 674 } 675 676 /** 677 * Check to see if this object had previously had its {@code free} method 678 * called 679 * 680 * @throws SerialException 681 */ 682 private void isValid() throws SerialException { 683 if (buf == null) { 684 throw new SerialException("Error: You cannot call a method on a " 685 + "SerialClob instance once free() has been called."); 686 } 687 } 688 689 /** 690 * The identifier that assists in the serialization of this {@code SerialClob} 691 * object. 692 */ 693 static final long serialVersionUID = -1662519690087375313L; 694 }