KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > client > am > Blob


1 /*
2
3    Derby - Class org.apache.derby.client.am.Blob
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to You under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20 */

21
22 package org.apache.derby.client.am;
23
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.sql.SQLException JavaDoc;
27 import java.util.ArrayList JavaDoc;
28
29 import org.apache.derby.shared.common.reference.SQLState;
30
31 public class Blob extends Lob implements java.sql.Blob JavaDoc {
32     
33     //This boolean variable indicates whether the Blob object has
34
//been invalidated by calling free() on it
35
private boolean isValid = true;
36     
37     //-----------------------------state------------------------------------------
38

39     byte[] binaryString_ = null;
40
41     // Only used for input purposes. For output, each getBinaryStream call
42
// must generate an independent stream.
43
java.io.InputStream JavaDoc binaryStream_ = null;
44     int dataOffset_;
45
46     //---------------------constructors/finalizer---------------------------------
47

48     public Blob(byte[] binaryString,
49                 Agent agent,
50                 int dataOffset) {
51         super(agent);
52         binaryString_ = binaryString;
53         dataType_ |= BINARY_STRING;
54         sqlLength_ = binaryString.length - dataOffset;
55         lengthObtained_ = true;
56         dataOffset_ = dataOffset;
57     }
58
59     // CTOR for input:
60
public Blob(Agent agent,
61                 java.io.InputStream JavaDoc binaryStream,
62                 int length) {
63         super(agent);
64         binaryStream_ = binaryStream;
65         dataType_ |= BINARY_STREAM;
66         sqlLength_ = length;
67         lengthObtained_ = true;
68     }
69
70     /**
71      * Create a new <code>Blob</code> from a stream with unknown length.
72      * <em>Important:</em> This constructor is a temporary solution for
73      * implementing lengthless overloads in the JDBC4 API. Before a proper
74      * solution can be implemented, we need to enable streaming without having
75      * to know the stream length in the DRDA protocol. See Jira DERBY-1471 and
76      * DERBY-1417 for more details.
77      *
78      * <em>Shortcomings:</em> This constructor will cause the <em>whole stream
79      * to be materialized</em> to determine its length. If the stream is big
80      * enough, the client will fail with an OutOfMemoryError. Since this is a
81      * temporary solution, state checking is not added to all methods as it
82      * would clutter up the class severely. After using the constructor, the
83      * <code>length</code>-method must be called, which will materialize the
84      * stream and determine the length. <em>Do not pass a Blob object created
85      * with this constructor to the user!</em>
86      *
87      * @param agent
88      * @param binaryStream the stream to get data from
89      */

90     public Blob(Agent agent, java.io.InputStream JavaDoc binaryStream) {
91         super(agent);
92         binaryStream_ = binaryStream;
93         dataType_ |= BINARY_STREAM;
94         sqlLength_ = -1;
95         lengthObtained_ = false;
96     }
97
98     // ---------------------------jdbc 2------------------------------------------
99

100     public long length() throws SQLException JavaDoc {
101         //call checkValidity to exit by throwing a SQLException if
102
//the Blob object has been freed by calling free() on it
103
checkValidity();
104         try
105         {
106             synchronized (agent_.connection_) {
107                 if (agent_.loggingEnabled()) {
108                     agent_.logWriter_.traceEntry(this, "length");
109                 }
110                 // Code to handle the lengthless constructor.
111
if (!lengthObtained_) {
112                     binaryStream_ = super.materializeStream(binaryStream_,
113                                                             "java.sql.Blob");
114                 }
115                 long retVal = super.sqlLength();
116                 if (agent_.loggingEnabled()) {
117                     agent_.logWriter_.traceExit(this, "length", retVal);
118                 }
119                 return retVal;
120             }
121         }
122         catch ( SqlException se )
123         {
124             throw se.getSQLException();
125         }
126     }
127
128   /**
129    * Returns as an array of bytes part or all of the <code>BLOB</code>
130    * value that this <code>Blob</code> object designates. The byte
131    * array contains up to <code>length</code> consecutive bytes
132    * starting at position <code>pos</code>.
133    * The starting position must be between 1 and the length
134    * of the BLOB plus 1. This allows for zero-length BLOB values, from
135    * which only zero-length byte arrays can be returned.
136    * If a larger length is requested than there are bytes available,
137    * characters from the start position to the end of the BLOB are returned.
138    * @param pos the ordinal position of the first byte in the
139    * <code>BLOB</code> value to be extracted; the first byte is at
140    * position 1
141    * @param length is the number of consecutive bytes to be copied
142    * @return a byte array containing up to <code>length</code>
143    * consecutive bytes from the <code>BLOB</code> value designated
144    * by this <code>Blob</code> object, starting with the
145    * byte at position <code>startPos</code>.
146    * @exception SQLException if there is an error accessing the
147    * <code>BLOB</code>
148    * NOTE: If the starting position is the length of the BLOB plus 1,
149    * zero bytess are returned regardless of the length requested.
150    */

151     public byte[] getBytes(long pos, int length) throws SQLException JavaDoc {
152         //call checkValidity to exit by throwing a SQLException if
153
//the Blob object has been freed by calling free() on it
154
checkValidity();
155         try
156         {
157             synchronized (agent_.connection_) {
158                 if (agent_.loggingEnabled()) {
159                     agent_.logWriter_.traceEntry(this, "getBytes", (int) pos, length);
160                 }
161                 if (pos <= 0) {
162                     throw new SqlException(agent_.logWriter_,
163                         new ClientMessageId(SQLState.BLOB_BAD_POSITION),
164                         new Long JavaDoc(pos));
165                 }
166                 if (pos > this.length() + 1) {
167                     throw new SqlException(agent_.logWriter_,
168                         new ClientMessageId(SQLState.BLOB_POSITION_TOO_LARGE),
169                         new Long JavaDoc(pos));
170                 }
171                 if (length < 0) {
172                     throw new SqlException(agent_.logWriter_,
173                         new ClientMessageId(SQLState.BLOB_NONPOSITIVE_LENGTH),
174                         new Integer JavaDoc(length));
175                 }
176                 byte[] retVal = getBytesX(pos, length);
177                 if (agent_.loggingEnabled()) {
178                     agent_.logWriter_.traceExit(this, "getBytes", retVal);
179                 }
180                 return retVal;
181             }
182         }
183         catch ( SqlException se )
184         {
185             throw se.getSQLException();
186         }
187     }
188
189     private byte[] getBytesX(long pos, int length) throws SqlException {
190         checkForClosedConnection();
191
192         long actualLength;
193         try {
194             // actual length is the lesser of the number of bytes requested
195
// and the number of bytes available from pos to the end
196
actualLength = Math.min(this.length() - pos + 1, (long) length);
197         } catch ( SQLException JavaDoc se ) {
198             throw new SqlException(se);
199         }
200         byte[] retVal = new byte[(int) actualLength];
201         System.arraycopy(binaryString_, (int) pos + dataOffset_ - 1, retVal, 0, (int) actualLength);
202         return retVal;
203     }
204
205
206     public java.io.InputStream JavaDoc getBinaryStream() throws SQLException JavaDoc {
207         //call checkValidity to exit by throwing a SQLException if
208
//the Blob object has been freed by calling free() on it
209
checkValidity();
210         try
211         {
212             synchronized (agent_.connection_) {
213                 if (agent_.loggingEnabled()) {
214                     agent_.logWriter_.traceEntry(this, "getBinaryStream");
215                 }
216                 java.io.InputStream JavaDoc retVal = getBinaryStreamX();
217                 if (agent_.loggingEnabled()) {
218                     agent_.logWriter_.traceExit(this, "getBinaryStream", retVal);
219                 }
220                 return retVal;
221             }
222         }
223         catch ( SqlException se )
224         {
225             throw se.getSQLException();
226         }
227     }
228
229     private java.io.InputStream JavaDoc getBinaryStreamX() throws SqlException {
230         checkForClosedConnection();
231
232         if (isBinaryStream()) // this Lob is used for input
233
{
234             return binaryStream_;
235         }
236
237         return new java.io.ByteArrayInputStream JavaDoc(binaryString_, dataOffset_, binaryString_.length - dataOffset_);
238     }
239
240     public long position(byte[] pattern, long start) throws SQLException JavaDoc {
241         //call checkValidity to exit by throwing a SQLException if
242
//the Blob object has been freed by calling free() on it
243
checkValidity();
244         try
245         {
246             synchronized (agent_.connection_) {
247                 if (agent_.loggingEnabled()) {
248                     agent_.logWriter_.traceEntry(this, "position(byte[], long)", pattern, start);
249                 }
250                 if (pattern == null) {
251                     throw new SqlException(agent_.logWriter_,
252                         new ClientMessageId(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR));
253                 }
254                 if (start < 1) {
255                     throw new SqlException(agent_.logWriter_,
256                         new ClientMessageId(SQLState.BLOB_BAD_POSITION),
257                             new Long JavaDoc(start));
258                 }
259                 long pos = positionX(pattern, start);
260                 if (agent_.loggingEnabled()) {
261                     agent_.logWriter_.traceExit(this, "position(byte[], long)", pos);
262                 }
263                 return pos;
264             }
265         }
266         catch ( SqlException se )
267         {
268             throw se.getSQLException();
269         }
270     }
271
272     private long positionX(byte[] pattern, long start) throws SqlException {
273         checkForClosedConnection();
274
275         return binaryStringPosition(pattern, start);
276     }
277
278     public long position(java.sql.Blob JavaDoc pattern, long start) throws SQLException JavaDoc {
279         //call checkValidity to exit by throwing a SQLException if
280
//the Blob object has been freed by calling free() on it
281
checkValidity();
282         try
283         {
284             synchronized (agent_.connection_) {
285                 if (agent_.loggingEnabled()) {
286                     agent_.logWriter_.traceEntry(this, "position(Blob, long)", pattern, start);
287                 }
288                 if (pattern == null) {
289                     throw new SqlException(agent_.logWriter_,
290                         new ClientMessageId(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR));
291                 }
292                 if (start < 1) {
293                     throw new SqlException(agent_.logWriter_,
294                         new ClientMessageId(SQLState.BLOB_BAD_POSITION),
295                             new Long JavaDoc(start));
296                 }
297                 long pos = positionX(pattern, start);
298                 if (agent_.loggingEnabled()) {
299                     agent_.logWriter_.traceExit(this, "position(Blob, long)", pos);
300                 }
301                 return pos;
302             }
303         }
304         catch ( SqlException se )
305         {
306             throw se.getSQLException();
307         }
308     }
309
310     private long positionX(java.sql.Blob JavaDoc pattern, long start) throws SqlException {
311         checkForClosedConnection();
312
313         try {
314             return binaryStringPosition(pattern.getBytes(1L, (int) pattern.length()), start);
315         } catch (java.sql.SQLException JavaDoc e) {
316             throw new SqlException(e);
317         }
318     }
319
320     // -------------------------- JDBC 3.0 -----------------------------------
321

322
323     public int setBytes(long pos, byte[] bytes) throws SQLException JavaDoc {
324         //call checkValidity to exit by throwing a SQLException if
325
//the Blob object has been freed by calling free() on it
326
checkValidity();
327         try
328         {
329             synchronized (agent_.connection_) {
330                 if (agent_.loggingEnabled()) {
331                     agent_.logWriter_.traceEntry(this, "setBytes", (int) pos, bytes);
332                 }
333                 int length = setBytesX(pos, bytes, 0, bytes.length);
334                 if (agent_.loggingEnabled()) {
335                     agent_.logWriter_.traceExit(this, "setBytes", length);
336                 }
337                 return length;
338             }
339         }
340         catch ( SqlException se )
341         {
342             throw se.getSQLException();
343         }
344     }
345
346     public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException JavaDoc {
347         //call checkValidity to exit by throwing a SQLException if
348
//the Blob object has been freed by calling free() on it
349
checkValidity();
350         try
351         {
352             synchronized (agent_.connection_) {
353                 if (agent_.loggingEnabled()) {
354                     agent_.logWriter_.traceEntry(this, "setBytes", (int) pos, bytes, offset, len);
355                 }
356                 int length = setBytesX(pos, bytes, offset, len);
357                 if (agent_.loggingEnabled()) {
358                     agent_.logWriter_.traceExit(this, "setBytes", length);
359                 }
360                 return length;
361             }
362         }
363         catch ( SqlException se )
364         {
365             throw se.getSQLException();
366         }
367     }
368
369     public int setBytesX(long pos, byte[] bytes, int offset, int len) throws SqlException {
370         int length = 0;
371         
372         /*
373             Check if position is less than 0 and if true
374             raise an exception
375          */

376         
377         if (pos <= 0L) {
378             throw new SqlException(agent_.logWriter_,
379                     new ClientMessageId(SQLState.BLOB_BAD_POSITION), new Long JavaDoc(pos));
380         }
381         
382         /*
383            Currently only 2G-1 bytes can be inserted in a
384            single Blob column hence check corresponding position
385            value
386          */

387         
388         if (pos >= Integer.MAX_VALUE) {
389             throw new SqlException(agent_.logWriter_,
390                     new ClientMessageId(SQLState.BLOB_POSITION_TOO_LARGE), new Long JavaDoc(pos));
391         }
392         
393         if (pos - 1 > binaryString_.length - dataOffset_) {
394             throw new SqlException(agent_.logWriter_,
395                     new ClientMessageId(SQLState.BLOB_POSITION_TOO_LARGE), new Long JavaDoc(pos));
396         }
397         
398         if ((offset < 0) || offset > bytes.length )
399         {
400             throw new SqlException(agent_.logWriter_,
401                 new ClientMessageId(SQLState.BLOB_INVALID_OFFSET),
402                 new Integer JavaDoc(offset));
403         }
404         if ( len < 0 ) {
405             throw new SqlException(agent_.logWriter_,
406                 new ClientMessageId(SQLState.BLOB_NONPOSITIVE_LENGTH),
407                 new Integer JavaDoc(length));
408         }
409         if (len == 0) {
410             return 0;
411         }
412         length = Math.min((bytes.length - offset), len);
413         if ((binaryString_.length - dataOffset_ - (int) pos + 1) < length) {
414             byte newbuf[] = new byte[(int) pos + length + dataOffset_ - 1];
415             System.arraycopy(binaryString_, 0, newbuf, 0, binaryString_.length);
416             binaryString_ = newbuf;
417         }
418
419         System.arraycopy(bytes, offset, binaryString_, (int) pos + dataOffset_ - 1, length);
420         binaryStream_ = new java.io.ByteArrayInputStream JavaDoc(binaryString_);
421         sqlLength_ = binaryString_.length - dataOffset_;
422         return length;
423     }
424
425     public java.io.OutputStream JavaDoc setBinaryStream(long pos) throws SQLException JavaDoc {
426         //call checkValidity to exit by throwing a SQLException if
427
//the Blob object has been freed by calling free() on it
428
checkValidity();
429         synchronized (agent_.connection_) {
430             if (agent_.loggingEnabled()) {
431                 agent_.logWriter_.traceEntry(this, "setBinaryStream", (int) pos);
432             }
433             BlobOutputStream outStream = new BlobOutputStream(this, pos);
434
435             if (agent_.loggingEnabled()) {
436                 agent_.logWriter_.traceExit(this, "setBinaryStream", outStream);
437             }
438             return outStream;
439         }
440     }
441
442     public void truncate(long len) throws SQLException JavaDoc {
443         //call checkValidity to exit by throwing a SQLException if
444
//the Blob object has been freed by calling free() on it
445
checkValidity();
446         try
447         {
448             synchronized (agent_.connection_) {
449                 if (agent_.loggingEnabled()) {
450                     agent_.logWriter_.traceEntry(this, " truncate", (int) len);
451                 }
452                 if (len < 0 || len > this.length()) {
453                     throw new SqlException(agent_.logWriter_,
454                         new ClientMessageId(SQLState.INVALID_API_PARAMETER),
455                         new Long JavaDoc(len), "len", "Blob.truncate()");
456                 }
457                 if (len == this.length()) {
458                     return;
459                 }
460                 long newLength = (int) len + dataOffset_;
461                 byte newbuf[] = new byte[(int) len + dataOffset_];
462                 System.arraycopy(binaryString_, 0, newbuf, 0, (int) newLength);
463                 binaryString_ = newbuf;
464                 binaryStream_ = new java.io.ByteArrayInputStream JavaDoc(binaryString_);
465                 sqlLength_ = binaryString_.length - dataOffset_;
466             }
467         }
468         catch ( SqlException se )
469         {
470             throw se.getSQLException();
471         }
472     }
473
474     // -------------------------- JDBC 4.0 -----------------------------------
475

476     /**
477      * This method frees the <code>Blob</code> object and releases the resources that
478      * it holds. The object is invalid once the <code>free</code>
479      * method is called. If <code>free</code> is called multiple times, the subsequent
480      * calls to <code>free</code> are treated as a no-op.
481      *
482      * @throws SQLException if an error occurs releasing
483      * the Blob's resources
484      */

485     public void free()
486         throws SQLException JavaDoc {
487         
488         //calling free() on a already freed object is treated as a no-op
489
if (!isValid) return;
490         
491         //now that free has been called the Blob object is no longer
492
//valid
493
isValid = false;
494         
495         if(isBinaryStream()) {
496             try {
497                 binaryStream_.close();
498             }
499             catch(IOException JavaDoc ioe) {
500                 throw new SqlException(null, new ClientMessageId(SQLState.IO_ERROR_UPON_LOB_FREE)).getSQLException();
501             }
502         }
503         else {
504             binaryString_ = null;
505         }
506     }
507
508     public InputStream JavaDoc getBinaryStream(long pos, long length)
509         throws SQLException JavaDoc {
510         throw SQLExceptionFactory.notImplemented("getBinaryStream(long,long)");
511     }
512
513     //------------------ Material layer event callback methods -------------------
514

515     //---------------------------- helper methods --------------------------------
516
public boolean isBinaryString() {
517         return ((dataType_ & BINARY_STRING) == BINARY_STRING);
518     }
519
520     public boolean isBinaryStream() {
521         return ((dataType_ & BINARY_STREAM) == BINARY_STREAM);
522     }
523
524     public byte[] getBinaryString() {
525         return binaryString_;
526     }
527
528     protected long binaryStringPosition(byte[] pattern, long start) {
529         // perform a local byte string search, starting at start
530
// check that the range of comparison is valid
531
int index = (int) start + dataOffset_ - 1; // api start begins at 1
532

533         while (index + pattern.length <= binaryString_.length) {
534             if (isSubString(pattern, index)) {
535                 return (long) (index - dataOffset_ + 1); // readjust for api indexing
536
}
537             index++;
538         }
539         return -1L; // not found
540
}
541
542     // precondition: binaryString_ is long enough for the comparison
543
protected boolean isSubString(byte[] pattern, int index) {
544         for (int i = 0; i < pattern.length; i++, index++) {
545             if (pattern[i] != binaryString_[index]) {
546                 return false;
547             }
548         }
549
550         return true;
551     }
552     
553     /*
554      * Checks is isValid is true. If it is not true throws
555      * a SQLException stating that a method has been called on
556      * an invalid LOB object
557      *
558      * throws SQLException if isvalid is not true.
559      */

560     private void checkValidity() throws SQLException JavaDoc{
561         if(!isValid)
562             throw new SqlException(null,new ClientMessageId(SQLState.LOB_OBJECT_INVALID))
563                                                   .getSQLException();
564     }
565 }
566
Popular Tags