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