001 /*
002 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.sql.rowset.serial;
027
028 import java.sql.*;
029 import java.io.*;
030 import java.lang.reflect.*;
031
032 /**
033 * A serialized mapping in the Java programming language of an SQL
034 * <code>BLOB</code> value.
035 * <P>
036 * The <code>SerialBlob</code> class provides a constructor for creating
037 * an instance from a <code>Blob</code> object. Note that the
038 * <code>Blob</code>
039 * object should have brought the SQL <code>BLOB</code> value's data over
040 * to the client before a <code>SerialBlob</code> object
041 * is constructed from it. The data of an SQL <code>BLOB</code> value can
042 * be materialized on the client as an array of bytes (using the method
043 * <code>Blob.getBytes</code>) or as a stream of uninterpreted bytes
044 * (using the method <code>Blob.getBinaryStream</code>).
045 * <P>
046 * <code>SerialBlob</code> methods make it possible to make a copy of a
047 * <code>SerialBlob</code> object as an array of bytes or as a stream.
048 * They also make it possible to locate a given pattern of bytes or a
049 * <code>Blob</code> object within a <code>SerialBlob</code> object
050 * and to update or truncate a <code>Blob</code> object.
051 *
052 * @author Jonathan Bruce
053 */
054 public class SerialBlob implements Blob, Serializable, Cloneable {
055
056 /**
057 * A serialized array of uninterpreted bytes representing the
058 * value of this <code>SerialBlob</code> object.
059 * @serial
060 */
061 private byte buf[];
062
063 /**
064 * The internal representation of the <code>Blob</code> object on which this
065 * <code>SerialBlob</code> object is based.
066 */
067 private Blob blob;
068
069 /**
070 * The number of bytes in this <code>SerialBlob</code> object's
071 * array of bytes.
072 * @serial
073 */
074 private long len;
075
076 /**
077 * The orginal number of bytes in this <code>SerialBlob</code> object's
078 * array of bytes when it was first established.
079 * @serial
080 */
081 private long origLen;
082
083 /**
084 * Constructs a <code>SerialBlob</code> object that is a serialized version of
085 * the given <code>byte</code> array.
086 * <p>
087 * The new <code>SerialBlob</code> object is initialized with the data from the
088 * <code>byte</code> array, thus allowing disconnected <code>RowSet</code>
089 * objects to establish serialized <code>Blob</code> objects without
090 * touching the data source.
091 *
092 * @param b the <code>byte</code> array containing the data for the
093 * <code>Blob</code> object to be serialized
094 * @throws SerialException if an error occurs during serialization
095 * @throws SQLException if a SQL errors occurs
096 */
097 public SerialBlob(byte[] b) throws SerialException, SQLException {
098
099 len = b.length;
100 buf = new byte[(int) len];
101 for (int i = 0; i < len; i++) {
102 buf[i] = b[i];
103 }
104 origLen = len;
105 }
106
107 /**
108 * Constructs a <code>SerialBlob</code> object that is a serialized
109 * version of the given <code>Blob</code> object.
110 * <P>
111 * The new <code>SerialBlob</code> object is initialized with the
112 * data from the <code>Blob</code> object; therefore, the
113 * <code>Blob</code> object should have previously brought the
114 * SQL <code>BLOB</code> value's data over to the client from
115 * the database. Otherwise, the new <code>SerialBlob</code> object
116 * will contain no data.
117 *
118 * @param blob the <code>Blob</code> object from which this
119 * <code>SerialBlob</code> object is to be constructed;
120 * cannot be null.
121 * @throws SerialException if an error occurs during serialization
122 * @throws SQLException if the <code>Blob</code> passed to this
123 * to this constructor is a <code>null</code>.
124 * @see java.sql.Blob
125 */
126 public SerialBlob(Blob blob) throws SerialException, SQLException {
127
128 if (blob == null) {
129 throw new SQLException("Cannot instantiate a SerialBlob "
130 + "object with a null Blob object");
131 }
132
133 len = blob.length();
134 buf = blob.getBytes(1, (int) len);
135 this .blob = blob;
136
137 //if ( len < 10240000)
138 // len = 10240000;
139 origLen = len;
140 }
141
142 /**
143 * Copies the specified number of bytes, starting at the given
144 * position, from this <code>SerialBlob</code> object to
145 * another array of bytes.
146 * <P>
147 * Note that if the given number of bytes to be copied is larger than
148 * the length of this <code>SerialBlob</code> object's array of
149 * bytes, the given number will be shortened to the array's length.
150 *
151 * @param pos the ordinal position of the first byte in this
152 * <code>SerialBlob</code> object to be copied;
153 * numbering starts at <code>1</code>; must not be less
154 * than <code>1</code> and must be less than or equal
155 * to the length of this <code>SerialBlob</code> object
156 * @param length the number of bytes to be copied
157 * @return an array of bytes that is a copy of a region of this
158 * <code>SerialBlob</code> object, starting at the given
159 * position and containing the given number of consecutive bytes
160 * @throws SerialException if the given starting position is out of bounds
161 */
162 public byte[] getBytes(long pos, int length) throws SerialException {
163 if (length > len) {
164 length = (int) len;
165 }
166
167 if (pos < 1 || length - pos < 0) {
168 throw new SerialException(
169 "Invalid arguments: position cannot be less that 1");
170 }
171
172 pos--; // correct pos to array index
173
174 byte[] b = new byte[length];
175
176 for (int i = 0; i < length; i++) {
177 b[i] = this .buf[(int) pos];
178 pos++;
179 }
180 return b;
181 }
182
183 /**
184 * Retrieves the number of bytes in this <code>SerialBlob</code>
185 * object's array of bytes.
186 *
187 * @return a <code>long</code> indicating the length in bytes of this
188 * <code>SerialBlob</code> object's array of bytes
189 * @throws SerialException if an error occurs
190 */
191 public long length() throws SerialException {
192 return len;
193 }
194
195 /**
196 * Returns this <code>SerialBlob</code> object as an input stream.
197 * Unlike the related method, <code>setBinaryStream</code>,
198 * a stream is produced regardless of whether the <code>SerialBlob</code>
199 * was created with a <code>Blob</code> object or a <code>byte</code> array.
200 *
201 * @return a <code>java.io.InputStream</code> object that contains
202 * this <code>SerialBlob</code> object's array of bytes
203 * @throws SerialException if an error occurs
204 * @see #setBinaryStream
205 */
206 public java.io.InputStream getBinaryStream() throws SerialException {
207 InputStream stream = new ByteArrayInputStream(buf);
208 return (java.io.InputStream) stream;
209 }
210
211 /**
212 * Returns the position in this <code>SerialBlob</code> object where
213 * the given pattern of bytes begins, starting the search at the
214 * specified position.
215 *
216 * @param pattern the pattern of bytes for which to search
217 * @param start the position of the byte in this
218 * <code>SerialBlob</code> object from which to begin
219 * the search; the first position is <code>1</code>;
220 * must not be less than <code>1</code> nor greater than
221 * the length of this <code>SerialBlob</code> object
222 * @return the position in this <code>SerialBlob</code> object
223 * where the given pattern begins, starting at the specified
224 * position; <code>-1</code> if the pattern is not found
225 * or the given starting position is out of bounds; position
226 * numbering for the return value starts at <code>1</code>
227 * @throws SerialException if an error occurs when serializing the blob
228 * @throws SQLException if there is an error accessing the <code>BLOB</code>
229 * value from the database
230 */
231 public long position(byte[] pattern, long start)
232 throws SerialException, SQLException {
233 if (start < 1 || start > len) {
234 return -1;
235 }
236
237 int pos = (int) start - 1; // internally Blobs are stored as arrays.
238 int i = 0;
239 long patlen = pattern.length;
240
241 while (pos < len) {
242 if (pattern[i] == buf[pos]) {
243 if (i + 1 == patlen) {
244 return (pos + 1) - (patlen - 1);
245 }
246 i++;
247 pos++; // increment pos, and i
248 } else if (pattern[i] != buf[pos]) {
249 pos++; // increment pos only
250 }
251 }
252 return -1; // not found
253 }
254
255 /**
256 * Returns the position in this <code>SerialBlob</code> object where
257 * the given <code>Blob</code> object begins, starting the search at the
258 * specified position.
259 *
260 * @param pattern the <code>Blob</code> object for which to search;
261 * @param start the position of the byte in this
262 * <code>SerialBlob</code> object from which to begin
263 * the search; the first position is <code>1</code>;
264 * must not be less than <code>1</code> nor greater than
265 * the length of this <code>SerialBlob</code> object
266 * @return the position in this <code>SerialBlob</code> object
267 * where the given <code>Blob</code> object begins, starting
268 * at the specified position; <code>-1</code> if the pattern is
269 * not found or the given starting position is out of bounds;
270 * position numbering for the return value starts at <code>1</code>
271 * @throws SerialException if an error occurs when serializing the blob
272 * @throws SQLException if there is an error accessing the <code>BLOB</code>
273 * value from the database
274 */
275 public long position(Blob pattern, long start)
276 throws SerialException, SQLException {
277 return position(pattern.getBytes(1, (int) (pattern.length())),
278 start);
279 }
280
281 /**
282 * Writes the given array of bytes to the <code>BLOB</code> value that
283 * this <code>Blob</code> object represents, starting at position
284 * <code>pos</code>, and returns the number of bytes written.
285 *
286 * @param pos the position in the SQL <code>BLOB</code> value at which
287 * to start writing. The first position is <code>1</code>;
288 * must not be less than <code>1</code> nor greater than
289 * the length of this <code>SerialBlob</code> object.
290 * @param bytes the array of bytes to be written to the <code>BLOB</code>
291 * value that this <code>Blob</code> object represents
292 * @return the number of bytes written
293 * @throws SerialException if there is an error accessing the
294 * <code>BLOB</code> value; or if an invalid position is set; if an
295 * invalid offset value is set
296 * @throws SQLException if there is an error accessing the <code>BLOB</code>
297 * value from the database
298 * @see #getBytes
299 */
300 public int setBytes(long pos, byte[] bytes) throws SerialException,
301 SQLException {
302 return (setBytes(pos, bytes, 0, bytes.length));
303 }
304
305 /**
306 * Writes all or part of the given <code>byte</code> array to the
307 * <code>BLOB</code> value that this <code>Blob</code> object represents
308 * and returns the number of bytes written.
309 * Writing starts at position <code>pos</code> in the <code>BLOB</code>
310 * value; <i>len</i> bytes from the given byte array are written.
311 *
312 * @param pos the position in the <code>BLOB</code> object at which
313 * to start writing. The first position is <code>1</code>;
314 * must not be less than <code>1</code> nor greater than
315 * the length of this <code>SerialBlob</code> object.
316 * @param bytes the array of bytes to be written to the <code>BLOB</code>
317 * value
318 * @param offset the offset in the <code>byte</code> array at which
319 * to start reading the bytes. The first offset position is
320 * <code>0</code>; must not be less than <code>0</code> nor greater
321 * than the length of the <code>byte</code> array
322 * @param length the number of bytes to be written to the
323 * <code>BLOB</code> value from the array of bytes <i>bytes</i>.
324 *
325 * @return the number of bytes written
326 * @throws SerialException if there is an error accessing the
327 * <code>BLOB</code> value; if an invalid position is set; if an
328 * invalid offset value is set; if number of bytes to be written
329 * is greater than the <code>SerialBlob</code> length; or the combined
330 * values of the length and offset is greater than the Blob buffer
331 * @throws SQLException if there is an error accessing the <code>BLOB</code>
332 * value from the database.
333 * @see #getBytes
334 */
335 public int setBytes(long pos, byte[] bytes, int offset, int length)
336 throws SerialException, SQLException {
337
338 if (offset < 0 || offset > bytes.length) {
339 throw new SerialException(
340 "Invalid offset in byte array set");
341 }
342
343 if (pos < 1 || pos > this .length()) {
344 throw new SerialException(
345 "Invalid position in BLOB object set");
346 }
347
348 if ((long) (length) > origLen) {
349 throw new SerialException(
350 "Buffer is not sufficient to hold the value");
351 }
352
353 if ((length + offset) > bytes.length) {
354 throw new SerialException(
355 "Invalid OffSet. Cannot have combined offset "
356 + "and length that is greater that the Blob buffer");
357 }
358
359 int i = 0;
360 pos--; // correct to array indexing
361 while (i < length || (offset + i + 1) < (bytes.length - offset)) {
362 this .buf[(int) pos + i] = bytes[offset + i];
363 i++;
364 }
365 return i;
366 }
367
368 /**
369 * Retrieves a stream that can be used to write to the <code>BLOB</code>
370 * value that this <code>Blob</code> object represents. The stream begins
371 * at position <code>pos</code>. This method forwards the
372 * <code>setBinaryStream()</code> call to the underlying <code>Blob</code> in
373 * the event that this <code>SerialBlob</code> object is instantiated with a
374 * <code>Blob</code>. If this <code>SerialBlob</code> is instantiated with
375 * a <code>byte</code> array, a <code>SerialException</code> is thrown.
376 *
377 * @param pos the position in the <code>BLOB</code> value at which
378 * to start writing
379 * @return a <code>java.io.OutputStream</code> object to which data can
380 * be written
381 * @throws SQLException if there is an error accessing the
382 * <code>BLOB</code> value
383 * @throws SerialException if the SerialBlob in not instantiated with a
384 * <code>Blob</code> object that supports <code>setBinaryStream()</code>
385 * @see #getBinaryStream
386 */
387 public java.io.OutputStream setBinaryStream(long pos)
388 throws SerialException, SQLException {
389 if (this .blob.setBinaryStream(pos) != null) {
390 return this .blob.setBinaryStream(pos);
391 } else {
392 throw new SerialException(
393 "Unsupported operation. SerialBlob cannot "
394 + "return a writable binary stream, unless instantiated with a Blob object "
395 + "that provides a setBinaryStream() implementation");
396 }
397 }
398
399 /**
400 * Truncates the <code>BLOB</code> value that this <code>Blob</code>
401 * object represents to be <code>len</code> bytes in length.
402 *
403 * @param length the length, in bytes, to which the <code>BLOB</code>
404 * value that this <code>Blob</code> object represents should be
405 * truncated
406 * @throws SerialException if there is an error accessing the Blob value;
407 * or the length to truncate is greater that the SerialBlob length
408 */
409 public void truncate(long length) throws SerialException {
410
411 if (length > len) {
412 throw new SerialException(
413 "Length more than what can be truncated");
414 } else if ((int) length == 0) {
415 buf = new byte[0];
416 len = length;
417 } else {
418 len = length;
419 buf = this .getBytes(1, (int) len);
420 }
421 }
422
423 /**
424 * Returns an <code>InputStream</code> object that contains a partial <code>Blob</code> value,
425 * starting with the byte specified by pos, which is length bytes in length.
426 *
427 * @param pos the offset to the first byte of the partial value to be retrieved.
428 * The first byte in the <code>Blob</code> is at position 1
429 * @param length the length in bytes of the partial value to be retrieved
430 * @return <code>InputStream</code> through which the partial <code>Blob</code> value can be read.
431 * @throws SQLException if pos is less than 1 or if pos is greater than the number of bytes
432 * in the <code>Blob</code> or if pos + length is greater than the number of bytes
433 * in the <code>Blob</code>
434 *
435 * @since 1.6
436 */
437 public InputStream getBinaryStream(long pos, long length)
438 throws SQLException {
439 throw new java.lang.UnsupportedOperationException(
440 "Not supported");
441 }
442
443 /**
444 * This method frees the <code>Blob</code> object and releases the resources that it holds.
445 * <code>Blob</code> object. The object is invalid once the <code>free</code>
446 * method is called. If <code>free</code> is called multiple times, the subsequent
447 * calls to <code>free</code> are treated as a no-op.
448 *
449 * @throws SQLException if an error occurs releasing
450 * the Blob's resources
451 * @since 1.6
452 */
453 public void free() throws SQLException {
454 throw new java.lang.UnsupportedOperationException(
455 "Not supported");
456 }
457
458 /**
459 * The identifier that assists in the serialization of this <code>SerialBlob</code>
460 * object.
461 */
462
463 static final long serialVersionUID = -8144641928112860441L;
464 }
|