From 513da60d950e78ac0d49214c9bd717ffcc96bf90 Mon Sep 17 00:00:00 2001 From: Honghua Zhu Date: Tue, 13 Oct 2015 23:27:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6FileStorage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/lealone/client/FrontendSession.java | 18 +- .../common/security/SecureFileStore.java | 6 +- .../org/lealone/common/value/ValueLob.java | 22 +- .../org/lealone/common/value/ValueLobDb.java | 16 +- .../main/java/org/lealone/db/DataHandler.java | 4 +- .../{FileStore.java => FileStorage.java} | 364 ++++++++++++++++-- ...tream.java => FileStorageInputStream.java} | 36 +- ...ream.java => FileStorageOutputStream.java} | 22 +- .../org/lealone/storage/StorageBuilder.java | 17 +- .../lealone/storage/StorageEngineManager.java | 2 - .../storage/cache/CacheLongKeyLIRS.java | 30 +- .../lealone/storage/cache/FilePathCache.java | 3 +- .../main/java/org/lealone/db/Database.java | 12 +- .../lealone/db/result/ResultDiskBuffer.java | 14 +- .../java/org/lealone/db/result/RowList.java | 10 +- .../java/org/lealone/sql/dml/ScriptBase.java | 26 +- .../java/org/lealone/storage/FileStorage.java | 315 --------------- .../lealone/transaction/log/LogChunkMap.java | 5 +- .../transaction/log/LogFileStorage.java | 313 --------------- 19 files changed, 451 insertions(+), 784 deletions(-) rename lealone-common/src/main/java/org/lealone/storage/{FileStore.java => FileStorage.java} (61%) rename lealone-common/src/main/java/org/lealone/storage/{FileStoreInputStream.java => FileStorageInputStream.java} (79%) rename lealone-common/src/main/java/org/lealone/storage/{FileStoreOutputStream.java => FileStorageOutputStream.java} (80%) rename {lealone-storage => lealone-common}/src/main/java/org/lealone/storage/cache/CacheLongKeyLIRS.java (97%) rename {lealone-storage => lealone-common}/src/main/java/org/lealone/storage/cache/FilePathCache.java (99%) delete mode 100644 lealone-storage/src/main/java/org/lealone/storage/FileStorage.java delete mode 100644 lealone-transaction/src/main/java/org/lealone/transaction/log/LogFileStorage.java diff --git a/lealone-client/src/main/java/org/lealone/client/FrontendSession.java b/lealone-client/src/main/java/org/lealone/client/FrontendSession.java index d21ddea20..9f3839c0f 100644 --- a/lealone-client/src/main/java/org/lealone/client/FrontendSession.java +++ b/lealone-client/src/main/java/org/lealone/client/FrontendSession.java @@ -33,7 +33,7 @@ import org.lealone.db.SessionWithState; import org.lealone.db.SetTypes; import org.lealone.db.SysProperties; -import org.lealone.storage.FileStore; +import org.lealone.storage.FileStorage; import org.lealone.storage.LobStorage; import org.lealone.storage.fs.FileUtils; import org.lealone.transaction.Transaction; @@ -457,24 +457,24 @@ public int getMaxLengthInplaceLob() { } @Override - public FileStore openFile(String name, String mode, boolean mustExist) { + public FileStorage openFile(String name, String mode, boolean mustExist) { if (mustExist && !FileUtils.exists(name)) { throw DbException.get(ErrorCode.FILE_NOT_FOUND_1, name); } - FileStore store; + FileStorage fileStorage; if (cipher == null) { - store = FileStore.open(this, name, mode); + fileStorage = FileStorage.open(this, name, mode); } else { - store = FileStore.open(this, name, mode, cipher, fileEncryptionKey, 0); + fileStorage = FileStorage.open(this, name, mode, cipher, fileEncryptionKey, 0); } - store.setCheckedWriting(false); + fileStorage.setCheckedWriting(false); try { - store.init(); + fileStorage.init(); } catch (DbException e) { - store.closeSilently(); + fileStorage.closeSilently(); throw e; } - return store; + return fileStorage; } @Override diff --git a/lealone-common/src/main/java/org/lealone/common/security/SecureFileStore.java b/lealone-common/src/main/java/org/lealone/common/security/SecureFileStore.java index 7fe1270f0..3bdfa3f56 100644 --- a/lealone-common/src/main/java/org/lealone/common/security/SecureFileStore.java +++ b/lealone-common/src/main/java/org/lealone/common/security/SecureFileStore.java @@ -9,14 +9,14 @@ import org.lealone.common.util.MathUtils; import org.lealone.db.Constants; import org.lealone.db.DataHandler; -import org.lealone.storage.FileStore; +import org.lealone.storage.FileStorage; /** - * A file store that encrypts all data before writing, and decrypts all data + * A file storage that encrypts all data before writing, and decrypts all data * after reading. Areas that were never written to (for example after calling * setLength to enlarge the file) are not encrypted (contains 0 bytes). */ -public class SecureFileStore extends FileStore { +public class SecureFileStore extends FileStorage { private byte[] key; private final BlockCipher cipher; diff --git a/lealone-common/src/main/java/org/lealone/common/value/ValueLob.java b/lealone-common/src/main/java/org/lealone/common/value/ValueLob.java index 8e8bcdbde..331ae7688 100644 --- a/lealone-common/src/main/java/org/lealone/common/value/ValueLob.java +++ b/lealone-common/src/main/java/org/lealone/common/value/ValueLob.java @@ -24,9 +24,9 @@ import org.lealone.db.Constants; import org.lealone.db.DataHandler; import org.lealone.db.SysProperties; -import org.lealone.storage.FileStore; -import org.lealone.storage.FileStoreInputStream; -import org.lealone.storage.FileStoreOutputStream; +import org.lealone.storage.FileStorage; +import org.lealone.storage.FileStorageInputStream; +import org.lealone.storage.FileStorageOutputStream; import org.lealone.storage.fs.FileUtils; /** @@ -62,7 +62,7 @@ public class ValueLob extends Value { private byte[] small; private int hash; private boolean compressed; - private FileStore tempFile; + private FileStorage tempFile; private ValueLob(int type, DataHandler handler, String fileName, int tableId, int objectId, boolean linked, long precision, boolean compressed) { @@ -212,7 +212,7 @@ private static int getBufferSize(DataHandler handler, boolean compress, long rem } private void createFromReader(char[] buff, int len, Reader in, long remaining, DataHandler h) throws IOException { - FileStoreOutputStream out = initLarge(h); + FileStorageOutputStream out = initLarge(h); boolean compress = h.getLobCompressionAlgorithm(Value.CLOB) != null; try { while (true) { @@ -380,7 +380,7 @@ private static ValueLob createBlob(InputStream in, long length, DataHandler hand } } - private FileStoreOutputStream initLarge(DataHandler h) { + private FileStorageOutputStream initLarge(DataHandler h) { this.handler = h; this.tableId = 0; this.linked = false; @@ -400,13 +400,13 @@ private FileStoreOutputStream initLarge(DataHandler h) { tempFile = h.openFile(fileName, "rw", false); tempFile.autoDelete(); } - FileStoreOutputStream out = new FileStoreOutputStream(tempFile, h, compressionAlgorithm); + FileStorageOutputStream out = new FileStorageOutputStream(tempFile, h, compressionAlgorithm); return out; } private void createFromStream(byte[] buff, int len, InputStream in, long remaining, DataHandler h) throws IOException { - FileStoreOutputStream out = initLarge(h); + FileStorageOutputStream out = initLarge(h); boolean compress = h.getLobCompressionAlgorithm(Value.BLOB) != null; try { while (true) { @@ -483,7 +483,7 @@ public void unlink(DataHandler handler) { temp = getFileName(handler, -1, objectId); deleteFile(handler, temp); renameFile(handler, fileName, temp); - tempFile = FileStore.open(handler, temp, "rw"); + tempFile = FileStorage.open(handler, temp, "rw"); tempFile.autoDelete(); tempFile.closeSilently(); fileName = temp; @@ -643,9 +643,9 @@ public InputStream getInputStream() { if (fileName == null) { return new ByteArrayInputStream(small); } - FileStore store = handler.openFile(fileName, "r", true); + FileStorage fileStorage = handler.openFile(fileName, "r", true); boolean alwaysClose = SysProperties.LOB_CLOSE_BETWEEN_READS; - return new BufferedInputStream(new FileStoreInputStream(store, handler, compressed, alwaysClose), + return new BufferedInputStream(new FileStorageInputStream(fileStorage, handler, compressed, alwaysClose), Constants.IO_BUFFER_SIZE); } diff --git a/lealone-common/src/main/java/org/lealone/common/value/ValueLobDb.java b/lealone-common/src/main/java/org/lealone/common/value/ValueLobDb.java index e65206b32..8c1cc6a86 100644 --- a/lealone-common/src/main/java/org/lealone/common/value/ValueLobDb.java +++ b/lealone-common/src/main/java/org/lealone/common/value/ValueLobDb.java @@ -23,9 +23,9 @@ import org.lealone.db.Constants; import org.lealone.db.DataHandler; import org.lealone.db.SysProperties; -import org.lealone.storage.FileStore; -import org.lealone.storage.FileStoreInputStream; -import org.lealone.storage.FileStoreOutputStream; +import org.lealone.storage.FileStorage; +import org.lealone.storage.FileStorageInputStream; +import org.lealone.storage.FileStorageOutputStream; import org.lealone.storage.LobStorage; import org.lealone.storage.fs.FileUtils; @@ -50,7 +50,7 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo private final long precision; private final String fileName; - private final FileStore tempFile; + private final FileStorage tempFile; private int tableId; private int hash; @@ -89,7 +89,7 @@ private ValueLobDb(DataHandler handler, Reader in, long remaining) throws IOExce this.fileName = createTempLobFileName(handler); this.tempFile = this.handler.openFile(fileName, "rw", false); this.tempFile.autoDelete(); - FileStoreOutputStream out = new FileStoreOutputStream(tempFile, null, null); + FileStorageOutputStream out = new FileStorageOutputStream(tempFile, null, null); long tmpPrecision = 0; try { char[] buff = new char[Constants.IO_BUFFER_SIZE]; @@ -118,7 +118,7 @@ private ValueLobDb(DataHandler handler, byte[] buff, int len, InputStream in, lo this.fileName = createTempLobFileName(handler); this.tempFile = this.handler.openFile(fileName, "rw", false); this.tempFile.autoDelete(); - FileStoreOutputStream out = new FileStoreOutputStream(tempFile, null, null); + FileStorageOutputStream out = new FileStorageOutputStream(tempFile, null, null); long tmpPrecision = 0; boolean compress = this.handler.getLobCompressionAlgorithm(Value.BLOB) != null; try { @@ -370,9 +370,9 @@ public InputStream getInputStream() { if (small != null) { return new ByteArrayInputStream(small); } else if (fileName != null) { - FileStore store = handler.openFile(fileName, "r", true); + FileStorage fileStorage = handler.openFile(fileName, "r", true); boolean alwaysClose = SysProperties.LOB_CLOSE_BETWEEN_READS; - return new BufferedInputStream(new FileStoreInputStream(store, handler, false, alwaysClose), + return new BufferedInputStream(new FileStorageInputStream(fileStorage, handler, false, alwaysClose), Constants.IO_BUFFER_SIZE); } long byteCount = (type == Value.BLOB) ? precision : -1; diff --git a/lealone-common/src/main/java/org/lealone/db/DataHandler.java b/lealone-common/src/main/java/org/lealone/db/DataHandler.java index 591773ab4..cffd3f6b7 100644 --- a/lealone-common/src/main/java/org/lealone/db/DataHandler.java +++ b/lealone-common/src/main/java/org/lealone/db/DataHandler.java @@ -11,7 +11,7 @@ import org.lealone.common.message.DbException; import org.lealone.common.util.SmallLRUCache; import org.lealone.common.util.TempFileDeleter; -import org.lealone.storage.FileStore; +import org.lealone.storage.FileStorage; import org.lealone.storage.LobStorage; /** @@ -35,7 +35,7 @@ public interface DataHandler { * @param mustExist whether the file must already exist * @return the file */ - FileStore openFile(String name, String mode, boolean mustExist); + FileStorage openFile(String name, String mode, boolean mustExist); /** * Check if the simulated power failure occurred. diff --git a/lealone-common/src/main/java/org/lealone/storage/FileStore.java b/lealone-common/src/main/java/org/lealone/storage/FileStorage.java similarity index 61% rename from lealone-common/src/main/java/org/lealone/storage/FileStore.java rename to lealone-common/src/main/java/org/lealone/storage/FileStorage.java index 27b287b6d..fafed8f47 100644 --- a/lealone-common/src/main/java/org/lealone/storage/FileStore.java +++ b/lealone-common/src/main/java/org/lealone/storage/FileStorage.java @@ -10,15 +10,24 @@ import java.lang.ref.Reference; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import java.util.Arrays; +import java.util.Map; import org.lealone.api.ErrorCode; import org.lealone.common.message.DbException; import org.lealone.common.security.SecureFileStore; +import org.lealone.common.util.DataUtils; import org.lealone.common.util.TempFileDeleter; import org.lealone.db.Constants; import org.lealone.db.DataHandler; import org.lealone.db.SysProperties; +import org.lealone.storage.cache.FilePathCache; +import org.lealone.storage.fs.FilePath; +import org.lealone.storage.fs.FilePathDisk; +import org.lealone.storage.fs.FilePathEncrypt; +import org.lealone.storage.fs.FilePathNio; import org.lealone.storage.fs.FileUtils; /** @@ -26,7 +35,7 @@ * Each file contains a magic header, and reading / writing is done in blocks. * See also {@link SecureFileStore} */ -public class FileStore { +public class FileStorage { /** * The size of the file header in bytes. @@ -47,17 +56,75 @@ public class FileStore { * The callback object is responsible to check access rights, and free up * disk space if required. */ - private final DataHandler handler; + private DataHandler handler; - private FileChannel file; private long filePos; private long fileLength; private Reference autoDeleteReference; private boolean checkedWriting = true; - private final String mode; - private final TempFileDeleter tempFileDeleter; + private String mode; + private TempFileDeleter tempFileDeleter; private java.nio.channels.FileLock lock; + /** + * The number of read operations. + */ + protected long readCount; + + /** + * The number of read bytes. + */ + protected long readBytes; + + /** + * The number of write operations. + */ + protected long writeCount; + + /** + * The number of written bytes. + */ + protected long writeBytes; + + /** + * The file name. + */ + protected String fileName; + + /** + * Whether this storage is read-only. + */ + protected boolean readOnly; + + /** + * The file size (cached). + */ + protected long fileSize; + + /** + * The file. + */ + protected FileChannel file; + + /** + * The encrypted file (if encryption is used). + */ + protected FileChannel encryptedFile; + + /** + * The file lock. + */ + protected FileLock fileLock; + + @Override + public String toString() { + return fileName; + } + + public FileStorage() { + + } + /** * Create a new file using the given settings. * @@ -65,7 +132,7 @@ public class FileStore { * @param name the file name * @param mode the access mode ("r", "rw", "rws", "rwd") */ - protected FileStore(DataHandler handler, String name, String mode) { + public FileStorage(DataHandler handler, String name, String mode) { this.handler = handler; this.name = name; if (handler != null) { @@ -98,7 +165,7 @@ protected FileStore(DataHandler handler, String name, String mode) { * @param mode the access mode (r, rw, rws, rwd) * @return the created object */ - public static FileStore open(DataHandler handler, String name, String mode) { + public static FileStorage open(DataHandler handler, String name, String mode) { return open(handler, name, mode, null, null, 0); } @@ -112,7 +179,7 @@ public static FileStore open(DataHandler handler, String name, String mode) { * @param key the encryption key * @return the created object */ - public static FileStore open(DataHandler handler, String name, String mode, String cipher, byte[] key) { + public static FileStorage open(DataHandler handler, String name, String mode, String cipher, byte[] key) { return open(handler, name, mode, cipher, key, Constants.ENCRYPTION_KEY_HASH_ITERATIONS); } @@ -127,11 +194,11 @@ public static FileStore open(DataHandler handler, String name, String mode, Stri * @param keyIterations the number of iterations the key should be hashed * @return the created object */ - public static FileStore open(DataHandler handler, String name, String mode, String cipher, byte[] key, + public static FileStorage open(DataHandler handler, String name, String mode, String cipher, byte[] key, int keyIterations) { - FileStore store; + FileStorage store; if (cipher == null) { - store = new FileStore(handler, name, mode); + store = new FileStorage(handler, name, mode); } else { store = new SecureFileStore(handler, name, mode, cipher, key, keyIterations); } @@ -209,21 +276,21 @@ public void init() { } } - /** - * Close the file. - */ - public void close() { - if (file != null) { - try { - trace("close", name, file); - file.close(); - } catch (IOException e) { - throw DbException.convertIOException(e, name); - } finally { - file = null; - } - } - } + // /** + // * Close the file. + // */ + // public void close() { + // if (file != null) { + // try { + // trace("close", name, file); + // file.close(); + // } catch (IOException e) { + // throw DbException.convertIOException(e, name); + // } finally { + // file = null; + // } + // } + // } /** * Close the file without throwing any exceptions. Exceptions are simply @@ -407,14 +474,14 @@ public long getFilePointer() { * Call fsync. Depending on the operating system and hardware, this may or * may not in fact write the changes. */ - public void sync() { - try { - file.force(true); - } catch (IOException e) { - closeFileSilently(); - throw DbException.convertIOException(e, name); - } - } + // public void sync() { + // try { + // file.force(true); + // } catch (IOException e) { + // closeFileSilently(); + // throw DbException.convertIOException(e, name); + // } + // } /** * Automatically delete the file once it is no longer in use. @@ -500,4 +567,233 @@ public synchronized void releaseLock() { } } + /** + * Read from the file. + * + * @param pos the write position + * @param len the number of bytes to read + * @return the byte buffer + */ + public ByteBuffer readFully(long pos, int len) { + ByteBuffer dst = ByteBuffer.allocate(len); + DataUtils.readFully(file, pos, dst); + readCount++; + readBytes += len; + return dst; + } + + /** + * Write to the file. + * + * @param pos the write position + * @param src the source buffer + */ + public void writeFully(long pos, ByteBuffer src) { + int len = src.remaining(); + fileSize = Math.max(fileSize, pos + len); + DataUtils.writeFully(file, pos, src); + writeCount++; + writeBytes += len; + } + + /** + * Try to open the file. + * + * @param fileName the file name + * @param readOnly whether the file should only be opened in read-only mode, + * even if the file is writable + * @param encryptionKey the encryption key, or null if encryption is not + * used + */ + public void open(String fileName, Map config) { + if (file != null) { + return; + } + char[] encryptionKey = (char[]) config.get("encryptionKey"); + boolean readOnly = config.containsKey("readOnly"); + + if (fileName != null) { + FilePath p = FilePath.get(fileName); + // if no explicit scheme was specified, NIO is used + if (p instanceof FilePathDisk && !fileName.startsWith(p.getScheme() + ":")) { + // ensure the NIO file system is registered + FilePathNio.class.getName(); + fileName = "nio:" + fileName; + } + } + this.fileName = fileName; + FilePath f = FilePath.get(fileName); + FilePath parent = f.getParent(); + if (parent != null && !parent.exists()) { + throw DataUtils.newIllegalArgumentException("Directory does not exist: {0}", parent); + } + if (f.exists() && !f.canWrite()) { + readOnly = true; + } + this.readOnly = readOnly; + try { + file = f.open(readOnly ? "r" : "rw"); + if (encryptionKey != null) { + byte[] key = FilePathEncrypt.getPasswordBytes(encryptionKey); + encryptedFile = file; + file = new FilePathEncrypt.FileEncrypt(fileName, key, file); + } + file = FilePathCache.wrap(file); + try { + if (readOnly) { + fileLock = file.tryLock(0, Long.MAX_VALUE, true); + } else { + fileLock = file.tryLock(); + } + } catch (OverlappingFileLockException e) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", + fileName, e); + } + if (fileLock == null) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", + fileName); + } + fileSize = file.size(); + } catch (IOException e) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_READING_FAILED, "Could not open file {0}", + fileName, e); + } + } + + /** + * Close this file. + */ + public void close() { + try { + trace("close", name, file); + if (fileLock != null) { + fileLock.release(); + fileLock = null; + } + if (file != null) + file.close(); + } catch (Exception e) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, "Closing failed for file {0}", + fileName, e); + } finally { + file = null; + } + } + + /** + * Flush all changes. + */ + public void sync() { + try { + file.force(true); + } catch (IOException e) { + closeFileSilently(); + throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, "Could not sync file {0}", + fileName, e); + } + } + + /** + * Get the file size. + * + * @return the file size + */ + public long size() { + return fileSize; + } + + /** + * Truncate the file. + * + * @param size the new file size + */ + public void truncate(long size) { + try { + writeCount++; + file.truncate(size); + fileSize = Math.min(fileSize, size); + } catch (IOException e) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, + "Could not truncate file {0} to size {1}", fileName, size, e); + } + } + + /** + * Get the file instance in use. + *

+ * The application may read from the file (for example for online backup), + * but not write to it or truncate it. + * + * @return the file + */ + public FileChannel getFile() { + return file; + } + + /** + * Get the encrypted file instance, if encryption is used. + *

+ * The application may read from the file (for example for online backup), + * but not write to it or truncate it. + * + * @return the encrypted file, or null if encryption is not used + */ + public FileChannel getEncryptedFile() { + return encryptedFile; + } + + /** + * Get the number of write operations since this storage was opened. + * For file based storages, this is the number of file write operations. + * + * @return the number of write operations + */ + public long getWriteCount() { + return writeCount; + } + + /** + * Get the number of written bytes since this storage was opened. + * + * @return the number of write operations + */ + public long getWriteBytes() { + return writeBytes; + } + + /** + * Get the number of read operations since this storage was opened. + * For file based storages, this is the number of file read operations. + * + * @return the number of read operations + */ + public long getReadCount() { + return readCount; + } + + /** + * Get the number of read bytes since this storage was opened. + * + * @return the number of write operations + */ + public long getReadBytes() { + return readBytes; + } + + public boolean isReadOnly() { + return readOnly; + } + + /** + * Get the file name. + * + * @return the file name + */ + public String getFileName() { + return fileName; + } + + public void delete() { + FileUtils.delete(fileName); + } } diff --git a/lealone-common/src/main/java/org/lealone/storage/FileStoreInputStream.java b/lealone-common/src/main/java/org/lealone/storage/FileStorageInputStream.java similarity index 79% rename from lealone-common/src/main/java/org/lealone/storage/FileStoreInputStream.java rename to lealone-common/src/main/java/org/lealone/storage/FileStorageInputStream.java index bad63f71c..388de32f2 100644 --- a/lealone-common/src/main/java/org/lealone/storage/FileStoreInputStream.java +++ b/lealone-common/src/main/java/org/lealone/storage/FileStorageInputStream.java @@ -17,19 +17,19 @@ import org.lealone.db.DataHandler; /** - * An input stream that is backed by a file store. + * An input stream that is backed by a file storage. */ -public class FileStoreInputStream extends InputStream { +public class FileStorageInputStream extends InputStream { - private FileStore store; + private FileStorage fileStorage; private final Data page; private int remainingInBuffer; private final CompressTool compress; private boolean endOfFile; private final boolean alwaysClose; - public FileStoreInputStream(FileStore store, DataHandler handler, boolean compression, boolean alwaysClose) { - this.store = store; + public FileStorageInputStream(FileStorage fileStorage, DataHandler handler, boolean compression, boolean alwaysClose) { + this.fileStorage = fileStorage; this.alwaysClose = alwaysClose; if (compression) { compress = CompressTool.getInstance(); @@ -38,24 +38,27 @@ public FileStoreInputStream(FileStore store, DataHandler handler, boolean compre } page = Data.create(handler, Constants.FILE_BLOCK_SIZE); try { - if (store.length() <= FileStore.HEADER_LENGTH) { + if (fileStorage.length() <= FileStorage.HEADER_LENGTH) { close(); } else { fillBuffer(); } } catch (IOException e) { - throw DbException.convertIOException(e, store.name); + throw DbException.convertIOException(e, fileStorage.name); } } + @Override public int available() { return remainingInBuffer <= 0 ? 0 : remainingInBuffer; } + @Override public int read(byte[] buff) throws IOException { return read(buff, 0, buff.length); } + @Override public int read(byte[] b, int off, int len) throws IOException { if (len == 0) { return 0; @@ -89,12 +92,12 @@ private void fillBuffer() throws IOException { return; } page.reset(); - store.openFile(); - if (store.length() == store.getFilePointer()) { + fileStorage.openFile(); + if (fileStorage.length() == fileStorage.getFilePointer()) { close(); return; } - store.readFully(page.getBytes(), 0, Constants.FILE_BLOCK_SIZE); + fileStorage.readFully(page.getBytes(), 0, Constants.FILE_BLOCK_SIZE); page.reset(); remainingInBuffer = page.readInt(); if (remainingInBuffer < 0) { @@ -112,7 +115,7 @@ private void fillBuffer() throws IOException { int len = page.length() - Constants.FILE_BLOCK_SIZE; page.reset(); page.readInt(); - store.readFully(page.getBytes(), Constants.FILE_BLOCK_SIZE, len); + fileStorage.readFully(page.getBytes(), Constants.FILE_BLOCK_SIZE, len); page.reset(); page.readInt(); if (compress != null) { @@ -125,25 +128,28 @@ private void fillBuffer() throws IOException { remainingInBuffer = uncompressed; } if (alwaysClose) { - store.closeFile(); + fileStorage.closeFile(); } } + @Override public void close() { - if (store != null) { + if (fileStorage != null) { try { - store.close(); + fileStorage.close(); endOfFile = true; } finally { - store = null; + fileStorage = null; } } } + @Override protected void finalize() { close(); } + @Override public int read() throws IOException { fillBuffer(); if (endOfFile) { diff --git a/lealone-common/src/main/java/org/lealone/storage/FileStoreOutputStream.java b/lealone-common/src/main/java/org/lealone/storage/FileStorageOutputStream.java similarity index 80% rename from lealone-common/src/main/java/org/lealone/storage/FileStoreOutputStream.java rename to lealone-common/src/main/java/org/lealone/storage/FileStorageOutputStream.java index c0094b65a..2fb9848aa 100644 --- a/lealone-common/src/main/java/org/lealone/storage/FileStoreOutputStream.java +++ b/lealone-common/src/main/java/org/lealone/storage/FileStorageOutputStream.java @@ -14,17 +14,17 @@ import org.lealone.db.DataHandler; /** - * An output stream that is backed by a file store. + * An output stream that is backed by a file storage. */ -public class FileStoreOutputStream extends OutputStream { - private FileStore store; +public class FileStorageOutputStream extends OutputStream { + private FileStorage fileStorage; private final Data page; private final String compressionAlgorithm; private final CompressTool compress; private final byte[] buffer = { 0 }; - public FileStoreOutputStream(FileStore store, DataHandler handler, String compressionAlgorithm) { - this.store = store; + public FileStorageOutputStream(FileStorage fileStorage, DataHandler handler, String compressionAlgorithm) { + this.fileStorage = fileStorage; if (compressionAlgorithm != null) { this.compress = CompressTool.getInstance(); this.compressionAlgorithm = compressionAlgorithm; @@ -35,15 +35,18 @@ public FileStoreOutputStream(FileStore store, DataHandler handler, String compre page = Data.create(handler, Constants.FILE_BLOCK_SIZE); } + @Override public void write(int b) { buffer[0] = (byte) b; write(buffer); } + @Override public void write(byte[] buff) { write(buff, 0, buff.length); } + @Override public void write(byte[] buff, int off, int len) { if (len > 0) { page.reset(); @@ -67,16 +70,17 @@ public void write(byte[] buff, int off, int len) { page.write(buff, off, len); } page.fillAligned(); - store.write(page.getBytes(), 0, page.length()); + fileStorage.write(page.getBytes(), 0, page.length()); } } + @Override public void close() { - if (store != null) { + if (fileStorage != null) { try { - store.close(); + fileStorage.close(); } finally { - store = null; + fileStorage = null; } } } diff --git a/lealone-common/src/main/java/org/lealone/storage/StorageBuilder.java b/lealone-common/src/main/java/org/lealone/storage/StorageBuilder.java index 83012eb9f..285c670c6 100644 --- a/lealone-common/src/main/java/org/lealone/storage/StorageBuilder.java +++ b/lealone-common/src/main/java/org/lealone/storage/StorageBuilder.java @@ -5,6 +5,7 @@ */ package org.lealone.storage; +import java.nio.file.FileStore; import java.util.HashMap; import org.lealone.common.util.DataUtils; @@ -194,20 +195,20 @@ public StorageBuilder backgroundExceptionHandler(Thread.UncaughtExceptionHandler } /** - * Use the provided file store instead of the default one. + * Use the provided file storage instead of the default one. *

- * File stores passed in this way need to be open. They are not closed - * when closing the store. + * File storages passed in this way need to be open. They are not closed + * when closing the storage. *

- * Please note that any kind of store (including an off-heap store) is - * considered a "persistence", while an "in-memory store" means objects + * Please note that any kind of storage (including an off-heap storage) is + * considered a "persistence", while an "in-memory storage" means objects * are not persisted and fully kept in the JVM heap. * - * @param store the file store + * @param fileStorage the file storage * @return this */ - public StorageBuilder fileStore(FileStore store) { - return set("fileStore", store); + public StorageBuilder fileStorage(FileStore fileStorage) { + return set("fileStorage", fileStorage); } /** diff --git a/lealone-common/src/main/java/org/lealone/storage/StorageEngineManager.java b/lealone-common/src/main/java/org/lealone/storage/StorageEngineManager.java index ffb5ea606..cf136cd7c 100644 --- a/lealone-common/src/main/java/org/lealone/storage/StorageEngineManager.java +++ b/lealone-common/src/main/java/org/lealone/storage/StorageEngineManager.java @@ -29,7 +29,5 @@ public static StorageEngineManager getInstance() { private StorageEngineManager() { super(StorageEngine.class); - - // new MemoryStorageEngine(); // 提前注册 } } diff --git a/lealone-storage/src/main/java/org/lealone/storage/cache/CacheLongKeyLIRS.java b/lealone-common/src/main/java/org/lealone/storage/cache/CacheLongKeyLIRS.java similarity index 97% rename from lealone-storage/src/main/java/org/lealone/storage/cache/CacheLongKeyLIRS.java rename to lealone-common/src/main/java/org/lealone/storage/cache/CacheLongKeyLIRS.java index 10f2c9e17..871b271af 100644 --- a/lealone-storage/src/main/java/org/lealone/storage/cache/CacheLongKeyLIRS.java +++ b/lealone-common/src/main/java/org/lealone/storage/cache/CacheLongKeyLIRS.java @@ -67,8 +67,7 @@ public class CacheLongKeyLIRS { public CacheLongKeyLIRS(Config config) { setMaxMemory(config.maxMemory); this.nonResidentQueueSize = config.nonResidentQueueSize; - DataUtils.checkArgument( - Integer.bitCount(config.segmentCount) == 1, + DataUtils.checkArgument(Integer.bitCount(config.segmentCount) == 1, "The segment count must be a power of 2, is {0}", config.segmentCount); this.segmentCount = config.segmentCount; this.segmentMask = segmentCount - 1; @@ -85,8 +84,7 @@ public CacheLongKeyLIRS(Config config) { public void clear() { long max = Math.max(1, maxMemory / segmentCount); for (int i = 0; i < segmentCount; i++) { - segments[i] = new Segment( - max, stackMoveDistance, 8, nonResidentQueueSize); + segments[i] = new Segment(max, stackMoveDistance, 8, nonResidentQueueSize); } } @@ -269,9 +267,7 @@ public long getUsedMemory() { * @param maxMemory the maximum size (1 or larger) in bytes */ public void setMaxMemory(long maxMemory) { - DataUtils.checkArgument( - maxMemory > 0, - "Max memory must be larger than 0, is {0}", maxMemory); + DataUtils.checkArgument(maxMemory > 0, "Max memory must be larger than 0, is {0}", maxMemory); this.maxMemory = maxMemory; if (segments != null) { long max = 1 + maxMemory / segments.length; @@ -298,7 +294,7 @@ public long getMaxMemory() { public synchronized Set> entrySet() { HashMap map = new HashMap(); for (long k : keySet()) { - map.put(k, find(k).value); + map.put(k, find(k).value); } return map.entrySet(); } @@ -581,8 +577,7 @@ private static class Segment { * @param len the number of hash table buckets (must be a power of 2) * @param nonResidentQueueSize the non-resident queue size factor */ - Segment(long maxMemory, int stackMoveDistance, int len, - int nonResidentQueueSize) { + Segment(long maxMemory, int stackMoveDistance, int len, int nonResidentQueueSize) { setMaxMemory(maxMemory); this.stackMoveDistance = stackMoveDistance; this.nonResidentQueueSize = nonResidentQueueSize; @@ -715,8 +710,7 @@ V get(long key, int hash) { } if (e.isHot()) { if (e != stack.stackNext) { - if (stackMoveDistance == 0 || - stackMoveCounter - e.topMove > stackMoveDistance) { + if (stackMoveDistance == 0 || stackMoveCounter - e.topMove > stackMoveDistance) { access(key, hash); } } @@ -740,8 +734,7 @@ private synchronized void access(long key, int hash) { } if (e.isHot()) { if (e != stack.stackNext) { - if (stackMoveDistance == 0 || - stackMoveCounter - e.topMove > stackMoveDistance) { + if (stackMoveDistance == 0 || stackMoveCounter - e.topMove > stackMoveDistance) { // move a hot entry to the top of the stack // unless it is already there boolean wasEnd = e == stack.stackPrev; @@ -787,8 +780,7 @@ private synchronized void access(long key, int hash) { */ synchronized V put(long key, int hash, V value, int memory) { if (value == null) { - throw DataUtils.newIllegalArgumentException( - "The value may not be null"); + throw DataUtils.newIllegalArgumentException("The value may not be null"); } V old; Entry e = find(key, hash); @@ -1029,13 +1021,11 @@ synchronized List keys(boolean cold, boolean nonResident) { ArrayList keys = new ArrayList(); if (cold) { Entry start = nonResident ? queue2 : queue; - for (Entry e = start.queueNext; e != start; - e = e.queueNext) { + for (Entry e = start.queueNext; e != start; e = e.queueNext) { keys.add(e.key); } } else { - for (Entry e = stack.stackNext; e != stack; - e = e.stackNext) { + for (Entry e = stack.stackNext; e != stack; e = e.stackNext) { keys.add(e.key); } } diff --git a/lealone-storage/src/main/java/org/lealone/storage/cache/FilePathCache.java b/lealone-common/src/main/java/org/lealone/storage/cache/FilePathCache.java similarity index 99% rename from lealone-storage/src/main/java/org/lealone/storage/cache/FilePathCache.java rename to lealone-common/src/main/java/org/lealone/storage/cache/FilePathCache.java index 108ca7e12..99a05aead 100644 --- a/lealone-storage/src/main/java/org/lealone/storage/cache/FilePathCache.java +++ b/lealone-common/src/main/java/org/lealone/storage/cache/FilePathCache.java @@ -154,8 +154,7 @@ public void force(boolean metaData) throws IOException { } @Override - public FileLock tryLock(long position, long size, boolean shared) - throws IOException { + public FileLock tryLock(long position, long size, boolean shared) throws IOException { return base.tryLock(position, size, shared); } diff --git a/lealone-db/src/main/java/org/lealone/db/Database.java b/lealone-db/src/main/java/org/lealone/db/Database.java index d46cf9881..c74de7685 100644 --- a/lealone-db/src/main/java/org/lealone/db/Database.java +++ b/lealone-db/src/main/java/org/lealone/db/Database.java @@ -57,7 +57,7 @@ import org.lealone.sql.SQLEngine; import org.lealone.sql.SQLEngineManager; import org.lealone.sql.SQLParser; -import org.lealone.storage.FileStore; +import org.lealone.storage.FileStorage; import org.lealone.storage.LobStorage; import org.lealone.storage.Storage; import org.lealone.storage.StorageBuilder; @@ -532,18 +532,18 @@ public Trace getTrace(int moduleId) { } @Override - public FileStore openFile(String name, String openMode, boolean mustExist) { + public FileStorage openFile(String name, String openMode, boolean mustExist) { if (mustExist && !FileUtils.exists(name)) { throw DbException.get(ErrorCode.FILE_NOT_FOUND_1, name); } - FileStore store = FileStore.open(this, name, openMode, cipher, filePasswordHash); + FileStorage fileStorage = FileStorage.open(this, name, openMode, cipher, filePasswordHash); try { - store.init(); + fileStorage.init(); } catch (DbException e) { - store.closeSilently(); + fileStorage.closeSilently(); throw e; } - return store; + return fileStorage; } /** diff --git a/lealone-db/src/main/java/org/lealone/db/result/ResultDiskBuffer.java b/lealone-db/src/main/java/org/lealone/db/result/ResultDiskBuffer.java index e9aa41815..9b2557b9f 100644 --- a/lealone-db/src/main/java/org/lealone/db/result/ResultDiskBuffer.java +++ b/lealone-db/src/main/java/org/lealone/db/result/ResultDiskBuffer.java @@ -16,7 +16,7 @@ import org.lealone.db.Data; import org.lealone.db.Database; import org.lealone.db.Session; -import org.lealone.storage.FileStore; +import org.lealone.storage.FileStorage; /** * This class implements the disk buffer for the LocalResult class. @@ -32,7 +32,7 @@ class ResultDiskBuffer implements ResultExternal { private final int columnCount; private final int maxBufferSize; - private FileStore file; + private FileStorage file; private int rowCount; private final ResultDiskBuffer parent; @@ -75,14 +75,14 @@ static class ResultDiskTape { String fileName = db.createTempFile(); file = db.openFile(fileName, "rw", false); file.setCheckedWriting(false); - file.seek(FileStore.HEADER_LENGTH); + file.seek(FileStorage.HEADER_LENGTH); if (sort != null) { tapes = New.arrayList(); mainTape = null; } else { tapes = null; mainTape = new ResultDiskTape(); - mainTape.pos = FileStore.HEADER_LENGTH; + mainTape.pos = FileStorage.HEADER_LENGTH; } this.maxBufferSize = db.getSettings().largeResultBufferSize; } @@ -104,7 +104,7 @@ private ResultDiskBuffer(ResultDiskBuffer parent) { } if (parent.mainTape != null) { mainTape = new ResultDiskTape(); - mainTape.pos = FileStore.HEADER_LENGTH; + mainTape.pos = FileStorage.HEADER_LENGTH; mainTape.start = parent.mainTape.start; mainTape.end = parent.mainTape.end; } else { @@ -172,7 +172,7 @@ public int addRows(ArrayList rows) { } public void done() { - file.seek(FileStore.HEADER_LENGTH); + file.seek(FileStorage.HEADER_LENGTH); file.autoDelete(); } @@ -183,7 +183,7 @@ public void reset() { tape.buffer = New.arrayList(); } } else { - mainTape.pos = FileStore.HEADER_LENGTH; + mainTape.pos = FileStorage.HEADER_LENGTH; mainTape.buffer = New.arrayList(); } } diff --git a/lealone-db/src/main/java/org/lealone/db/result/RowList.java b/lealone-db/src/main/java/org/lealone/db/result/RowList.java index dabc380a2..b07720cd2 100644 --- a/lealone-db/src/main/java/org/lealone/db/result/RowList.java +++ b/lealone-db/src/main/java/org/lealone/db/result/RowList.java @@ -14,7 +14,7 @@ import org.lealone.db.Data; import org.lealone.db.Database; import org.lealone.db.Session; -import org.lealone.storage.FileStore; +import org.lealone.storage.FileStorage; /** * A list of rows. If the list grows too large, it is buffered to disk @@ -26,7 +26,7 @@ public class RowList { private final ArrayList list = New.arrayList(); private int size; private int index, listIndex; - private FileStore file; + private FileStorage file; private Data rowBuff; private ArrayList lobs; private final int maxMemory; @@ -90,9 +90,9 @@ private void writeAllRows() { String fileName = db.createTempFile(); file = db.openFile(fileName, "rw", false); file.setCheckedWriting(false); - file.seek(FileStore.HEADER_LENGTH); + file.seek(FileStorage.HEADER_LENGTH); rowBuff = Data.create(db, Constants.DEFAULT_PAGE_SIZE); - file.seek(FileStore.HEADER_LENGTH); + file.seek(FileStorage.HEADER_LENGTH); } Data buff = rowBuff; initBuffer(buff); @@ -149,7 +149,7 @@ public void reset() { written = true; } list.clear(); - file.seek(FileStore.HEADER_LENGTH); + file.seek(FileStorage.HEADER_LENGTH); } } diff --git a/lealone-sql/src/main/java/org/lealone/sql/dml/ScriptBase.java b/lealone-sql/src/main/java/org/lealone/sql/dml/ScriptBase.java index bcdc3fa80..c67934c5c 100644 --- a/lealone-sql/src/main/java/org/lealone/sql/dml/ScriptBase.java +++ b/lealone-sql/src/main/java/org/lealone/sql/dml/ScriptBase.java @@ -27,9 +27,9 @@ import org.lealone.db.SysProperties; import org.lealone.sql.Prepared; import org.lealone.sql.expression.Expression; -import org.lealone.storage.FileStore; -import org.lealone.storage.FileStoreInputStream; -import org.lealone.storage.FileStoreOutputStream; +import org.lealone.storage.FileStorage; +import org.lealone.storage.FileStorageInputStream; +import org.lealone.storage.FileStorageOutputStream; import org.lealone.storage.LobStorage; import org.lealone.storage.fs.FileUtils; @@ -63,7 +63,7 @@ abstract class ScriptBase extends Prepared implements DataHandler { private String fileName; private String cipher; - private FileStore store; + private FileStorage fileStorage; private String compressionAlgorithm; ScriptBase(Session session) { @@ -120,9 +120,9 @@ private void initStore() { key = SHA256.getKeyPasswordHash("script", pass); } String file = getFileName(); - store = FileStore.open(db, file, "rw", cipher, key); - store.setCheckedWriting(false); - store.init(); + fileStorage = FileStorage.open(db, file, "rw", cipher, key); + fileStorage.setCheckedWriting(false); + fileStorage.init(); } /** @@ -135,7 +135,7 @@ void openOutput() { } if (isEncrypted()) { initStore(); - out = new FileStoreOutputStream(store, this, compressionAlgorithm); + out = new FileStorageOutputStream(fileStorage, this, compressionAlgorithm); // always use a big buffer, otherwise end-of-block is written a lot out = new BufferedOutputStream(out, Constants.IO_BUFFER_SIZE_COMPRESS); } else { @@ -160,7 +160,7 @@ void openInput() { } if (isEncrypted()) { initStore(); - in = new FileStoreInputStream(store, this, compressionAlgorithm != null, false); + in = new FileStorageInputStream(fileStorage, this, compressionAlgorithm != null, false); } else { InputStream inStream; try { @@ -184,9 +184,9 @@ void closeIO() { out = null; IOUtils.closeSilently(in); in = null; - if (store != null) { - store.closeSilently(); - store = null; + if (fileStorage != null) { + fileStorage.closeSilently(); + fileStorage = null; } } @@ -201,7 +201,7 @@ public String getDatabasePath() { } @Override - public FileStore openFile(String name, String mode, boolean mustExist) { + public FileStorage openFile(String name, String mode, boolean mustExist) { return null; } diff --git a/lealone-storage/src/main/java/org/lealone/storage/FileStorage.java b/lealone-storage/src/main/java/org/lealone/storage/FileStorage.java deleted file mode 100644 index 6cdb1d058..000000000 --- a/lealone-storage/src/main/java/org/lealone/storage/FileStorage.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, - * and the EPL 1.0 (http://h2database.com/html/license.html). - * Initial Developer: H2 Group - */ -package org.lealone.storage; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.OverlappingFileLockException; -import java.util.Map; - -import org.lealone.common.util.DataUtils; -import org.lealone.storage.cache.FilePathCache; -import org.lealone.storage.fs.FilePath; -import org.lealone.storage.fs.FilePathDisk; -import org.lealone.storage.fs.FilePathEncrypt; -import org.lealone.storage.fs.FilePathNio; -import org.lealone.storage.fs.FileUtils; - -/** - * The default storage mechanism of the AOStorage. This implementation persists - * data to a file. The file storage is responsible to persist data and for free - * space management. - * - * @author H2 Group - * @author zhh - */ -public class FileStorage { - - /** - * The number of read operations. - */ - protected long readCount; - - /** - * The number of read bytes. - */ - protected long readBytes; - - /** - * The number of write operations. - */ - protected long writeCount; - - /** - * The number of written bytes. - */ - protected long writeBytes; - - /** - * The file name. - */ - protected String fileName; - - /** - * Whether this storage is read-only. - */ - protected boolean readOnly; - - /** - * The file size (cached). - */ - protected long fileSize; - - /** - * The file. - */ - protected FileChannel file; - - /** - * The encrypted file (if encryption is used). - */ - protected FileChannel encryptedFile; - - /** - * The file lock. - */ - protected FileLock fileLock; - - @Override - public String toString() { - return fileName; - } - - /** - * Read from the file. - * - * @param pos the write position - * @param len the number of bytes to read - * @return the byte buffer - */ - public ByteBuffer readFully(long pos, int len) { - ByteBuffer dst = ByteBuffer.allocate(len); - DataUtils.readFully(file, pos, dst); - readCount++; - readBytes += len; - return dst; - } - - /** - * Write to the file. - * - * @param pos the write position - * @param src the source buffer - */ - public void writeFully(long pos, ByteBuffer src) { - int len = src.remaining(); - fileSize = Math.max(fileSize, pos + len); - DataUtils.writeFully(file, pos, src); - writeCount++; - writeBytes += len; - } - - /** - * Try to open the file. - * - * @param fileName the file name - * @param readOnly whether the file should only be opened in read-only mode, - * even if the file is writable - * @param encryptionKey the encryption key, or null if encryption is not - * used - */ - public void open(String fileName, Map config) { - if (file != null) { - return; - } - char[] encryptionKey = (char[]) config.get("encryptionKey"); - boolean readOnly = config.containsKey("readOnly"); - - if (fileName != null) { - FilePath p = FilePath.get(fileName); - // if no explicit scheme was specified, NIO is used - if (p instanceof FilePathDisk && !fileName.startsWith(p.getScheme() + ":")) { - // ensure the NIO file system is registered - FilePathNio.class.getName(); - fileName = "nio:" + fileName; - } - } - this.fileName = fileName; - FilePath f = FilePath.get(fileName); - FilePath parent = f.getParent(); - if (parent != null && !parent.exists()) { - throw DataUtils.newIllegalArgumentException("Directory does not exist: {0}", parent); - } - if (f.exists() && !f.canWrite()) { - readOnly = true; - } - this.readOnly = readOnly; - try { - file = f.open(readOnly ? "r" : "rw"); - if (encryptionKey != null) { - byte[] key = FilePathEncrypt.getPasswordBytes(encryptionKey); - encryptedFile = file; - file = new FilePathEncrypt.FileEncrypt(fileName, key, file); - } - file = FilePathCache.wrap(file); - try { - if (readOnly) { - fileLock = file.tryLock(0, Long.MAX_VALUE, true); - } else { - fileLock = file.tryLock(); - } - } catch (OverlappingFileLockException e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", - fileName, e); - } - if (fileLock == null) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", - fileName); - } - fileSize = file.size(); - } catch (IOException e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_READING_FAILED, "Could not open file {0}", - fileName, e); - } - } - - /** - * Close this storage. - */ - public void close() { - try { - if (fileLock != null) { - fileLock.release(); - fileLock = null; - } - if (file != null) - file.close(); - } catch (Exception e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, "Closing failed for file {0}", - fileName, e); - } finally { - file = null; - } - } - - /** - * Flush all changes. - */ - public void sync() { - try { - file.force(true); - } catch (IOException e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, "Could not sync file {0}", - fileName, e); - } - } - - /** - * Get the file size. - * - * @return the file size - */ - public long size() { - return fileSize; - } - - /** - * Truncate the file. - * - * @param size the new file size - */ - public void truncate(long size) { - try { - writeCount++; - file.truncate(size); - fileSize = Math.min(fileSize, size); - } catch (IOException e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, - "Could not truncate file {0} to size {1}", fileName, size, e); - } - } - - /** - * Get the file instance in use. - *

- * The application may read from the file (for example for online backup), - * but not write to it or truncate it. - * - * @return the file - */ - public FileChannel getFile() { - return file; - } - - /** - * Get the encrypted file instance, if encryption is used. - *

- * The application may read from the file (for example for online backup), - * but not write to it or truncate it. - * - * @return the encrypted file, or null if encryption is not used - */ - public FileChannel getEncryptedFile() { - return encryptedFile; - } - - /** - * Get the number of write operations since this storage was opened. - * For file based storages, this is the number of file write operations. - * - * @return the number of write operations - */ - public long getWriteCount() { - return writeCount; - } - - /** - * Get the number of written bytes since this storage was opened. - * - * @return the number of write operations - */ - public long getWriteBytes() { - return writeBytes; - } - - /** - * Get the number of read operations since this storage was opened. - * For file based storages, this is the number of file read operations. - * - * @return the number of read operations - */ - public long getReadCount() { - return readCount; - } - - /** - * Get the number of read bytes since this storage was opened. - * - * @return the number of write operations - */ - public long getReadBytes() { - return readBytes; - } - - public boolean isReadOnly() { - return readOnly; - } - - /** - * Get the file name. - * - * @return the file name - */ - public String getFileName() { - return fileName; - } - - public void delete() { - FileUtils.delete(fileName); - } -} diff --git a/lealone-transaction/src/main/java/org/lealone/transaction/log/LogChunkMap.java b/lealone-transaction/src/main/java/org/lealone/transaction/log/LogChunkMap.java index 064876491..4d1b401da 100644 --- a/lealone-transaction/src/main/java/org/lealone/transaction/log/LogChunkMap.java +++ b/lealone-transaction/src/main/java/org/lealone/transaction/log/LogChunkMap.java @@ -23,6 +23,7 @@ import java.util.Map.Entry; import java.util.Set; +import org.lealone.storage.FileStorage; import org.lealone.storage.memory.MemoryMap; import org.lealone.storage.type.DataType; import org.lealone.storage.type.WriteBuffer; @@ -55,7 +56,7 @@ private static void releaseWriteBuffer(WriteBuffer buff) { } } - protected final LogFileStorage fileStorage; + protected final FileStorage fileStorage; private long pos; private volatile K lastKey; @@ -65,7 +66,7 @@ public LogChunkMap(int id, String name, DataType keyType, DataType valueType, Ma String storageName = (String) config.get("storageName"); name = storageName + File.separator + name + LogStorage.MAP_NAME_ID_SEPARATOR + id; - fileStorage = new LogFileStorage(); + fileStorage = new FileStorage(); fileStorage.open(name, config); pos = fileStorage.size(); if (pos > 0) diff --git a/lealone-transaction/src/main/java/org/lealone/transaction/log/LogFileStorage.java b/lealone-transaction/src/main/java/org/lealone/transaction/log/LogFileStorage.java deleted file mode 100644 index 9ecc31825..000000000 --- a/lealone-transaction/src/main/java/org/lealone/transaction/log/LogFileStorage.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, - * and the EPL 1.0 (http://h2database.com/html/license.html). - * Initial Developer: H2 Group - */ -package org.lealone.transaction.log; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.OverlappingFileLockException; -import java.util.Map; - -import org.lealone.common.util.DataUtils; -import org.lealone.storage.fs.FilePath; -import org.lealone.storage.fs.FilePathDisk; -import org.lealone.storage.fs.FilePathEncrypt; -import org.lealone.storage.fs.FilePathNio; -import org.lealone.storage.fs.FileUtils; - -/** - * The default storage mechanism of the AOStorage. This implementation persists - * data to a file. The file storage is responsible to persist data and for free - * space management. - * - * @author H2 Group - * @author zhh - */ -public class LogFileStorage { - - /** - * The number of read operations. - */ - protected long readCount; - - /** - * The number of read bytes. - */ - protected long readBytes; - - /** - * The number of write operations. - */ - protected long writeCount; - - /** - * The number of written bytes. - */ - protected long writeBytes; - - /** - * The file name. - */ - protected String fileName; - - /** - * Whether this storage is read-only. - */ - protected boolean readOnly; - - /** - * The file size (cached). - */ - protected long fileSize; - - /** - * The file. - */ - protected FileChannel file; - - /** - * The encrypted file (if encryption is used). - */ - protected FileChannel encryptedFile; - - /** - * The file lock. - */ - protected FileLock fileLock; - - @Override - public String toString() { - return fileName; - } - - /** - * Read from the file. - * - * @param pos the write position - * @param len the number of bytes to read - * @return the byte buffer - */ - public ByteBuffer readFully(long pos, int len) { - ByteBuffer dst = ByteBuffer.allocate(len); - DataUtils.readFully(file, pos, dst); - readCount++; - readBytes += len; - return dst; - } - - /** - * Write to the file. - * - * @param pos the write position - * @param src the source buffer - */ - public void writeFully(long pos, ByteBuffer src) { - int len = src.remaining(); - fileSize = Math.max(fileSize, pos + len); - DataUtils.writeFully(file, pos, src); - writeCount++; - writeBytes += len; - } - - /** - * Try to open the file. - * - * @param fileName the file name - * @param readOnly whether the file should only be opened in read-only mode, - * even if the file is writable - * @param encryptionKey the encryption key, or null if encryption is not - * used - */ - public void open(String fileName, Map config) { - if (file != null) { - return; - } - char[] encryptionKey = (char[]) config.get("encryptionKey"); - boolean readOnly = config.containsKey("readOnly"); - - if (fileName != null) { - FilePath p = FilePath.get(fileName); - // if no explicit scheme was specified, NIO is used - if (p instanceof FilePathDisk && !fileName.startsWith(p.getScheme() + ":")) { - // ensure the NIO file system is registered - FilePathNio.class.getName(); - fileName = "nio:" + fileName; - } - } - this.fileName = fileName; - FilePath f = FilePath.get(fileName); - FilePath parent = f.getParent(); - if (parent != null && !parent.exists()) { - throw DataUtils.newIllegalArgumentException("Directory does not exist: {0}", parent); - } - if (f.exists() && !f.canWrite()) { - readOnly = true; - } - this.readOnly = readOnly; - try { - file = f.open(readOnly ? "r" : "rw"); - if (encryptionKey != null) { - byte[] key = FilePathEncrypt.getPasswordBytes(encryptionKey); - encryptedFile = file; - file = new FilePathEncrypt.FileEncrypt(fileName, key, file); - } - try { - if (readOnly) { - fileLock = file.tryLock(0, Long.MAX_VALUE, true); - } else { - fileLock = file.tryLock(); - } - } catch (OverlappingFileLockException e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", - fileName, e); - } - if (fileLock == null) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", - fileName); - } - fileSize = file.size(); - } catch (IOException e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_READING_FAILED, "Could not open file {0}", - fileName, e); - } - } - - /** - * Close this storage. - */ - public void close() { - try { - if (fileLock != null) { - fileLock.release(); - fileLock = null; - } - if (file != null) - file.close(); - } catch (Exception e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, "Closing failed for file {0}", - fileName, e); - } finally { - file = null; - } - } - - /** - * Flush all changes. - */ - public void sync() { - try { - file.force(true); - } catch (IOException e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, "Could not sync file {0}", - fileName, e); - } - } - - /** - * Get the file size. - * - * @return the file size - */ - public long size() { - return fileSize; - } - - /** - * Truncate the file. - * - * @param size the new file size - */ - public void truncate(long size) { - try { - writeCount++; - file.truncate(size); - fileSize = Math.min(fileSize, size); - } catch (IOException e) { - throw DataUtils.newIllegalStateException(DataUtils.ERROR_WRITING_FAILED, - "Could not truncate file {0} to size {1}", fileName, size, e); - } - } - - /** - * Get the file instance in use. - *

- * The application may read from the file (for example for online backup), - * but not write to it or truncate it. - * - * @return the file - */ - public FileChannel getFile() { - return file; - } - - /** - * Get the encrypted file instance, if encryption is used. - *

- * The application may read from the file (for example for online backup), - * but not write to it or truncate it. - * - * @return the encrypted file, or null if encryption is not used - */ - public FileChannel getEncryptedFile() { - return encryptedFile; - } - - /** - * Get the number of write operations since this storage was opened. - * For file based storages, this is the number of file write operations. - * - * @return the number of write operations - */ - public long getWriteCount() { - return writeCount; - } - - /** - * Get the number of written bytes since this storage was opened. - * - * @return the number of write operations - */ - public long getWriteBytes() { - return writeBytes; - } - - /** - * Get the number of read operations since this storage was opened. - * For file based storages, this is the number of file read operations. - * - * @return the number of read operations - */ - public long getReadCount() { - return readCount; - } - - /** - * Get the number of read bytes since this storage was opened. - * - * @return the number of write operations - */ - public long getReadBytes() { - return readBytes; - } - - public boolean isReadOnly() { - return readOnly; - } - - /** - * Get the file name. - * - * @return the file name - */ - public String getFileName() { - return fileName; - } - - public void delete() { - FileUtils.delete(fileName); - } -}