Source Code Cross Referenced for BlobBuffer.java in  » Database-JDBC-Connection-Pool » jTDS » net » sourceforge » jtds » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database JDBC Connection Pool » jTDS » net.sourceforge.jtds.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        // jTDS JDBC Driver for Microsoft SQL Server and Sybase
0002:        // Copyright (C) 2004 The jTDS Project
0003:        //
0004:        // This library is free software; you can redistribute it and/or
0005:        // modify it under the terms of the GNU Lesser General Public
0006:        // License as published by the Free Software Foundation; either
0007:        // version 2.1 of the License, or (at your option) any later version.
0008:        //
0009:        // This library is distributed in the hope that it will be useful,
0010:        // but WITHOUT ANY WARRANTY; without even the implied warranty of
0011:        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012:        // Lesser General Public License for more details.
0013:        //
0014:        // You should have received a copy of the GNU Lesser General Public
0015:        // License along with this library; if not, write to the Free Software
0016:        // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0017:        //
0018:        package net.sourceforge.jtds.util;
0019:
0020:        import java.io.*;
0021:        import java.sql.SQLException;
0022:
0023:        import net.sourceforge.jtds.jdbc.Messages;
0024:
0025:        /**
0026:         * Manages a buffer (backed by optional disk storage) for use as a data store
0027:         * by the CLOB and BLOB objects.
0028:         * <p/>
0029:         * The data can be purely memory based until the size exceeds the value
0030:         * dictated by the <code>lobBuffer</code> URL property after which it will be
0031:         * written to disk. The disk array is accessed randomly one page (1024 bytes)
0032:         * at a time.
0033:         * <p/>
0034:         * This class is not synchronized and concurrent open input and output
0035:         * streams can conflict.
0036:         * <p/>
0037:         * Tuning hints:
0038:         * <ol>
0039:         *   <li>The <code>PAGE_SIZE</code> governs how much data is buffered when
0040:         *     reading or writing data a byte at a time. 1024 bytes seems to work well
0041:         *     but if very large objects are being written a byte at a time 4096 may be
0042:         *     better. <b>NB.</b> ensure that the <code>PAGE_MASK</code> and
0043:         *     <code>BYTE_MASK</code> fields are also adjusted to match.
0044:         *   <li>Reading or writing byte arrays that are greater than or equal to the
0045:         *     page size will go directly to or from the random access file cutting out
0046:         *     an ArrayCopy operation.
0047:         *   <li>If BLOBs are being buffered exclusively in memory you may wish to
0048:         *     adjust the <code>MAX_BUF_INC</code> value. Every time the buffer is
0049:         *     expanded the existing contents are copied and this may get expensive
0050:         *     with very large BLOBs.
0051:         *   <li>The BLOB file will be kept open for as long as there are open input or
0052:         *     output streams. Therefore BLOB streams should be explicitly closed as
0053:         *     soon as they are finished with.
0054:         * </ol>
0055:         *
0056:         * @author Mike Hutchinson
0057:         * @version $Id: BlobBuffer.java,v 1.4 2007/07/08 21:38:14 bheineman Exp $
0058:         */
0059:        public class BlobBuffer {
0060:
0061:            /**
0062:             * Default zero length buffer.
0063:             */
0064:            private static final byte[] EMPTY_BUFFER = new byte[0];
0065:            /**
0066:             * Default page size (must be power of 2).
0067:             */
0068:            private static final int PAGE_SIZE = 1024;
0069:            /**
0070:             * Mask for page component of read/write pointer.
0071:             */
0072:            private static final int PAGE_MASK = 0xFFFFFC00;
0073:            /**
0074:             * Mask for page offset component of R/W pointer.
0075:             */
0076:            private static final int BYTE_MASK = 0x000003FF;
0077:            /**
0078:             * Maximum buffer increment.
0079:             */
0080:            private static final int MAX_BUF_INC = 16384;
0081:            /**
0082:             * Invalid page marker.
0083:             */
0084:            private static final int INVALID_PAGE = -1;
0085:
0086:            /**
0087:             * The BLOB buffer or the current page buffer.
0088:             */
0089:            private byte[] buffer;
0090:            /**
0091:             * The total length of the valid data in buffer.
0092:             */
0093:            private int length;
0094:            /**
0095:             * The number of the current page in memory.
0096:             */
0097:            private int currentPage;
0098:            /**
0099:             * The name of the temporary BLOB disk file.
0100:             */
0101:            private File blobFile;
0102:            /**
0103:             * The RA file object reference or null if closed.
0104:             */
0105:            private RandomAccessFile raFile;
0106:            /**
0107:             * Indicates page in memory must be saved.
0108:             */
0109:            private boolean bufferDirty;
0110:            /**
0111:             * Count of callers that have opened the BLOB file.
0112:             */
0113:            private int openCount;
0114:            /**
0115:             * True if attempts to create a BLOB file have failed.
0116:             */
0117:            private boolean isMemOnly;
0118:            /**
0119:             * The directory to buffer data to.
0120:             */
0121:            private final File bufferDir;
0122:            /**
0123:             * The maximum size of an in memory buffer.
0124:             */
0125:            private final int maxMemSize;
0126:
0127:            /**
0128:             * Creates a blob buffer.
0129:             *
0130:             * @param bufferDir
0131:             * @param maxMemSize the maximum size of the in memory buffer
0132:             */
0133:            public BlobBuffer(File bufferDir, long maxMemSize) {
0134:                this .bufferDir = bufferDir;
0135:                this .maxMemSize = (int) maxMemSize;
0136:                buffer = EMPTY_BUFFER;
0137:            }
0138:
0139:            /**
0140:             * Finalizes this object by deleting any work files.
0141:             */
0142:            protected void finalize() throws Throwable {
0143:                try {
0144:                    if (raFile != null) {
0145:                        raFile.close();
0146:                    }
0147:                } catch (IOException e) {
0148:                    // Ignore we are going to delete anyway
0149:                } finally {
0150:                    if (blobFile != null) {
0151:                        blobFile.delete();
0152:                    }
0153:                }
0154:            }
0155:
0156:            /**
0157:             * Creates a random access disk file to use as backing storage for the LOB
0158:             * data.
0159:             * <p/>
0160:             * This method may fail due to security exceptions or local disk problems,
0161:             * in which case the blob storage will remain entirely in memory.
0162:             */
0163:            public void createBlobFile() {
0164:                try {
0165:                    blobFile = File.createTempFile("jtds", ".tmp", bufferDir);
0166:                    blobFile.deleteOnExit();
0167:                    raFile = new RandomAccessFile(blobFile, "rw");
0168:                    if (length > 0) {
0169:                        raFile.write(buffer, 0, (int) length);
0170:                    }
0171:                    buffer = new byte[PAGE_SIZE];
0172:                    currentPage = INVALID_PAGE;
0173:                    openCount = 0;
0174:                } catch (SecurityException e) {
0175:                    blobFile = null;
0176:                    raFile = null;
0177:                    isMemOnly = true;
0178:                    Logger.println("SecurityException creating BLOB file:");
0179:                    Logger.logException(e);
0180:                } catch (IOException ioe) {
0181:                    blobFile = null;
0182:                    raFile = null;
0183:                    isMemOnly = true;
0184:                    Logger.println("IOException creating BLOB file:");
0185:                    Logger.logException(ioe);
0186:                }
0187:            }
0188:
0189:            /**
0190:             * Opens the BLOB disk file.
0191:             * <p/>
0192:             * A count of open and close requests is kept so that the file may be
0193:             * closed when no longer required thus keeping the number of open files to
0194:             * a minimum.
0195:             *
0196:             * @throws IOException if an I/O error occurs
0197:             */
0198:            public void open() throws IOException {
0199:                if (raFile == null && blobFile != null) {
0200:                    // reopen file
0201:                    raFile = new RandomAccessFile(blobFile, "rw");
0202:                    openCount = 1;
0203:                    currentPage = INVALID_PAGE;
0204:                    buffer = new byte[PAGE_SIZE];
0205:                    return;
0206:                }
0207:                if (raFile != null) {
0208:                    openCount++;
0209:                }
0210:            }
0211:
0212:            /**
0213:             * Reads byte from the BLOB buffer at the specified location.
0214:             * <p/>
0215:             * The read pointer is partitioned into a page number and an offset within
0216:             * the page. This routine will read new pages as required. The page size
0217:             * must be a power of 2 and is currently set to 1024 bytes.
0218:             *
0219:             * @param readPtr the offset in the buffer of the required byte
0220:             * @return the byte value as an <code>int</code> or -1 if at EOF
0221:             * @throws IOException if an I/O error occurs
0222:             */
0223:            public int read(int readPtr) throws IOException {
0224:                if (readPtr >= length) {
0225:                    // At end of file.
0226:                    return -1;
0227:                }
0228:                if (raFile != null) {
0229:                    // Paged storage as a file exists
0230:                    if (currentPage != (readPtr & PAGE_MASK)) {
0231:                        // Requested page not in memory so read it
0232:                        readPage(readPtr);
0233:                    }
0234:                    // Use the byte offset to return the correct
0235:                    // byte from the page.
0236:                    return buffer[readPtr & BYTE_MASK] & 0xFF;
0237:                } else {
0238:                    // In memory buffer just return byte.
0239:                    return buffer[readPtr] & 0xFF;
0240:                }
0241:            }
0242:
0243:            /**
0244:             * Reads bytes from the BLOB buffer at the specified location.
0245:             *
0246:             * @param readPtr the offset in the buffer of the required byte
0247:             * @param bytes   the byte array to fill
0248:             * @param offset  the start position in the byte array
0249:             * @param len     the number of bytes to read
0250:             * @return the number of bytes read or -1 if at end of file
0251:             * @throws IOException if an I/O error occurs
0252:             */
0253:            public int read(int readPtr, byte[] bytes, int offset, int len)
0254:                    throws IOException {
0255:                // Validate parameters
0256:                if (bytes == null) {
0257:                    throw new NullPointerException();
0258:                } else if ((offset < 0) || (offset > bytes.length) || (len < 0)
0259:                        || ((offset + len) > bytes.length)
0260:                        || ((offset + len) < 0)) {
0261:                    throw new IndexOutOfBoundsException();
0262:                } else if (len == 0) {
0263:                    return 0;
0264:                }
0265:                if (readPtr >= length) {
0266:                    // At end of file
0267:                    return -1;
0268:                }
0269:
0270:                if (raFile != null) {
0271:                    // Need to read from disk file
0272:                    len = Math.min(length - readPtr, len);
0273:                    if (len >= PAGE_SIZE) {
0274:                        // This is a big write so we optimize by reading directly
0275:                        // from the RA File.
0276:                        if (bufferDirty) {
0277:                            writePage(currentPage);
0278:                        }
0279:                        currentPage = INVALID_PAGE;
0280:                        raFile.seek(readPtr);
0281:                        raFile.readFully(bytes, offset, len);
0282:                    } else {
0283:                        //
0284:                        // Partial read so buffer locally
0285:                        //
0286:                        int count = len;
0287:                        while (count > 0) {
0288:                            if (currentPage != (readPtr & PAGE_MASK)) {
0289:                                // Requested page not in memory so read it
0290:                                readPage(readPtr);
0291:                            }
0292:                            int inBuffer = Math.min(PAGE_SIZE
0293:                                    - (readPtr & BYTE_MASK), count);
0294:                            System.arraycopy(buffer, readPtr & BYTE_MASK,
0295:                                    bytes, offset, inBuffer);
0296:                            offset += inBuffer;
0297:                            readPtr += inBuffer;
0298:                            count -= inBuffer;
0299:                        }
0300:                    }
0301:                } else {
0302:                    // In memory buffer
0303:                    len = Math.min(length - readPtr, len);
0304:                    System.arraycopy(buffer, readPtr, bytes, offset, len);
0305:                }
0306:
0307:                return len;
0308:            }
0309:
0310:            /**
0311:             * Inserts a byte into the buffer at the specified location.
0312:             * <p/>
0313:             * The write pointer is partitioned into a page number and an offset within
0314:             * the page. This routine will write new pages as required. The page size
0315:             * must be a power of 2 and is currently set to 1024 bytes.
0316:             *
0317:             * @param writePtr the offset in the buffer of the required byte
0318:             * @param b        the byte value to write
0319:             * @throws IOException if an I/O error occurs
0320:             */
0321:            public void write(int writePtr, int b) throws IOException {
0322:                if (writePtr >= length) {
0323:                    if (writePtr > length) {
0324:                        // Probably because the user called truncate at
0325:                        // the same time as writing to the blob!
0326:                        throw new IOException("BLOB buffer has been truncated");
0327:                    }
0328:                    // We are writing beyond the current length
0329:                    // of the buffer and need to update the total length.
0330:                    if (++length < 0) {
0331:                        // We have wrapped 31 bits!
0332:                        // This should ensure that the disk file is limited to 2GB.
0333:                        // If in memory JVM will probably have failed by now anyway.
0334:                        throw new IOException("BLOB may not exceed 2GB in size");
0335:                    }
0336:                }
0337:
0338:                if (raFile != null) {
0339:                    // OK we have a disk based buffer
0340:                    if (currentPage != (writePtr & PAGE_MASK)) {
0341:                        // The page we need is not in memory
0342:                        readPage(writePtr);
0343:                    }
0344:                    buffer[writePtr & BYTE_MASK] = (byte) b;
0345:                    // Ensure change will saved if buffer is replaced
0346:                    bufferDirty = true;
0347:                } else {
0348:                    // In memory buffer only (only used here if disk unavailable
0349:                    if (writePtr >= buffer.length) {
0350:                        growBuffer(writePtr + 1);
0351:                    }
0352:                    buffer[writePtr] = (byte) b;
0353:                }
0354:            }
0355:
0356:            /**
0357:             * Inserts bytes into the buffer at the specified location.
0358:             *
0359:             * @param writePtr the offset in the buffer of the required byte
0360:             * @param bytes    the byte array value to write
0361:             * @param offset   the start position in the byte array
0362:             * @param len      the number of bytes to write
0363:             * @throws IOException if an I/O error occurs
0364:             */
0365:            void write(int writePtr, byte[] bytes, int offset, int len)
0366:                    throws IOException {
0367:                // Validate parameters
0368:                if (bytes == null) {
0369:                    throw new NullPointerException();
0370:                } else if ((offset < 0) || (offset > bytes.length) || (len < 0)
0371:                        || ((offset + len) > bytes.length)
0372:                        || ((offset + len) < 0)) {
0373:                    throw new IndexOutOfBoundsException();
0374:                } else if (len == 0) {
0375:                    return;
0376:                }
0377:                if ((long) writePtr + len > (long) Integer.MAX_VALUE) {
0378:                    throw new IOException("BLOB may not exceed 2GB in size");
0379:                }
0380:                if (writePtr > length) {
0381:                    // Probably because the user called truncate at
0382:                    // the same time as writing to the blob!
0383:                    throw new IOException("BLOB buffer has been truncated");
0384:                }
0385:
0386:                if (raFile != null) {
0387:                    // dealing with disk storage (normal case)
0388:                    //
0389:                    if (len >= PAGE_SIZE) {
0390:                        // This is a big write so we optimize by writing directly
0391:                        // to the RA File.
0392:                        if (bufferDirty) {
0393:                            writePage(currentPage);
0394:                        }
0395:                        currentPage = INVALID_PAGE;
0396:                        raFile.seek(writePtr);
0397:                        raFile.write(bytes, offset, len);
0398:                        writePtr += len;
0399:                    } else {
0400:                        // Small writes so use the page buffer for
0401:                        // effeciency.
0402:                        int count = len;
0403:                        while (count > 0) {
0404:                            // Paged storage as a file exists
0405:                            if (currentPage != (writePtr & PAGE_MASK)) {
0406:                                // Requested page not in memory so read it
0407:                                readPage(writePtr);
0408:                            }
0409:                            int inBuffer = Math.min(PAGE_SIZE
0410:                                    - (writePtr & BYTE_MASK), count);
0411:                            System.arraycopy(bytes, offset, buffer, writePtr
0412:                                    & BYTE_MASK, inBuffer);
0413:                            bufferDirty = true;
0414:                            offset += inBuffer;
0415:                            writePtr += inBuffer;
0416:                            count -= inBuffer;
0417:                        }
0418:                    }
0419:                } else {
0420:                    // In memory (only used here if disk not available)
0421:                    if (writePtr + len > buffer.length) {
0422:                        growBuffer(writePtr + len);
0423:                    }
0424:                    System.arraycopy(bytes, offset, buffer, writePtr, len);
0425:                    writePtr += len;
0426:                }
0427:                if (writePtr > length) {
0428:                    length = writePtr;
0429:                }
0430:            }
0431:
0432:            /**
0433:             * Reads in the specified page from the disk buffer.
0434:             * <p/>
0435:             * Any existing dirty page is first saved to disk.
0436:             *
0437:             * @param page the page number
0438:             * @throws IOException if an I/O error occurs
0439:             */
0440:            public void readPage(int page) throws IOException {
0441:                page = page & PAGE_MASK;
0442:                if (bufferDirty) {
0443:                    writePage(currentPage);
0444:                }
0445:                if (page > raFile.length()) {
0446:                    throw new IOException("readPage: Invalid page number "
0447:                            + page);
0448:                }
0449:                currentPage = page;
0450:                // Locate and read requested page
0451:                // NB. Page may not be completely filled.
0452:                raFile.seek(currentPage);
0453:                // Repeat reading until buffer is filled or EOF is reached
0454:                int count = 0, res;
0455:                do {
0456:                    res = raFile.read(buffer, count, buffer.length - count);
0457:                    count += (res == -1) ? 0 : res;
0458:                } while (count < PAGE_SIZE && res != -1);
0459:            }
0460:
0461:            /**
0462:             * Writes the specified page to the disk buffer.
0463:             *
0464:             * @param page the page number
0465:             * @throws IOException if an I/O error occurs
0466:             */
0467:            public void writePage(int page) throws IOException {
0468:                page = page & PAGE_MASK;
0469:                if (page > raFile.length()) {
0470:                    throw new IOException("writePage: Invalid page number "
0471:                            + page);
0472:                }
0473:                if (buffer.length != PAGE_SIZE) {
0474:                    throw new IllegalStateException(
0475:                            "writePage: buffer size invalid");
0476:                }
0477:                raFile.seek(page);
0478:                raFile.write(buffer);
0479:                bufferDirty = false;
0480:            }
0481:
0482:            /**
0483:             * Logically closes the file or physically close it if the open count is
0484:             * now zero.
0485:             * <p/>
0486:             * Any updated buffer in memory is flushed to disk before the file is
0487:             * closed.
0488:             *
0489:             * @throws IOException if an I/O error occurs
0490:             */
0491:            public void close() throws IOException {
0492:                if (openCount > 0) {
0493:                    if (--openCount == 0 && raFile != null) {
0494:                        if (bufferDirty) {
0495:                            writePage(currentPage);
0496:                        }
0497:                        raFile.close();
0498:                        raFile = null;
0499:                        // Allow buffer to be garbage collected
0500:                        buffer = EMPTY_BUFFER;
0501:                        currentPage = INVALID_PAGE;
0502:                    }
0503:                }
0504:            }
0505:
0506:            /**
0507:             * Increases the size of the in memory buffer for situations where disk
0508:             * storage of BLOB is not possible.
0509:             *
0510:             * @param minSize the minimum size of buffer required
0511:             */
0512:            public void growBuffer(int minSize) {
0513:                if (buffer.length == 0) {
0514:                    // Assign initial buffer
0515:                    buffer = new byte[Math.max(PAGE_SIZE, minSize)];
0516:                } else {
0517:                    byte[] tmp;
0518:                    if (buffer.length * 2 > minSize
0519:                            && buffer.length <= MAX_BUF_INC) {
0520:                        tmp = new byte[buffer.length * 2];
0521:                    } else {
0522:                        tmp = new byte[minSize + MAX_BUF_INC];
0523:                    }
0524:                    // Copy over existing data
0525:                    System.arraycopy(buffer, 0, tmp, 0, buffer.length);
0526:                    buffer = tmp; // Assign new buffer.
0527:                }
0528:            }
0529:
0530:            /**
0531:             * Sets the initial buffer to an existing byte array.
0532:             *
0533:             * @param bytes the byte array containing the BLOB data
0534:             * @param copy  true if a local copy of the data is required
0535:             */
0536:            public void setBuffer(byte[] bytes, boolean copy) {
0537:                if (copy) {
0538:                    this .buffer = new byte[bytes.length];
0539:                    System.arraycopy(bytes, 0, this .buffer, 0, buffer.length);
0540:                } else {
0541:                    this .buffer = bytes;
0542:                }
0543:                this .length = buffer.length;
0544:            }
0545:
0546:            //
0547:            // ---- Inner classes implementing the various input/output stream classes ---
0548:            //
0549:
0550:            /**
0551:             * An <code>InputStream</code> over the BLOB buffer.
0552:             */
0553:            private class BlobInputStream extends InputStream {
0554:                private int readPtr;
0555:                private boolean open;
0556:
0557:                /**
0558:                 * Costructs an <code>InputStream</code> object over the BLOB buffer.
0559:                 *
0560:                 * @param pos  the starting position (from 0)
0561:                 * @throws IOException if an I/O error occurs
0562:                 */
0563:                public BlobInputStream(long pos) throws IOException {
0564:                    BlobBuffer.this .open();
0565:                    open = true;
0566:                    readPtr = (int) pos;
0567:                }
0568:
0569:                /**
0570:                 * Ensures underlying BLOB file can be closed even if user does not
0571:                 * close this stream.
0572:                 */
0573:                protected void finalize() throws Throwable {
0574:                    if (open) {
0575:                        try {
0576:                            close();
0577:                        } catch (IOException e) {
0578:                            // Ignore closing anyway
0579:                        } finally {
0580:                            super .finalize();
0581:                        }
0582:                    }
0583:                }
0584:
0585:                /**
0586:                 * Returns the number of bytes available to read.
0587:                 *
0588:                 * @throws IOException if an I/O error occurs
0589:                 */
0590:                public int available() throws IOException {
0591:                    return (int) BlobBuffer.this .getLength() - readPtr;
0592:                }
0593:
0594:                /**
0595:                 * Reads the next byte from the stream.
0596:                 *
0597:                 * @return the next byte as an <code>int</code> or -1 if at EOF
0598:                 * @throws IOException if an I/O error occurs
0599:                 */
0600:                public int read() throws IOException {
0601:                    int b = BlobBuffer.this .read(readPtr);
0602:                    if (b >= 0) {
0603:                        readPtr++;
0604:                    }
0605:                    return b;
0606:                }
0607:
0608:                /**
0609:                 * Reads a bytes from the stream.
0610:                 *
0611:                 * @param bytes  the byte array to fill
0612:                 * @param offset the start position in the byte array
0613:                 * @param len    the number of bytes to read
0614:                 * @return the number of bytes read or -1 if at end of file
0615:                 * @throws IOException if an I/O error occurs
0616:                 */
0617:                public int read(byte[] bytes, int offset, int len)
0618:                        throws IOException {
0619:                    int b = BlobBuffer.this .read(readPtr, bytes, offset, len);
0620:                    if (b > 0) {
0621:                        readPtr += b;
0622:                    }
0623:                    return b;
0624:                }
0625:
0626:                /**
0627:                 * Closes the output stream.
0628:                 *
0629:                 * @throws IOException if an I/O error occurs
0630:                 */
0631:                public void close() throws IOException {
0632:                    if (open) {
0633:                        BlobBuffer.this .close();
0634:                        open = false;
0635:                    }
0636:                }
0637:            }
0638:
0639:            /**
0640:             * A Big Endian Unicode <code>InputStream</code> over the CLOB buffer.
0641:             */
0642:            private class UnicodeInputStream extends InputStream {
0643:                private int readPtr;
0644:                private boolean open;
0645:
0646:                /**
0647:                 * Costructs an InputStream object over the BLOB buffer.
0648:                 *
0649:                 * @param pos  the starting position (from 0)
0650:                 * @throws IOException if an I/O error occurs
0651:                 */
0652:                public UnicodeInputStream(long pos) throws IOException {
0653:                    BlobBuffer.this .open();
0654:                    open = true;
0655:                    readPtr = (int) pos;
0656:                }
0657:
0658:                /**
0659:                 * Ensures underlying BLOB file can be closed even if user does not
0660:                 * close this stream.
0661:                 */
0662:                protected void finalize() throws Throwable {
0663:                    if (open) {
0664:                        try {
0665:                            close();
0666:                        } catch (IOException e) {
0667:                            // Ignore closing anyway
0668:                        } finally {
0669:                            super .finalize();
0670:                        }
0671:                    }
0672:                }
0673:
0674:                /**
0675:                 * Returns the number of bytes available to read.
0676:                 *
0677:                 * @throws IOException if an I/O error occurs
0678:                 */
0679:                public int available() throws IOException {
0680:                    return (int) BlobBuffer.this .getLength() - readPtr;
0681:                }
0682:
0683:                /**
0684:                 * Reads the next byte from the stream.
0685:                 *
0686:                 * @return the next byte as an <code>int</code> or -1 if at EOF
0687:                 * @throws IOException if an I/O error occurs
0688:                 */
0689:                public int read() throws IOException {
0690:                    //
0691:                    // The XOR of 1 with the readPtr forces the bytes to be returned
0692:                    // in big endian order.
0693:                    //
0694:                    int b = BlobBuffer.this .read(readPtr ^ 1);
0695:                    if (b >= 0) {
0696:                        readPtr++;
0697:                    }
0698:                    return b;
0699:                }
0700:
0701:                /**
0702:                 * Close the output stream.
0703:                 *
0704:                 * @throws IOException if an I/O error occurs
0705:                 */
0706:                public void close() throws IOException {
0707:                    if (open) {
0708:                        BlobBuffer.this .close();
0709:                        open = false;
0710:                    }
0711:                }
0712:            }
0713:
0714:            /**
0715:             * An ASCII <code>InputStream</code> over the CLOB buffer.
0716:             * <p/>
0717:             * This class interprets ASCII as anything which has a value below 0x80.
0718:             * This is more rigid than other drivers which allow any character below
0719:             * 0x100 to be converted to returned. The more relaxed coding is useful
0720:             * when dealing with most single byte character sets and if this behaviour
0721:             * is desired, comment out the line indicated in the read method.
0722:             */
0723:            private class AsciiInputStream extends InputStream {
0724:                private int readPtr;
0725:                private boolean open;
0726:
0727:                /**
0728:                 * Costructs an InputStream object over the BLOB buffer.
0729:                 *
0730:                 * @param pos  the starting position (from 0)
0731:                 * @throws IOException if an I/O error occurs
0732:                 */
0733:                public AsciiInputStream(long pos) throws IOException {
0734:                    BlobBuffer.this .open();
0735:                    open = true;
0736:                    readPtr = (int) pos;
0737:                }
0738:
0739:                /**
0740:                 * Ensures underlying BLOB file can be closed even if user does not
0741:                 * close this stream.
0742:                 */
0743:                protected void finalize() throws Throwable {
0744:                    if (open) {
0745:                        try {
0746:                            close();
0747:                        } catch (IOException e) {
0748:                            // Ignore closing anyway
0749:                        } finally {
0750:                            super .finalize();
0751:                        }
0752:                    }
0753:                }
0754:
0755:                /**
0756:                 * Returns the number of bytes available to read.
0757:                 *
0758:                 * @throws IOException if an I/O error occurs
0759:                 */
0760:                public int available() throws IOException {
0761:                    return ((int) BlobBuffer.this .getLength() - readPtr) / 2;
0762:                }
0763:
0764:                /**
0765:                 * Read the next byte from the stream.
0766:                 *
0767:                 * @return the next byte as an <code>int</code> or -1 if at EOF
0768:                 * @throws IOException if an I/O error occurs
0769:                 */
0770:                public int read() throws IOException {
0771:                    int b1 = BlobBuffer.this .read(readPtr);
0772:                    if (b1 >= 0) {
0773:                        readPtr++;
0774:                        int b2 = BlobBuffer.this .read(readPtr);
0775:                        if (b2 >= 0) {
0776:                            readPtr++;
0777:                            if (b2 != 0 || b1 > 0x7F // Comment out this line for a more
0778:                            // permissive interpretation of 'ASCII'.
0779:                            ) {
0780:                                b1 = '?'; // Not ASCII set to '?'
0781:                            }
0782:                            return b1;
0783:                        }
0784:                    }
0785:                    return -1;
0786:                }
0787:
0788:                /**
0789:                 * Closes the output stream.
0790:                 *
0791:                 * @throws IOException if an I/O error occurs
0792:                 */
0793:                public void close() throws IOException {
0794:                    if (open) {
0795:                        BlobBuffer.this .close();
0796:                        open = false;
0797:                    }
0798:                }
0799:            }
0800:
0801:            /**
0802:             * Implements an <code>OutputStream</code> for BLOB data.
0803:             */
0804:            private class BlobOutputStream extends OutputStream {
0805:                private int writePtr;
0806:                private boolean open;
0807:
0808:                /**
0809:                 * Costructs an OutputStream object over the BLOB buffer.
0810:                 *
0811:                 * @param pos  the starting position (from 0)
0812:                 * @throws IOException if an I/O error occurs
0813:                 */
0814:                BlobOutputStream(long pos) throws IOException {
0815:                    BlobBuffer.this .open();
0816:                    open = true;
0817:                    writePtr = (int) pos;
0818:                }
0819:
0820:                /**
0821:                 * Ensures underlying BLOB file can be closed even if user does not
0822:                 * close this stream.
0823:                 */
0824:                protected void finalize() throws Throwable {
0825:                    if (open) {
0826:                        try {
0827:                            close();
0828:                        } catch (IOException e) {
0829:                            // Ignore closing anyway
0830:                        } finally {
0831:                            super .finalize();
0832:                        }
0833:                    }
0834:                }
0835:
0836:                /**
0837:                 * Write a byte to the BLOB buffer.
0838:                 *
0839:                 * @param b the byte value to write
0840:                 * @throws IOException if an I/O error occurs
0841:                 */
0842:                public void write(int b) throws IOException {
0843:                    BlobBuffer.this .write(writePtr++, b);
0844:                }
0845:
0846:                /**
0847:                 * Write bytes to the BLOB buffer.
0848:                 *
0849:                 * @param bytes  the byte array value to write
0850:                 * @param offset the start position in the byte array
0851:                 * @param len    the number of bytes to write
0852:                 * @throws IOException if an I/O error occurs
0853:                 */
0854:                public void write(byte[] bytes, int offset, int len)
0855:                        throws IOException {
0856:                    BlobBuffer.this .write(writePtr, bytes, offset, len);
0857:                    writePtr += len;
0858:                }
0859:
0860:                /**
0861:                 * Close the output stream.
0862:                 *
0863:                 * @throws IOException if an I/O error occurs
0864:                 */
0865:                public void close() throws IOException {
0866:                    if (open) {
0867:                        BlobBuffer.this .close();
0868:                        open = false;
0869:                    }
0870:                }
0871:            }
0872:
0873:            /**
0874:             * Implements an ASCII <code>OutputStream</code> for CLOB data.
0875:             */
0876:            private class AsciiOutputStream extends OutputStream {
0877:                private int writePtr;
0878:                private boolean open;
0879:
0880:                /**
0881:                 * Costructs an ASCII <code>OutputStream</code> object over the BLOB
0882:                 * buffer.
0883:                 *
0884:                 * @param pos  the starting position (from 0)
0885:                 * @throws IOException if an I/O error occurs
0886:                 */
0887:                AsciiOutputStream(long pos) throws IOException {
0888:                    BlobBuffer.this .open();
0889:                    open = true;
0890:                    writePtr = (int) pos;
0891:                }
0892:
0893:                /**
0894:                 * Ensures underlying BLOB file can be closed even if user does not
0895:                 * close this stream.
0896:                 */
0897:                protected void finalize() throws Throwable {
0898:                    if (open) {
0899:                        try {
0900:                            close();
0901:                        } catch (IOException e) {
0902:                            // Ignore closing anyway
0903:                        } finally {
0904:                            super .finalize();
0905:                        }
0906:                    }
0907:                }
0908:
0909:                /**
0910:                 * Writes a byte to the BLOB buffer.
0911:                 *
0912:                 * @param b the byte value to write
0913:                 * @throws IOException if an I/O error occurs
0914:                 */
0915:                public void write(int b) throws IOException {
0916:                    BlobBuffer.this .write(writePtr++, b);
0917:                    BlobBuffer.this .write(writePtr++, 0);
0918:                }
0919:
0920:                /**
0921:                 * Closes the output stream.
0922:                 *
0923:                 * @throws IOException if an I/O error occurs
0924:                 */
0925:                public void close() throws IOException {
0926:                    if (open) {
0927:                        BlobBuffer.this .close();
0928:                        open = false;
0929:                    }
0930:                }
0931:            }
0932:
0933:            //
0934:            // ---- Support methods for CLOB/BLOB ----
0935:            //
0936:
0937:            /**
0938:             * Returns the BLOB data as a byte array.
0939:             *
0940:             * @param pos the start position in the BLOB buffer (from 1)
0941:             * @param len the number of bytes to copy
0942:             * @return the requested data as a <code>byte[]</code>
0943:             */
0944:            public byte[] getBytes(long pos, int len) throws SQLException {
0945:                pos--;
0946:                if (pos < 0) {
0947:                    throw new SQLException(Messages
0948:                            .get("error.blobclob.badpos"), "HY090");
0949:                }
0950:                if (pos > this .length) {
0951:                    throw new SQLException(Messages
0952:                            .get("error.blobclob.badposlen"), "HY090");
0953:                }
0954:                if (len < 0) {
0955:                    throw new SQLException(Messages
0956:                            .get("error.blobclob.badlen"), "HY090");
0957:                }
0958:                if (pos + len > this .length) {
0959:                    // Don't throw an exception, just return as much data as available
0960:                    len = (int) (this .length - pos);
0961:                }
0962:                try {
0963:                    // Should not do this. It could cause trouble.
0964:                    //            if (pos == 0 && len == buffer.length && blobFile == null) {
0965:                    //                // There is no file and we do not need a subset of the data.
0966:                    //                // We should copy the buffer as the user may modify its
0967:                    //                // contents but this would be wasteful in most cases.
0968:                    //                return buffer;
0969:                    //            }
0970:                    // We do need a subset or we are reading from the file
0971:                    byte[] data = new byte[len];
0972:                    if (blobFile == null) {
0973:                        // Just copy subset from memory buffer
0974:                        System.arraycopy(buffer, (int) (pos), data, 0, len);
0975:                    } else {
0976:                        // Copy data from disk buffer
0977:                        InputStream is = new BlobInputStream(pos);
0978:                        int bc = is.read(data);
0979:                        is.close();
0980:                        if (bc != data.length) {
0981:                            throw new IOException(
0982:                                    "Unexpected EOF on BLOB data file bc=" + bc
0983:                                            + " data.len=" + data.length);
0984:                        }
0985:                    }
0986:                    return data;
0987:                } catch (IOException e) {
0988:                    throw new SQLException(Messages.get(
0989:                            "error.generic.ioerror", e.getMessage()), "HY000");
0990:                }
0991:            }
0992:
0993:            /**
0994:             * Retrieve the BLOB data as an <code>InputStream</code>.
0995:             *
0996:             * @param ascii true if an ASCII input stream should be returned
0997:             * @return the <code>InputStream</code> built over the BLOB data
0998:             * @throws SQLException if an error occurs
0999:             */
1000:            public InputStream getBinaryStream(boolean ascii)
1001:                    throws SQLException {
1002:                try {
1003:                    if (ascii) {
1004:                        return new AsciiInputStream(0);
1005:                    } else {
1006:                        return new BlobInputStream(0);
1007:                    }
1008:                } catch (IOException e) {
1009:                    throw new SQLException(Messages.get(
1010:                            "error.generic.ioerror", e.getMessage()), "HY000");
1011:                }
1012:            }
1013:
1014:            /**
1015:             * Retrieve the BLOB data as an Big Endian Unicode
1016:             * <code>InputStream</code>.
1017:             *
1018:             * @return the <code>InputStream</code> built over the BLOB data
1019:             * @throws SQLException if an error occurs
1020:             */
1021:            public InputStream getUnicodeStream() throws SQLException {
1022:                try {
1023:                    return new UnicodeInputStream(0);
1024:                } catch (IOException e) {
1025:                    throw new SQLException(Messages.get(
1026:                            "error.generic.ioerror", e.getMessage()), "HY000");
1027:                }
1028:            }
1029:
1030:            /**
1031:             * Creates an <code>OutputStream</code> that can be used to update the
1032:             * BLOB.
1033:             * <p/>
1034:             * Given that we cannot know the final size of a BLOB created by the caller
1035:             * of this method, we assume the worst and create a disk BLOB by default.
1036:             *
1037:             * @param pos   the start position in the buffer (from 1)
1038:             * @param ascii true if an ASCII output stream is required
1039:             * @return the <code>OutputStream</code> to be used to update the BLOB
1040:             * @throws SQLException if an error occurs
1041:             */
1042:            public OutputStream setBinaryStream(long pos, boolean ascii)
1043:                    throws SQLException {
1044:                pos--;
1045:                if (pos < 0) {
1046:                    throw new SQLException(Messages
1047:                            .get("error.blobclob.badpos"), "HY090");
1048:                }
1049:                if (pos > this .length) {
1050:                    throw new SQLException(Messages
1051:                            .get("error.blobclob.badposlen"), "HY090");
1052:                }
1053:                try {
1054:                    if (!isMemOnly && blobFile == null) {
1055:                        createBlobFile();
1056:                    }
1057:                    if (ascii) {
1058:                        return new AsciiOutputStream(pos);
1059:                    } else {
1060:                        return new BlobOutputStream(pos);
1061:                    }
1062:                } catch (IOException e) {
1063:                    throw new SQLException(Messages.get(
1064:                            "error.generic.ioerror", e.getMessage()), "HY000");
1065:                }
1066:            }
1067:
1068:            /**
1069:             * Sets the content of the BLOB to the supplied byte array value.
1070:             * <p/>
1071:             * If the following conditions are met:
1072:             * <ol>
1073:             *   <li>The start position is 1
1074:             *   <li>The existing BLOB length is smaller or the same as the length of
1075:             *     the new data
1076:             *   <li>The new data length does not exceed the in memory limit
1077:             * </ol>
1078:             * then the new data is buffered entirely in memory, otherwise a disk file
1079:             * is created.
1080:             *
1081:             * @param pos    the start position in the buffer (from 1)
1082:             * @param bytes  the byte array containing the data to copy
1083:             * @param offset the start position in the byte array (from 0)
1084:             * @param len    the number of bytes to copy
1085:             * @param copy   true if a local copy of the byte array is required
1086:             * @return the number of bytes copied
1087:             * @throws SQLException if an error occurs
1088:             */
1089:            public int setBytes(long pos, byte[] bytes, int offset, int len,
1090:                    boolean copy) throws SQLException {
1091:                pos--;
1092:                if (pos < 0) {
1093:                    throw new SQLException(Messages
1094:                            .get("error.blobclob.badpos"), "HY090");
1095:                }
1096:                if (pos > this .length) {
1097:                    throw new SQLException(Messages
1098:                            .get("error.blobclob.badposlen"), "HY090");
1099:                }
1100:                if (bytes == null) {
1101:                    throw new SQLException(
1102:                            Messages.get("error.blob.bytesnull"), "HY009");
1103:                }
1104:                if (offset < 0 || offset > bytes.length) {
1105:                    throw new SQLException(Messages
1106:                            .get("error.blobclob.badoffset"), "HY090");
1107:                }
1108:                if (len < 0 || pos + len > (long) Integer.MAX_VALUE
1109:                        || offset + len > bytes.length) {
1110:                    throw new SQLException(Messages
1111:                            .get("error.blobclob.badlen"), "HY090");
1112:                }
1113:                //
1114:                // If there is no disk file and this data will replace the
1115:                // existing contents of the BLOB then just copy byte data to
1116:                // a new buffer array if the size is small enough.
1117:                //
1118:                if (blobFile == null && pos == 0 && len >= this .length
1119:                        && len <= maxMemSize) {
1120:                    if (copy) {
1121:                        buffer = new byte[len];
1122:                        System.arraycopy(bytes, offset, buffer, 0, len);
1123:                    } else {
1124:                        // A copy is not always required
1125:                        buffer = bytes;
1126:                    }
1127:                    length = len;
1128:                    return len;
1129:                }
1130:                try {
1131:                    //
1132:                    // OK we will now try and create a BLOB file as this
1133:                    // is a more complex update.
1134:                    //
1135:                    if (!isMemOnly && blobFile == null) {
1136:                        createBlobFile();
1137:                    }
1138:                    //
1139:                    // Open the BLOB file
1140:                    //
1141:                    open();
1142:                    int ptr = (int) pos;
1143:                    write(ptr, bytes, offset, len);
1144:                    close();
1145:                    return len;
1146:                } catch (IOException e) {
1147:                    throw new SQLException(Messages.get(
1148:                            "error.generic.ioerror", e.getMessage()), "HY000");
1149:                }
1150:            }
1151:
1152:            /**
1153:             * Retrieves the length of this BLOB buffer in bytes.
1154:             *
1155:             * @return the length of the BLOB data in bytes
1156:             */
1157:            public long getLength() {
1158:                return this .length;
1159:            }
1160:
1161:            /**
1162:             * Retrieves the length of the BLOB buffer (in memory version only).
1163:             *
1164:             * @param length the length of the valid data in the buffer
1165:             */
1166:            public void setLength(long length) {
1167:                this .length = (int) length;
1168:            }
1169:
1170:            /**
1171:             * Truncates the BLOB buffer to the specified size.
1172:             *
1173:             * @param len the required length
1174:             * @throws SQLException if an error occurs
1175:             */
1176:            public void truncate(long len) throws SQLException {
1177:                if (len < 0) {
1178:                    throw new SQLException(Messages
1179:                            .get("error.blobclob.badlen"), "HY090");
1180:                }
1181:                if (len > this .length) {
1182:                    throw new SQLException(Messages
1183:                            .get("error.blobclob.lentoolong"), "HY090");
1184:                }
1185:
1186:                length = (int) len;
1187:                if (len == 0) {
1188:                    try {
1189:                        // Try to discard and delete work file
1190:                        // Any open input streams will get EOF
1191:                        // open write streams will probably fail.
1192:                        if (blobFile != null) {
1193:                            if (raFile != null) {
1194:                                raFile.close();
1195:                            }
1196:                            blobFile.delete();
1197:                        }
1198:                    } catch (IOException e) {
1199:                        throw new SQLException(Messages.get(
1200:                                "error.generic.ioerror", e.getMessage()),
1201:                                "HY000");
1202:                    } finally {
1203:                        buffer = EMPTY_BUFFER;
1204:                        blobFile = null;
1205:                        raFile = null;
1206:                        openCount = 0;
1207:                        currentPage = INVALID_PAGE;
1208:                    }
1209:                }
1210:            }
1211:
1212:            /**
1213:             * Provides support for pattern searching methods.
1214:             *
1215:             * @param pattern the byte array containg the search pattern
1216:             * @param start   the start position in the BLOB (from 1)
1217:             * @return the <code>int</code> start index for the pattern (from 1) or -1
1218:             *         if the pattern is not found.
1219:             * @throws SQLException if an error occurs
1220:             */
1221:            public int position(byte[] pattern, long start) throws SQLException {
1222:                try {
1223:                    start--;
1224:                    if (start < 0) {
1225:                        throw new SQLException(Messages
1226:                                .get("error.blobclob.badpos"), "HY090");
1227:                    }
1228:                    if (start >= this .length) {
1229:                        throw new SQLException(Messages
1230:                                .get("error.blobclob.badposlen"), "HY090");
1231:                    }
1232:                    if (pattern == null) {
1233:                        throw new SQLException(Messages
1234:                                .get("error.blob.badpattern"), "HY009");
1235:                    }
1236:                    if (pattern.length == 0 || length == 0
1237:                            || pattern.length > length) {
1238:                        // Impossible for there to be a match
1239:                        return -1;
1240:                    }
1241:                    // FIXME Implement a better (O(n)) search algorithm
1242:                    int limit = (int) length - pattern.length;
1243:                    if (blobFile == null) {
1244:                        for (int i = (int) start; i <= limit; i++) {
1245:                            int p;
1246:                            for (p = 0; p < pattern.length
1247:                                    && buffer[i + p] == pattern[p]; p++)
1248:                                ;
1249:                            if (p == pattern.length) {
1250:                                return i + 1;
1251:                            }
1252:                        }
1253:                    } else {
1254:                        open();
1255:                        for (int i = (int) start; i <= limit; i++) {
1256:                            int p;
1257:                            for (p = 0; p < pattern.length
1258:                                    && read(i + p) == (pattern[p] & 0xFF); p++)
1259:                                ;
1260:                            if (p == pattern.length) {
1261:                                close();
1262:                                return i + 1;
1263:                            }
1264:                        }
1265:                        close();
1266:                    }
1267:                    return -1;
1268:                } catch (IOException e) {
1269:                    throw new SQLException(Messages.get(
1270:                            "error.generic.ioerror", e.getMessage()), "HY000");
1271:                }
1272:            }
1273:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.