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