001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb.persist;
032:
033: import java.io.IOException;
034: import java.io.RandomAccessFile;
035: import java.nio.MappedByteBuffer;
036: import java.nio.channels.FileChannel;
037:
038: import org.hsqldb.Database;
039: import org.hsqldb.Trace;
040: import org.hsqldb.lib.SimpleLog;
041:
042: /**
043: * New NIO version of ScaledRAFile. This class is used only for storing a CACHED
044: * TABLE .data file and cannot be used for TEXT TABLE source files.
045: *
046: * Due to various issues with java.nio classes, this class will use a mapped
047: * channel of fixed size. After reaching this size, the file and channel are
048: * closed.
049: *
050: * @author fredt@users
051: * @version 1.8.0.5
052: * @since 1.8.0.5
053: */
054: class ScaledRAFileNIO implements ScaledRAInterface {
055:
056: private final boolean readOnly;
057: private final long bufferLength;
058: private RandomAccessFile file;
059: private MappedByteBuffer buffer;
060: private FileChannel channel;
061: private boolean bufferModified;
062: private SimpleLog appLog;
063: private final static String JVM_ERROR = "JVM threw unsupported Exception";
064:
065: ScaledRAFileNIO(Database database, String name, boolean readOnly,
066: int bufferLength) throws Throwable {
067:
068: long fileLength;
069:
070: if (bufferLength < 1 << 18) {
071: bufferLength = 1 << 18;
072: }
073:
074: try {
075: file = new RandomAccessFile(name, readOnly ? "r" : "rw");
076: } catch (Throwable e) {
077: throw e;
078: }
079:
080: try {
081: fileLength = file.length();
082: } catch (Throwable e) {
083: file.close();
084:
085: throw e;
086: }
087:
088: if (fileLength > ScaledRAFile.MAX_NIO_LENGTH) {
089: file.close();
090:
091: throw new IOException("length exceeds nio limit");
092: }
093:
094: if (bufferLength < fileLength) {
095: bufferLength = (int) fileLength;
096: }
097:
098: bufferLength = newNIOBufferSize(bufferLength);
099:
100: if (readOnly) {
101: bufferLength = (int) fileLength;
102: }
103:
104: if (fileLength < bufferLength) {
105: try {
106: file.seek(bufferLength - 1);
107: file.writeByte(0);
108: file.getFD().sync();
109: file.close();
110:
111: file = new RandomAccessFile(name, readOnly ? "r" : "rw");
112: } catch (Throwable e) {
113: file.close();
114:
115: throw e;
116: }
117: }
118:
119: this .appLog = database.logger.appLog;
120: this .readOnly = readOnly;
121: this .bufferLength = bufferLength;
122: this .channel = file.getChannel();
123:
124: try {
125: buffer = channel.map(
126: readOnly ? FileChannel.MapMode.READ_ONLY
127: : FileChannel.MapMode.READ_WRITE, 0,
128: bufferLength);
129:
130: Trace.printSystemOut("NIO file instance created. mode: "
131: + readOnly);
132:
133: if (!readOnly) {
134: long tempSize = bufferLength - fileLength;
135:
136: if (tempSize > 1 << 18) {
137: tempSize = 1 << 18;
138: }
139:
140: byte[] temp = new byte[(int) tempSize];
141:
142: try {
143: long pos = fileLength;
144:
145: for (; pos < bufferLength - tempSize; pos += tempSize) {
146: buffer.position((int) pos);
147: buffer.put(temp, 0, temp.length);
148: }
149:
150: buffer.position((int) pos);
151: buffer.put(temp, 0, (int) (bufferLength - pos));
152: buffer.force();
153: } catch (Throwable t) {
154: appLog.logContext(t, JVM_ERROR + " " + "length: "
155: + bufferLength);
156: }
157:
158: buffer.position(0);
159: }
160: } catch (Throwable e) {
161: Trace.printSystemOut("NIO constructor failed: "
162: + bufferLength);
163:
164: buffer = null;
165: channel = null;
166:
167: file.close();
168: System.gc();
169:
170: throw e;
171: }
172: }
173:
174: public long length() throws IOException {
175:
176: try {
177: return file.length();
178: } catch (IOException e) {
179: appLog.logContext(e, "nio");
180:
181: throw e;
182: } catch (Throwable e) {
183: appLog.logContext(e, JVM_ERROR);
184:
185: throw new IOException(e.toString());
186: }
187: }
188:
189: public void seek(long newPos) throws IOException {
190:
191: try {
192: buffer.position((int) newPos);
193: } catch (IllegalArgumentException e) {
194: appLog.logContext(e, "nio");
195:
196: throw new IOException(e.toString());
197: } catch (Throwable e) {
198: appLog.logContext(e, JVM_ERROR);
199:
200: throw new IOException(e.toString());
201: }
202: }
203:
204: public long getFilePointer() throws IOException {
205:
206: try {
207: return buffer.position();
208: } catch (Throwable e) {
209: appLog.logContext(e, JVM_ERROR);
210:
211: throw new IOException(e.toString());
212: }
213: }
214:
215: public int read() throws IOException {
216:
217: try {
218: return buffer.get();
219: } catch (Throwable e) {
220: appLog.logContext(e, JVM_ERROR);
221:
222: throw new IOException(e.toString());
223: }
224: }
225:
226: public void read(byte[] b, int offset, int length)
227: throws IOException {
228:
229: try {
230: buffer.get(b, offset, length);
231: } catch (Throwable e) {
232: appLog.logContext(e, JVM_ERROR);
233:
234: throw new IOException(e.toString());
235: }
236: }
237:
238: public int readInt() throws IOException {
239:
240: try {
241: return buffer.getInt();
242: } catch (Throwable e) {
243: appLog.logContext(e, JVM_ERROR);
244:
245: throw new IOException(e.toString());
246: }
247: }
248:
249: public long readLong() throws IOException {
250:
251: try {
252: return buffer.getLong();
253: } catch (Throwable e) {
254: appLog.logContext(e, JVM_ERROR);
255:
256: throw new IOException(e.toString());
257: }
258: }
259:
260: public void write(byte[] b, int offset, int len) throws IOException {
261:
262: try {
263: bufferModified = true;
264:
265: buffer.put(b, offset, len);
266: } catch (Throwable e) {
267: appLog.logContext(e, JVM_ERROR);
268:
269: throw new IOException(e.toString());
270: }
271: }
272:
273: public void writeInt(int i) throws IOException {
274:
275: try {
276: bufferModified = true;
277:
278: buffer.putInt(i);
279: } catch (Throwable e) {
280: appLog.logContext(e, JVM_ERROR);
281:
282: throw new IOException(e.toString());
283: }
284: }
285:
286: public void writeLong(long i) throws IOException {
287:
288: try {
289: bufferModified = true;
290:
291: buffer.putLong(i);
292: } catch (Throwable e) {
293: appLog.logContext(e, JVM_ERROR);
294:
295: throw new IOException(e.toString());
296: }
297: }
298:
299: public void close() throws IOException {
300:
301: try {
302: Trace.printSystemOut("NIO close() start - fileLength = "
303: + bufferLength);
304:
305: if (buffer != null && bufferModified) {
306: try {
307: buffer.force();
308: } catch (Throwable t) {
309: try {
310: buffer.force();
311: } catch (Throwable t1) {
312: appLog.logContext(t, JVM_ERROR + " "
313: + "length: " + bufferLength);
314: }
315: }
316: }
317:
318: buffer = null;
319: channel = null;
320:
321: file.close();
322: System.gc();
323: } catch (Throwable e) {
324: appLog.logContext(e, "length: " + bufferLength);
325:
326: throw new IOException(e.toString());
327: }
328: }
329:
330: public boolean isReadOnly() {
331: return readOnly;
332: }
333:
334: public boolean wasNio() {
335: return true;
336: }
337:
338: public boolean canAccess(int length) {
339: return buffer.position() + length <= bufferLength;
340: }
341:
342: public boolean canSeek(long position) {
343: return position <= bufferLength;
344: }
345:
346: public Database getDatabase() {
347: return null;
348: }
349:
350: static int newNIOBufferSize(int newSize) {
351:
352: int bufSize = 0;
353:
354: for (int scale = 20; scale < 30; scale++) {
355: bufSize = 1 << scale;
356:
357: if (bufSize >= newSize) {
358: break;
359: }
360: }
361:
362: return bufSize;
363: }
364: }
|