提交 5c412dbc authored 作者: Thomas Mueller's avatar Thomas Mueller

Improved compatibility with the Java 7 FileSystem abstraction.

上级 ab2f646c
......@@ -18,8 +18,8 @@ import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.message.DbException;
import org.h2.security.SHA256;
import org.h2.store.fs.FilePathRec;
import org.h2.store.fs.FileUtils;
import org.h2.store.fs.RecordingFileSystem;
import org.h2.util.New;
import org.h2.util.SortedProperties;
import org.h2.util.StringUtils;
......@@ -81,13 +81,13 @@ public class ConnectionInfo implements Cloneable {
parseName();
String recoverTest = removeProperty("RECOVER_TEST", null);
if (recoverTest != null) {
RecordingFileSystem.register();
FilePathRec.register();
try {
Utils.callStaticMethod("org.h2.store.RecoverTester.init", recoverTest);
} catch (Exception e) {
throw DbException.convert(e);
}
name = RecordingFileSystem.PREFIX + name;
name = "rec:" + name;
}
}
......
......@@ -43,7 +43,6 @@ import org.h2.store.InDoubtTransaction;
import org.h2.store.LobStorage;
import org.h2.store.PageStore;
import org.h2.store.WriterThread;
import org.h2.store.fs.FileSystemMemory;
import org.h2.store.fs.FileUtils;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
......@@ -1468,7 +1467,7 @@ public class Database implements DataHandler {
boolean inTempDir = readOnly;
String name = databaseName;
if (!persistent) {
name = FileSystemMemory.PREFIX + name;
name = "memFS:" + name;
}
return FileUtils.createTempFile(name, Constants.SUFFIX_TEMP_FILE, true, inTempDir);
} catch (IOException e) {
......
......@@ -17,9 +17,9 @@ import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.store.fs.FilePathRec;
import org.h2.store.fs.FileUtils;
import org.h2.store.fs.Recorder;
import org.h2.store.fs.RecordingFileSystem;
import org.h2.tools.Recover;
import org.h2.util.IOUtils;
import org.h2.util.New;
......@@ -52,7 +52,7 @@ public class RecoverTester implements Recorder {
if (StringUtils.isNumber(recoverTest)) {
tester.setTestEvery(Integer.parseInt(recoverTest));
}
RecordingFileSystem.setRecorder(tester);
FilePathRec.setRecorder(tester);
}
public static synchronized RecoverTester getInstance() {
......
......@@ -11,13 +11,13 @@ import java.io.IOException;
/**
* This class represents an in-memory file.
*/
public class FileObjectMemory implements FileObject {
public class FileObjectMem implements FileObject {
private final FileObjectMemoryData data;
private final FileObjectMemData data;
private final boolean readOnly;
private long pos;
FileObjectMemory(FileObjectMemoryData data, boolean readOnly) {
FileObjectMem(FileObjectMemData data, boolean readOnly) {
this.data = data;
this.readOnly = readOnly;
}
......
......@@ -17,7 +17,7 @@ import org.h2.util.MathUtils;
* This class contains the data of an in-memory random access file.
* Data compression using the LZF algorithm is supported as well.
*/
class FileObjectMemoryData {
class FileObjectMemData {
private static final int CACHE_SIZE = 8;
private static final int BLOCK_SIZE_SHIFT = 10;
......@@ -96,7 +96,7 @@ class FileObjectMemoryData {
}
}
FileObjectMemoryData(String name, boolean compress) {
FileObjectMemData(String name, boolean compress) {
this.name = name;
this.compress = compress;
data = new byte[0][];
......
......@@ -17,7 +17,7 @@ import java.nio.channels.NonWritableChannelException;
/**
* File which uses NIO FileChannel.
*/
public class FileObjectDiskChannel implements FileObject {
public class FileObjectNio implements FileObject {
private final String name;
private final RandomAccessFile file;
......@@ -25,7 +25,7 @@ public class FileObjectDiskChannel implements FileObject {
private FileLock lock;
private long length;
FileObjectDiskChannel(String fileName, String mode) throws IOException {
FileObjectNio(String fileName, String mode) throws IOException {
this.name = fileName;
file = new RandomAccessFile(fileName, mode);
channel = file.getChannel();
......
......@@ -21,7 +21,7 @@ import org.h2.constant.SysProperties;
* FileObject which is using NIO MappedByteBuffer mapped to memory from file.
* The file size is limited to 2 GB.
*/
public class FileObjectDiskMapped implements FileObject {
public class FileObjectNioMapped implements FileObject {
private static final long GC_TIMEOUT_MS = 10000;
private final String name;
......@@ -36,7 +36,7 @@ public class FileObjectDiskMapped implements FileObject {
*/
private int pos;
FileObjectDiskMapped(String fileName, String mode) throws IOException {
FileObjectNioMapped(String fileName, String mode) throws IOException {
if ("r".equals(mode)) {
this.mode = MapMode.READ_ONLY;
} else {
......@@ -133,7 +133,7 @@ public class FileObjectDiskMapped implements FileObject {
}
public String toString() {
return FileSystemDiskNioMapped.PREFIX + name;
return "nioMapped:" + name;
}
public synchronized long size() throws IOException {
......
......@@ -15,15 +15,15 @@ import org.h2.message.DbException;
*/
public class FileObjectSplit implements FileObject {
private final String name;
private final FilePathSplit file;
private final String mode;
private final long maxLength;
private FileObject[] list;
private long filePointer;
private long length;
FileObjectSplit(String name, String mode, FileObject[] list, long length, long maxLength) {
this.name = name;
FileObjectSplit(FilePathSplit file, String mode, FileObject[] list, long length, long maxLength) {
this.file = file;
this.mode = mode;
this.list = list;
this.length = length;
......@@ -78,8 +78,8 @@ public class FileObjectSplit implements FileObject {
int i = list.length;
FileObject[] newList = new FileObject[i + 1];
System.arraycopy(list, 0, newList, 0, i);
String fileName = FileSystemSplit.getFileName(name, i);
newList[i] = FileSystem.getInstance(fileName).openFileObject(fileName, mode);
FilePath f = file.getBase(i);
newList[i] = f.openFileObject(mode);
list = newList;
}
return list[id];
......@@ -100,7 +100,7 @@ public class FileObjectSplit implements FileObject {
list[i].truncate(0);
list[i].close();
try {
FileUtils.delete(FileSystemSplit.getFileName(name, i));
file.getBase(i).delete();
} catch (DbException e) {
throw DbException.convertToIOException(e);
}
......
......@@ -36,7 +36,7 @@ public class FilePathDisk extends FilePath {
public FilePathDisk getPath(String path) {
FilePathDisk p = new FilePathDisk();
p.name = path;
p.name = translateFileName(path);
return p;
}
......@@ -135,8 +135,6 @@ public class FilePathDisk extends FilePath {
}
public void delete() {
System.out.println("delete " + name);
// if (true)return;
File file = new File(name);
for (int i = 0; i < SysProperties.MAX_FILE_RETRY; i++) {
IOUtils.trace("delete", name, null);
......@@ -179,16 +177,11 @@ public class FilePathDisk extends FilePath {
}
public FilePathDisk getCanonicalPath() {
return getPath(getCanonicalPath(name));
}
private static String getCanonicalPath(String fileName) {
fileName = translateFileName(fileName);
File f = new File(fileName);
try {
return f.getCanonicalPath();
String fileName = new File(name).getCanonicalPath();
return getPath(fileName);
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
throw DbException.convertIOException(e, name);
}
}
......@@ -253,11 +246,6 @@ public class FilePathDisk extends FilePath {
}
}
public String getName(String path) {
path = translateFileName(path);
return new File(path).getName();
}
public boolean fileStartsWith(String prefix) {
prefix = translateFileName(prefix);
String fileName = name;
......@@ -364,16 +352,15 @@ public class FilePathDisk extends FilePath {
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
String fileName = translateFileName(name);
fileName += ".";
String fileName = name + ".";
String prefix = new File(fileName).getName();
File dir;
if (inTempDir) {
dir = new File(Utils.getProperty("java.io.tmpdir", "."));
} else {
dir = new File(fileName).getAbsoluteFile().getParentFile();
FileUtils.createDirectories(dir.getAbsolutePath());
}
FileUtils.createDirectories(dir.getAbsolutePath());
while (true) {
File f = new File(dir, prefix + getNextTempFileNamePart(false) + suffix);
if (f.exists() || !f.createNewFile()) {
......
......@@ -21,7 +21,7 @@ import org.h2.util.New;
*/
public class FilePathMem extends FilePath {
private static final TreeMap<String, FileObjectMemoryData> MEMORY_FILES = new TreeMap<String, FileObjectMemoryData>();
private static final TreeMap<String, FileObjectMemData> MEMORY_FILES = new TreeMap<String, FileObjectMemData>();
public FilePathMem getPath(String path) {
FilePathMem p = new FilePathMem();
......@@ -35,7 +35,7 @@ public class FilePathMem extends FilePath {
public void moveTo(FilePath newName) {
synchronized (MEMORY_FILES) {
FileObjectMemoryData f = getMemoryFile();
FileObjectMemData f = getMemoryFile();
f.setName(newName.name);
MEMORY_FILES.remove(name);
MEMORY_FILES.put(newName.name, f);
......@@ -127,8 +127,8 @@ public class FilePathMem extends FilePath {
public OutputStream newOutputStream(boolean append) {
try {
FileObjectMemoryData obj = getMemoryFile();
FileObjectMemory m = new FileObjectMemory(obj, false);
FileObjectMemData obj = getMemoryFile();
FileObjectMem m = new FileObjectMem(obj, false);
return new FileObjectOutputStream(m, append);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
......@@ -136,21 +136,21 @@ public class FilePathMem extends FilePath {
}
public InputStream newInputStream() {
FileObjectMemoryData obj = getMemoryFile();
FileObjectMemory m = new FileObjectMemory(obj, true);
FileObjectMemData obj = getMemoryFile();
FileObjectMem m = new FileObjectMem(obj, true);
return new FileObjectInputStream(m);
}
public FileObject openFileObject(String mode) {
FileObjectMemoryData obj = getMemoryFile();
return new FileObjectMemory(obj, "r".equals(mode));
FileObjectMemData obj = getMemoryFile();
return new FileObjectMem(obj, "r".equals(mode));
}
private FileObjectMemoryData getMemoryFile() {
private FileObjectMemData getMemoryFile() {
synchronized (MEMORY_FILES) {
FileObjectMemoryData m = MEMORY_FILES.get(name);
FileObjectMemData m = MEMORY_FILES.get(name);
if (m == null) {
m = new FileObjectMemoryData(name, compressed());
m = new FileObjectMemData(name, compressed());
MEMORY_FILES.put(name, m);
}
return m;
......
......@@ -23,7 +23,7 @@ public class FilePathNio extends FilePathWrapper {
* @throws IOException if opening fails
*/
protected FileObject open(String fileName, String mode) throws IOException {
return new FileObjectDiskChannel(fileName, mode);
return new FileObjectNio(fileName, mode);
}
/**
......
......@@ -15,7 +15,7 @@ import java.io.IOException;
public class FilePathNioMapped extends FilePathNio {
protected FileObject open(String mode) throws IOException {
return new FileObjectDiskMapped(name, mode);
return new FileObjectNioMapped(name, mode);
}
/**
......
......@@ -150,7 +150,7 @@ public class FilePathSplit extends FilePathWrapper {
closeAndThrow(array.length - 1, array, o, maxLength);
}
}
FileObjectSplit fo = new FileObjectSplit(name, mode, array, length, maxLength);
FileObjectSplit fo = new FileObjectSplit(this, mode, array, length, maxLength);
return fo;
}
......@@ -218,7 +218,7 @@ public class FilePathSplit extends FilePathWrapper {
* @param id the part id
* @return the file name including the part id
*/
private FilePath getBase(int id) {
FilePath getBase(int id) {
return FilePath.get(getName(id));
}
......
......@@ -25,7 +25,7 @@ public abstract class FilePathWrapper extends FilePath {
}
public FilePathWrapper wrap(FilePath base) {
return create(getPrefix() + base.name, base);
return base == null ? null : create(getPrefix() + base.name, base);
}
public FilePath unwrap() {
......
......@@ -37,13 +37,6 @@ public class FilePathZip extends FilePath {
throw DbException.getUnsupportedException("write");
}
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
if (!inTempDir) {
throw new IOException("File system is read-only");
}
return FileSystemDisk.getInstance().createTempFile(prefix, suffix, deleteOnExit, true);
}
public void delete() {
throw DbException.getUnsupportedException("write");
}
......@@ -65,18 +58,6 @@ public class FilePathZip extends FilePath {
return fileName.startsWith(prefix);
}
// public String _getName(String name) {
// name = getEntryName(name);
// if (name.endsWith("/")) {
// name = name.substring(0, name.length() - 1);
// }
// int idx = name.lastIndexOf('/');
// if (idx >= 0) {
// name = name.substring(idx + 1);
// }
// return name;
// }
public long lastModified() {
return 0;
}
......@@ -198,7 +179,7 @@ public class FilePathZip extends FilePath {
if (idx >= 0) {
fileName = fileName.substring(0, idx);
}
return FileSystemDisk.expandUserHomeDirectory(fileName);
return FilePathDisk.expandUserHomeDirectory(fileName);
}
public FilePath getCanonicalPath() {
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
/**
* The file system is a storage abstraction.
*/
public abstract class FileSystem {
/**
* The prefix for temporary files. See also TestClearReferences.
*/
private static String tempRandom;
private static long tempSequence;
private static boolean defaultServicesRegistered;
private static final ArrayList<FileSystem> SERVICES = New.arrayList();
/**
* Get the file system object.
*
* @param fileName the file name or prefix
* @return the file system
*/
public static FileSystem getInstance(String fileName) {
if (fileName.indexOf(':') >= 0) {
if (FileSystemMemory.getInstance().accepts(fileName)) {
return FileSystemMemory.getInstance();
}
registerDefaultServices();
for (FileSystem fs : SERVICES) {
if (fs.accepts(fileName)) {
return fs;
}
}
}
return FileSystemDisk.getInstance();
}
private static synchronized void registerDefaultServices() {
if (!defaultServicesRegistered) {
defaultServicesRegistered = true;
for (String c : new String[] {
"org.h2.store.fs.FileSystemZip",
"org.h2.store.fs.FileSystemSplit",
"org.h2.store.fs.FileSystemDiskNio",
"org.h2.store.fs.FileSystemDiskNioMapped"
}) {
try {
Class.forName(c);
} catch (Exception e) {
// ignore - the files may be excluded in purpose
}
}
}
}
/**
* Register a file system.
*
* @param service the file system
*/
public static synchronized void register(FileSystem service) {
registerDefaultServices();
SERVICES.add(service);
}
/**
* Unregister a file system.
*
* @param service the file system
*/
public static synchronized void unregister(FileSystem service) {
SERVICES.remove(service);
}
/**
* Check if the file system is responsible for this file name.
*
* @param fileName the file name
* @return true if it is
*/
protected abstract boolean accepts(String fileName);
/**
* Get the size of a file in bytes
*
* @param fileName the file name
* @return the size in bytes
*/
public abstract long size(String fileName);
/**
* Rename a file if this is allowed.
*
* @param oldName the old fully qualified file name
* @param newName the new fully qualified file name
*/
public abstract void moveTo(String oldName, String newName);
/**
* Create a new file.
*
* @param fileName the file name
* @return true if creating was successful
*/
public abstract boolean createFile(String fileName);
/**
* Checks if a file exists.
*
* @param fileName the file name
* @return true if it exists
*/
public abstract boolean exists(String fileName);
/**
* Delete a file or directory if it exists.
* Directories may only be deleted if they are empty.
*
* @param path the file or directory name
*/
public abstract void delete(String path);
/**
* List the files in the given directory.
*
* @param directory the directory
* @return the list of fully qualified file names
*/
public abstract String[] listFiles(String directory);
/**
* Normalize a file name.
*
* @param fileName the file name
* @return the normalized file name
*/
public abstract String getCanonicalPath(String fileName);
/**
* Get the parent directory of a file or directory.
*
* @param fileName the file or directory name
* @return the parent directory name
*/
public abstract String getParent(String fileName);
/**
* Check if it is a file or a directory.
*
* @param fileName the file or directory name
* @return true if it is a directory
*/
public abstract boolean isDirectory(String fileName);
/**
* Check if the file name includes a path.
*
* @param fileName the file name
* @return if the file name is absolute
*/
public abstract boolean isAbsolute(String fileName);
/**
* Get the last modified date of a file
*
* @param fileName the file name
* @return the last modified date
*/
public abstract long lastModified(String fileName);
/**
* Check if the file is writable.
*
* @param fileName the file name
* @return if the file is writable
*/
public abstract boolean canWrite(String fileName);
/**
* Create a directory (all required parent directories already exist).
*
* @param directoryName the directory name
*/
public abstract void createDirectory(String directoryName);
/**
* Get the file or directory name (the last element of the path).
*
* @param path the directory and file name
* @return just the file name
*/
public abstract String getName(String path);
/**
* Check if a file starts with a given prefix.
*
* @param fileName the complete file name
* @param prefix the prefix
* @return true if it starts with the prefix
*/
public abstract boolean fileStartsWith(String fileName, String prefix);
/**
* Create an output stream to write into the file.
*
* @param fileName the file name
* @param append if true, the file will grow, if false, the file will be
* truncated first
* @return the output stream
*/
public abstract OutputStream newOutputStream(String fileName, boolean append);
/**
* Open a random access file object.
*
* @param fileName the file name
* @param mode the access mode. Supported are r, rw, rws, rwd
* @return the file object
*/
public abstract FileObject openFileObject(String fileName, String mode) throws IOException;
/**
* Create an input stream to read from the file.
*
* @param fileName the file name
* @return the input stream
*/
public abstract InputStream newInputStream(String fileName) throws IOException;
/**
* Disable the ability to write.
*
* @param fileName the file name
* @return true if the call was successful
*/
public abstract boolean setReadOnly(String fileName);
/**
* Get the next temporary file name part (the part in the middle).
*
* @param newRandom if the random part of the filename should change
* @return the file name part
*/
protected static synchronized String getNextTempFileNamePart(boolean newRandom) {
if (newRandom || tempRandom == null) {
byte[] prefix = new byte[8];
MathUtils.randomBytes(prefix);
tempRandom = StringUtils.convertBytesToHex(prefix) + ".";
}
return tempRandom + tempSequence++;
}
/**
* Create a new temporary file.
*
* @param prefix the prefix of the file name (including directory name if
* required)
* @param suffix the suffix
* @param deleteOnExit if the file should be deleted when the virtual
* machine exists
* @param inTempDir if the file should be stored in the temporary directory
* @return the name of the created file
*/
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
while (true) {
String n = prefix + getNextTempFileNamePart(false) + suffix;
if (exists(n)) {
// in theory, the random number could collide
getNextTempFileNamePart(true);
}
// creates the file (not thread safe)
openFileObject(n, "rw").close();
return n;
}
}
/**
* Get the unwrapped file name (without wrapper prefixes if wrapping /
* delegating file systems are used).
*
* @param fileName the file name
* @return the unwrapped
*/
public abstract String unwrap(String fileName);
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: Jan Kotek
*/
package org.h2.store.fs;
import java.io.IOException;
import org.h2.util.IOUtils;
/**
* This file system stores files on disk and uses java.nio to access the files.
* This class uses FileChannel.
*/
public class FileSystemDiskNio extends FileSystemWrapper {
/**
* The prefix for the file system that uses java.nio.channels.FileChannel.
*/
static final String PREFIX = "nio:";
static {
FileSystem.register(new FileSystemDiskNio());
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
fileName = unwrap(fileName);
FileObject f;
try {
f = open(fileName, mode);
IOUtils.trace("openFileObject", fileName, f);
} catch (IOException e) {
FileSystemDisk.freeMemoryAndFinalize();
try {
f = open(fileName, mode);
} catch (IOException e2) {
throw e;
}
}
return f;
}
/**
* Get the prefix for this file system.
*
* @return the prefix
*/
protected String getPrefix() {
return PREFIX;
}
/**
* Try to open a file with this name and mode.
*
* @param fileName the file name
* @param mode the open mode
* @return the file object
* @throws IOException if opening fails
*/
protected FileObject open(String fileName, String mode) throws IOException {
return new FileObjectDiskChannel(fileName, mode);
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: Jan Kotek
*/
package org.h2.store.fs;
import java.io.IOException;
/**
* This file system stores files on disk and uses java.nio to access the files.
* This class used memory mapped files.
*/
public class FileSystemDiskNioMapped extends FileSystemDiskNio {
/**
* The prefix for the file system that uses memory mapped files.
*/
static final String PREFIX = "nioMapped:";
static {
FileSystem.register(new FileSystemDiskNioMapped());
}
protected String getPrefix() {
return PREFIX;
}
protected FileObject open(String fileName, String mode) throws IOException {
return new FileObjectDiskMapped(fileName, mode);
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.TreeMap;
import org.h2.message.DbException;
import org.h2.util.New;
/**
* This file system keeps files fully in memory.
* There is an option to compress file blocks to safe memory.
*/
public class FileSystemMemory extends FileSystem {
/**
* The prefix used for an in-memory file system.
*/
public static final String PREFIX = "memFS:";
/**
* The prefix used for a compressed in-memory file system.
*/
public static final String PREFIX_LZF = "memLZF:";
private static final FileSystemMemory INSTANCE = new FileSystemMemory();
private static final TreeMap<String, FileObjectMemoryData> MEMORY_FILES = new TreeMap<String, FileObjectMemoryData>();
private FileSystemMemory() {
// don't allow construction
}
public static FileSystemMemory getInstance() {
return INSTANCE;
}
public long size(String fileName) {
return getMemoryFile(fileName).length();
}
public void moveTo(String oldName, String newName) {
oldName = getCanonicalPath(oldName);
newName = getCanonicalPath(newName);
synchronized (MEMORY_FILES) {
FileObjectMemoryData f = getMemoryFile(oldName);
f.setName(newName);
MEMORY_FILES.remove(oldName);
MEMORY_FILES.put(newName, f);
}
}
public boolean createFile(String fileName) {
synchronized (MEMORY_FILES) {
if (exists(fileName)) {
return false;
}
getMemoryFile(fileName);
}
return true;
}
public boolean exists(String fileName) {
fileName = getCanonicalPath(fileName);
if (isRoot(fileName)) {
return true;
}
synchronized (MEMORY_FILES) {
return MEMORY_FILES.get(fileName) != null;
}
}
public void delete(String path) {
path = getCanonicalPath(path);
if (isRoot(path)) {
return;
}
synchronized (MEMORY_FILES) {
MEMORY_FILES.remove(path);
}
}
public String[] listFiles(String path) {
path = getCanonicalPath(path);
ArrayList<String> list = New.arrayList();
synchronized (MEMORY_FILES) {
for (String name : MEMORY_FILES.tailMap(path).keySet()) {
if (name.startsWith(path)) {
list.add(name);
} else {
break;
}
}
String[] array = new String[list.size()];
list.toArray(array);
return array;
}
}
public boolean setReadOnly(String fileName) {
return getMemoryFile(fileName).setReadOnly();
}
public boolean canWrite(String fileName) {
return getMemoryFile(fileName).canWrite();
}
public String getCanonicalPath(String fileName) {
fileName = fileName.replace('\\', '/');
int idx = fileName.indexOf(':') + 1;
if (fileName.length() > idx && fileName.charAt(idx) != '/') {
fileName = fileName.substring(0, idx) + "/" + fileName.substring(idx);
}
return fileName;
}
public String getParent(String fileName) {
fileName = getCanonicalPath(fileName);
int idx = fileName.lastIndexOf('/');
if (idx < 0) {
idx = fileName.indexOf(':') + 1;
}
return fileName.substring(0, idx);
}
public boolean isDirectory(String fileName) {
// TODO in memory file system currently
// does not really support directories
return false;
}
public boolean isAbsolute(String fileName) {
// TODO relative files are not supported
return true;
}
public long lastModified(String fileName) {
return getMemoryFile(fileName).getLastModified();
}
public void createDirectory(String directoryName) {
// TODO directories are not really supported
}
public String getName(String path) {
int idx = Math.max(path.indexOf(':'), path.lastIndexOf('/'));
return idx < 0 ? path : path.substring(idx + 1);
}
public boolean fileStartsWith(String fileName, String prefix) {
fileName = getCanonicalPath(fileName);
prefix = getCanonicalPath(prefix);
return fileName.startsWith(prefix);
}
public OutputStream newOutputStream(String fileName, boolean append) {
try {
FileObjectMemoryData obj = getMemoryFile(fileName);
FileObjectMemory m = new FileObjectMemory(obj, false);
return new FileObjectOutputStream(m, append);
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
public InputStream newInputStream(String fileName) {
FileObjectMemoryData obj = getMemoryFile(fileName);
FileObjectMemory m = new FileObjectMemory(obj, true);
return new FileObjectInputStream(m);
}
public FileObject openFileObject(String fileName, String mode) {
FileObjectMemoryData obj = getMemoryFile(fileName);
return new FileObjectMemory(obj, "r".equals(mode));
}
private FileObjectMemoryData getMemoryFile(String fileName) {
fileName = getCanonicalPath(fileName);
synchronized (MEMORY_FILES) {
FileObjectMemoryData m = MEMORY_FILES.get(fileName);
if (m == null) {
boolean compress = fileName.startsWith(PREFIX_LZF);
m = new FileObjectMemoryData(fileName, compress);
MEMORY_FILES.put(fileName, m);
}
return m;
}
}
private boolean isRoot(String path) {
return path.equals(PREFIX) || path.equals(PREFIX_LZF);
}
protected boolean accepts(String fileName) {
return fileName.startsWith(PREFIX) || fileName.startsWith(PREFIX_LZF);
}
public String unwrap(String fileName) {
return fileName;
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import org.h2.constant.SysProperties;
import org.h2.message.DbException;
import org.h2.util.New;
/**
* A file system that may split files into multiple smaller files.
* (required for a FAT32 because it only support files up to 2 GB).
*/
public class FileSystemSplit extends FileSystemWrapper {
/**
* The prefix to use for this file system.
*/
public static final String PREFIX = "split:";
private static final String PART_SUFFIX = ".part";
private long defaultMaxSize = 1L << SysProperties.SPLIT_FILE_SIZE_SHIFT;
static {
FileSystem.register(new FileSystemSplit());
}
public boolean setReadOnly(String fileName) {
fileName = unwrap(fileName);
boolean result = false;
for (int i = 0;; i++) {
String f = getFileName(fileName, i);
if (getInstance(f).exists(f)) {
result = getInstance(f).setReadOnly(f);
} else {
break;
}
}
return result;
}
public void delete(String path) {
path = unwrap(path);
for (int i = 0;; i++) {
String f = getFileName(path, i);
if (getInstance(path).exists(f)) {
getInstance(path).delete(f);
} else {
break;
}
}
}
public long lastModified(String fileName) {
fileName = unwrap(fileName);
long lastModified = 0;
for (int i = 0;; i++) {
String f = getFileName(fileName, i);
if (getInstance(fileName).exists(f)) {
long l = getInstance(fileName).lastModified(fileName);
lastModified = Math.max(lastModified, l);
} else {
break;
}
}
return lastModified;
}
public long size(String fileName) {
fileName = unwrap(fileName);
long length = 0;
for (int i = 0;; i++) {
String f = getFileName(fileName, i);
if (getInstance(fileName).exists(f)) {
length += getInstance(fileName).size(f);
} else {
break;
}
}
return length;
}
public String[] listFiles(String directory) {
String[] array = super.listFiles(directory);
ArrayList<String> list = New.arrayList();
for (int i = 0; i < array.length; i++) {
String f = array[i];
if (f.endsWith(PART_SUFFIX)) {
continue;
}
list.add(f);
}
if (list.size() != array.length) {
array = new String[list.size()];
list.toArray(array);
}
return array;
}
public InputStream newInputStream(String fileName) throws IOException {
fileName = unwrap(fileName);
InputStream input = getInstance(fileName).newInputStream(fileName);
for (int i = 1;; i++) {
String f = getFileName(fileName, i);
if (getInstance(f).exists(f)) {
InputStream i2 = getInstance(f).newInputStream(f);
input = new SequenceInputStream(input, i2);
} else {
break;
}
}
return input;
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
fileName = unwrap(fileName);
ArrayList<FileObject> list = New.arrayList();
FileObject o = getInstance(fileName).openFileObject(fileName, mode);
list.add(o);
for (int i = 1;; i++) {
String f = getFileName(fileName, i);
if (getInstance(fileName).exists(f)) {
o = getInstance(f).openFileObject(f, mode);
list.add(o);
} else {
break;
}
}
FileObject[] array = new FileObject[list.size()];
list.toArray(array);
long maxLength = array[0].size();
long length = maxLength;
if (array.length == 1) {
if (maxLength < defaultMaxSize) {
maxLength = defaultMaxSize;
}
} else {
if (maxLength == 0) {
closeAndThrow(fileName, 0, array, array[0], maxLength);
}
for (int i = 1; i < array.length - 1; i++) {
o = array[i];
long l = o.size();
length += l;
if (l != maxLength) {
closeAndThrow(fileName, i, array, o, maxLength);
}
}
o = array[array.length - 1];
long l = o.size();
length += l;
if (l > maxLength) {
closeAndThrow(fileName, array.length - 1, array, o, maxLength);
}
}
FileObjectSplit fo = new FileObjectSplit(fileName, mode, array, length, maxLength);
return fo;
}
private static void closeAndThrow(String fileName, int id, FileObject[] array, FileObject o, long maxLength) throws IOException {
String message = "Expected file length: " + maxLength + " got: " + o.size() + " for " + getFileName(fileName, id);
for (FileObject f : array) {
f.close();
}
throw new IOException(message);
}
public OutputStream newOutputStream(String fileName, boolean append) {
try {
return new FileObjectOutputStream(openFileObject(fileName, "rw"), append);
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
public void moveTo(String oldName, String newName) {
oldName = unwrap(oldName);
newName = unwrap(newName);
for (int i = 0;; i++) {
String o = getFileName(oldName, i);
if (getInstance(o).exists(o)) {
String n = getFileName(newName, i);
getInstance(n).moveTo(o, n);
} else {
break;
}
}
}
public String unwrap(String fileName) {
if (!fileName.startsWith(PREFIX)) {
DbException.throwInternalError(fileName + " doesn't start with " + PREFIX);
}
fileName = fileName.substring(PREFIX.length());
if (fileName.length() > 0 && Character.isDigit(fileName.charAt(0))) {
int idx = fileName.indexOf(':');
String size = fileName.substring(0, idx);
try {
defaultMaxSize = 1L << Integer.decode(size).intValue();
fileName = fileName.substring(idx + 1);
} catch (NumberFormatException e) {
// ignore
}
}
return fileName;
}
/**
* Get the file name of a part file.
*
* @param fileName the file name
* @param id the part id
* @return the file name including the part id
*/
static String getFileName(String fileName, int id) {
if (id > 0) {
fileName += "." + id + PART_SUFFIX;
}
return fileName;
}
protected String getPrefix() {
return PREFIX;
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.h2.message.DbException;
/**
* The base class for wrapping / delegating file systems such as
* FileSystemSplit
*/
public abstract class FileSystemWrapper extends FileSystem {
/**
* Get the prefix for this file system.
*
* @return the prefix
*/
protected abstract String getPrefix();
public boolean setReadOnly(String fileName) {
return FileUtils.setReadOnly(unwrap(fileName));
}
public void createDirectory(String directoryName) {
FileUtils.createDirectory(unwrap(directoryName));
}
public boolean createFile(String fileName) {
return FileUtils.createFile(unwrap(fileName));
}
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
return wrap(FileUtils.createTempFile(unwrap(prefix), suffix, deleteOnExit, inTempDir));
}
public void delete(String path) {
FileUtils.delete(unwrap(path));
}
public boolean exists(String fileName) {
return FileUtils.exists(unwrap(fileName));
}
public boolean fileStartsWith(String fileName, String prefix) {
return FileUtils.fileStartsWith(unwrap(fileName), unwrap(prefix));
}
public String getName(String path) {
return FileUtils.getName(unwrap(path));
}
public long lastModified(String fileName) {
return FileUtils.lastModified(unwrap(fileName));
}
public String getParent(String fileName) {
return wrap(FileUtils.getParent(unwrap(fileName)));
}
public boolean isAbsolute(String fileName) {
return FileUtils.isAbsolute(unwrap(fileName));
}
public boolean isDirectory(String fileName) {
return FileUtils.isDirectory(unwrap(fileName));
}
public boolean canWrite(String fileName) {
return FileUtils.canWrite(unwrap(fileName));
}
public long size(String fileName) {
return FileUtils.size(unwrap(fileName));
}
public String[] listFiles(String directory) {
String[] array = FileUtils.listFiles(unwrap(directory));
for (int i = 0; i < array.length; i++) {
array[i] = wrap(array[i]);
}
return array;
}
public String getCanonicalPath(String fileName) {
return wrap(FileUtils.getCanonicalPath(unwrap(fileName)));
}
public InputStream newInputStream(String fileName) throws IOException {
return FileUtils.newInputStream(unwrap(fileName));
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
return FileUtils.openFileObject(unwrap(fileName), mode);
}
public OutputStream newOutputStream(String fileName, boolean append) {
return FileUtils.newOutputStream(unwrap(fileName), append);
}
public void moveTo(String oldName, String newName) {
FileUtils.moveTo(unwrap(oldName), unwrap(newName));
}
protected boolean accepts(String fileName) {
return fileName.startsWith(getPrefix());
}
private String wrap(String fileName) {
return getPrefix() + fileName;
}
public String unwrap(String fileName) {
String prefix = getPrefix();
if (!fileName.startsWith(prefix)) {
DbException.throwInternalError(fileName + " doesn't start with " + prefix);
}
return fileName.substring(prefix.length());
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store.fs;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.h2.message.DbException;
import org.h2.util.New;
/**
* This is a read-only file system that allows
* to access databases stored in a .zip or .jar file.
*/
public class FileSystemZip extends FileSystem {
private static final String PREFIX = "zip:";
static {
FileSystem.register(new FileSystemZip());
}
public void createDirectory(String directoryName) {
// ignore
}
public boolean createFile(String fileName) {
throw DbException.getUnsupportedException("write");
}
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
if (!inTempDir) {
throw new IOException("File system is read-only");
}
return FileSystemDisk.getInstance().createTempFile(prefix, suffix, deleteOnExit, true);
}
public void delete(String fileName) {
throw DbException.getUnsupportedException("write");
}
public boolean exists(String fileName) {
try {
String entryName = getEntryName(fileName);
if (entryName.length() == 0) {
return true;
}
ZipFile file = openZipFile(fileName);
return file.getEntry(entryName) != null;
} catch (IOException e) {
return false;
}
}
public boolean fileStartsWith(String fileName, String prefix) {
return fileName.startsWith(prefix);
}
public String getName(String name) {
name = getEntryName(name);
if (name.endsWith("/")) {
name = name.substring(0, name.length() - 1);
}
int idx = name.lastIndexOf('/');
if (idx >= 0) {
name = name.substring(idx + 1);
}
return name;
}
public long lastModified(String fileName) {
return 0;
}
public String getParent(String fileName) {
int idx = fileName.lastIndexOf('/');
if (idx > 0) {
fileName = fileName.substring(0, idx);
}
return fileName;
}
public boolean isAbsolute(String fileName) {
return true;
}
public boolean isDirectory(String fileName) {
try {
String entryName = getEntryName(fileName);
if (entryName.length() == 0) {
return true;
}
ZipFile file = openZipFile(fileName);
Enumeration<? extends ZipEntry> en = file.entries();
while (en.hasMoreElements()) {
ZipEntry entry = en.nextElement();
String n = entry.getName();
if (n.equals(entryName)) {
return entry.isDirectory();
} else if (n.startsWith(entryName)) {
if (n.length() == entryName.length() + 1) {
if (n.equals(entryName + "/")) {
return true;
}
}
}
}
return false;
} catch (IOException e) {
return false;
}
}
public boolean canWrite(String fileName) {
return false;
}
public boolean setReadOnly(String fileName) {
return true;
}
public long size(String fileName) {
try {
ZipFile file = openZipFile(fileName);
ZipEntry entry = file.getEntry(getEntryName(fileName));
return entry == null ? 0 : entry.getSize();
} catch (IOException e) {
return 0;
}
}
public String[] listFiles(String path) {
try {
if (path.indexOf('!') < 0) {
path += "!";
}
if (!path.endsWith("/")) {
path += "/";
}
ZipFile file = openZipFile(path);
String dirName = getEntryName(path);
String prefix = path.substring(0, path.length() - dirName.length());
Enumeration<? extends ZipEntry> en = file.entries();
ArrayList<String> list = New.arrayList();
while (en.hasMoreElements()) {
ZipEntry entry = en.nextElement();
String name = entry.getName();
if (!name.startsWith(dirName)) {
continue;
}
if (name.length() <= dirName.length()) {
continue;
}
int idx = name.indexOf('/', dirName.length());
if (idx < 0 || idx >= name.length() - 1) {
list.add(prefix + name);
}
}
String[] result = new String[list.size()];
list.toArray(result);
return result;
} catch (IOException e) {
throw DbException.convertIOException(e, "listFiles " + path);
}
}
public String getCanonicalPath(String fileName) {
return fileName;
}
public InputStream newInputStream(String fileName) throws IOException {
FileObject file = openFileObject(fileName, "r");
return new FileObjectInputStream(file);
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
ZipFile file = openZipFile(translateFileName(fileName));
ZipEntry entry = file.getEntry(getEntryName(fileName));
if (entry == null) {
throw new FileNotFoundException(fileName);
}
return new FileObjectZip(file, entry);
}
public OutputStream newOutputStream(String fileName, boolean append) {
throw DbException.getUnsupportedException("write");
}
public void moveTo(String oldName, String newName) {
throw DbException.getUnsupportedException("write");
}
private static String translateFileName(String fileName) {
if (fileName.startsWith(PREFIX)) {
fileName = fileName.substring(PREFIX.length());
}
int idx = fileName.indexOf('!');
if (idx >= 0) {
fileName = fileName.substring(0, idx);
}
return FileSystemDisk.expandUserHomeDirectory(fileName);
}
private static String getEntryName(String fileName) {
int idx = fileName.indexOf('!');
if (idx <= 0) {
fileName = "";
} else {
fileName = fileName.substring(idx + 1);
}
fileName = fileName.replace('\\', '/');
if (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
return fileName;
}
private static ZipFile openZipFile(String fileName) throws IOException {
fileName = translateFileName(fileName);
return new ZipFile(fileName);
}
protected boolean accepts(String fileName) {
return fileName.startsWith(PREFIX);
}
public String unwrap(String fileName) {
return fileName;
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store.fs;
import java.io.IOException;
/**
* A file object that records all write operations and can re-play them.
*/
public class RecordingFileObject implements FileObject {
private final RecordingFileSystem fs;
private final FileObject file;
private final String name;
RecordingFileObject(RecordingFileSystem fs, FileObject file, String fileName) {
this.fs = fs;
this.file = file;
this.name = fileName;
}
public void close() throws IOException {
file.close();
}
public long position() throws IOException {
return file.position();
}
public long size() throws IOException {
return file.size();
}
public void readFully(byte[] b, int off, int len) throws IOException {
file.readFully(b, off, len);
}
public void position(long pos) throws IOException {
file.position(pos);
}
public void truncate(long newLength) throws IOException {
fs.log(Recorder.TRUNCATE, name, null, newLength);
file.truncate(newLength);
}
public void sync() throws IOException {
file.sync();
}
public void write(byte[] b, int off, int len) throws IOException {
byte[] buff = b;
if (off != 0 || len != b.length) {
buff = new byte[len];
System.arraycopy(b, off, buff, 0, len);
}
file.write(b, off, len);
fs.log(Recorder.WRITE, name, buff, file.position());
}
public boolean tryLock() {
return file.tryLock();
}
public void releaseLock() {
file.releaseLock();
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store.fs;
import java.io.IOException;
import java.io.OutputStream;
/**
* A file system that records all write operations and can re-play them.
*/
public class RecordingFileSystem extends FileSystemWrapper {
/**
* The prefix used for a debugging file system.
*/
public static final String PREFIX = "rec:";
private static final RecordingFileSystem INSTANCE = new RecordingFileSystem();
private static Recorder recorder;
private boolean trace;
/**
* Register the file system.
*/
public static void register() {
FileSystem.register(INSTANCE);
}
/**
* Set the recorder class.
*
* @param recorder the recorder
*/
public static void setRecorder(Recorder recorder) {
RecordingFileSystem.recorder = recorder;
}
public void createDirectories(String directoryName) {
log(Recorder.CREATE_DIRECTORY, unwrap(directoryName));
super.createDirectory(directoryName);
}
public boolean createFile(String fileName) {
log(Recorder.CREATE_NEW_FILE, unwrap(fileName));
return super.createFile(fileName);
}
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
log(Recorder.CREATE_TEMP_FILE, unwrap(prefix) + ":" + suffix + ":" + deleteOnExit + ":" + inTempDir);
return super.createTempFile(prefix, suffix, deleteOnExit, inTempDir);
}
public void delete(String fileName) {
log(Recorder.DELETE, unwrap(fileName));
super.delete(fileName);
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
return new RecordingFileObject(this, super.openFileObject(fileName, mode), fileName);
}
public OutputStream newOutputStream(String fileName, boolean append) {
log(Recorder.OPEN_OUTPUT_STREAM, unwrap(fileName));
return super.newOutputStream(fileName, append);
}
public void moveTo(String oldName, String newName) {
log(Recorder.RENAME, unwrap(oldName) + ":" + unwrap(newName));
super.moveTo(oldName, newName);
}
public String getPrefix() {
return PREFIX;
}
public boolean isTrace() {
return trace;
}
public void setTrace(boolean trace) {
this.trace = trace;
}
/**
* Log the operation.
*
* @param op the operation
* @param fileName the file name
*/
void log(int op, String fileName) {
log(op, fileName, null, 0);
}
/**
* Log the operation.
*
* @param op the operation
* @param fileName the file name
* @param data the data or null
* @param x the value or 0
*/
void log(int op, String fileName, byte[] data, long x) {
if (recorder != null) {
recorder.log(op, fileName, data, x);
}
}
}
......@@ -10,8 +10,8 @@ import java.sql.SQLException;
import java.util.Properties;
import org.h2.Driver;
import org.h2.engine.Constants;
import org.h2.store.fs.FilePathRec;
import org.h2.store.fs.FileUtils;
import org.h2.store.fs.RecordingFileSystem;
import org.h2.test.bench.TestPerformance;
import org.h2.test.db.TestAlter;
import org.h2.test.db.TestAlterSchemaRename;
......@@ -381,10 +381,10 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
System.setProperty("h2.analyzeAuto", "100");
System.setProperty("h2.pageSize", "64");
System.setProperty("h2.reopenShift", "5");
RecordingFileSystem.register();
FilePathRec.register();
test.reopen = true;
TestReopen reopen = new TestReopen();
RecordingFileSystem.setRecorder(reopen);
FilePathRec.setRecorder(reopen);
test.runTests();
} else if ("crash".equals(args[0])) {
test.endless = true;
......
......@@ -32,9 +32,7 @@ import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.message.TraceSystem;
import org.h2.store.FileLock;
import org.h2.store.fs.FileSystemSplit;
import org.h2.store.fs.FileUtils;
import org.h2.store.fs.RecordingFileSystem;
import org.h2.test.utils.ProxyCodeGenerator;
import org.h2.test.utils.ResultVerifier;
import org.h2.tools.DeleteDbFiles;
......@@ -224,10 +222,10 @@ public abstract class TestBase {
String dir = baseDir;
if (config != null) {
if (config.reopen) {
dir = RecordingFileSystem.PREFIX + "memFS:" + dir;
dir = "rec:memFS:" + dir;
}
if (config.splitFileSystem) {
dir = FileSystemSplit.PREFIX + "16:" + dir;
dir = "split:16:" + dir;
}
}
// return "split:nioMapped:" + baseDir;
......
......@@ -12,14 +12,14 @@ import java.sql.SQLException;
import java.sql.Statement;
import org.h2.constant.ErrorCode;
import org.h2.test.TestBase;
import org.h2.test.utils.DebugFileSystem;
import org.h2.test.utils.FilePathDebug;
/**
* Tests that use the debug file system to simulate power failure.
*/
public class TestPowerOffFs extends TestBase {
private DebugFileSystem fs;
private FilePathDebug fs;
/**
* Run just this test.
......@@ -31,7 +31,7 @@ public class TestPowerOffFs extends TestBase {
}
public void test() throws Exception {
fs = DebugFileSystem.register();
fs = FilePathDebug.register();
test(Integer.MAX_VALUE);
System.out.println(Integer.MAX_VALUE - fs.getPowerOffCount());
System.out.println("done");
......
......@@ -15,7 +15,7 @@ import java.util.ArrayList;
import java.util.Random;
import org.h2.constant.ErrorCode;
import org.h2.test.TestBase;
import org.h2.test.utils.DebugFileSystem;
import org.h2.test.utils.FilePathDebug;
import org.h2.util.New;
/**
......@@ -24,7 +24,7 @@ import org.h2.util.New;
*/
public class TestPowerOffFs2 extends TestBase {
private DebugFileSystem fs;
private FilePathDebug fs;
private String url;
private String user = "sa";
......@@ -43,7 +43,7 @@ public class TestPowerOffFs2 extends TestBase {
}
public void test() throws Exception {
fs = DebugFileSystem.register();
fs = FilePathDebug.register();
url = "jdbc:h2:debug:memFS:powerOffFs;FILE_LOCK=NO;TRACE_LEVEL_FILE=0;WRITE_DELAY=0;CACHE_SIZE=32";
for (int i = 0;; i++) {
test(i);
......
......@@ -10,7 +10,6 @@ import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.constant.SysProperties;
import org.h2.store.fs.FileSystemMemory;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
......@@ -56,7 +55,7 @@ public class TestRandomSQL extends TestBase {
private void deleteDb() {
String name = getDatabaseName();
if (name.startsWith(FileSystemMemory.PREFIX)) {
if (name.startsWith("memFS:")) {
DeleteDbFiles.execute("memFS:/", name, true);
} else {
DeleteDbFiles.execute(getBaseDir() + "/dataRandomSQL", null, true);
......
......@@ -10,7 +10,7 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Random;
import org.h2.store.fs.RecordingFileSystem;
import org.h2.store.fs.FilePathRec;
import org.h2.test.unit.TestReopen;
import org.h2.tools.DeleteDbFiles;
......@@ -35,13 +35,12 @@ public class TestTempTableCrash {
System.setProperty("h2.delayWrongPasswordMin", "0");
System.setProperty("h2.check2", "false");
System.setProperty("h2.lobInDatabase", "true");
RecordingFileSystem.register();
FilePathRec.register();
System.setProperty("reopenShift", "4");
TestReopen reopen = new TestReopen();
RecordingFileSystem.setRecorder(reopen);
FilePathRec.setRecorder(reopen);
String url = "jdbc:h2:" + RecordingFileSystem.PREFIX +
"memFS:data;PAGE_SIZE=64;ANALYZE_AUTO=100";
String url = "jdbc:h2:rec:memFS:data;PAGE_SIZE=64;ANALYZE_AUTO=100";
// String url = "jdbc:h2:" + RecordingFileSystem.PREFIX +
// "data/test;PAGE_SIZE=64";
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.unit;
import java.io.EOFException;
import java.io.IOException;
import org.h2.store.fs.FileObject;
import org.h2.util.Utils;
/**
* In this file system, files are kept fully in memory until stored.
*/
public class FileObjectDatabase implements FileObject {
private final FileSystemDatabase db;
private final boolean readOnly;
private final String fileName;
private byte[] data;
private int pos, length;
private boolean changed;
FileObjectDatabase(FileSystemDatabase db, String fileName, byte[] data, boolean changed, boolean readOnly) {
this.db = db;
this.readOnly = readOnly;
this.fileName = fileName;
this.data = data;
this.length = data.length;
this.changed = changed;
}
public void close() {
sync();
}
public long position() {
return pos;
}
public long size() {
return length;
}
public void readFully(byte[] b, int off, int len) throws IOException {
if (pos + len > length) {
throw new EOFException();
}
System.arraycopy(data, pos, b, off, len);
pos += len;
}
public void position(long newPos) {
this.pos = (int) newPos;
}
public void truncate(long newLength) throws IOException {
if (readOnly) {
throw new IOException("read only");
}
if (newLength >= this.length) {
return;
}
this.length = (int) newLength;
if (length != data.length) {
byte[] n = Utils.newBytes(length);
System.arraycopy(data, 0, n, 0, Math.min(data.length, n.length));
data = n;
changed = true;
}
pos = Math.min(pos, length);
}
public void sync() {
if (changed) {
db.write(fileName, data, length);
changed = false;
}
}
public void write(byte[] b, int off, int len) throws IOException {
if (readOnly) {
throw new IOException("read only");
}
if (pos + len > data.length) {
int newLen = Math.max(data.length * 2, pos + len);
byte[] n = Utils.newBytes(newLen);
System.arraycopy(data, 0, n, 0, length);
data = n;
}
System.arraycopy(b, off, data, pos, len);
pos += len;
length = Math.max(length, pos);
changed = true;
}
public void releaseLock() {
// ignore
}
public boolean tryLock() {
return false;
}
}
......@@ -29,7 +29,8 @@ public class TestClearReferences extends TestBase {
"org.h2.engine.SessionRemote.sessionFactory",
"org.h2.jdbcx.JdbcDataSourceFactory.cachedTraceSystem",
"org.h2.store.RecoverTester.instance",
"org.h2.store.fs.FileSystem.tempRandom",
"org.h2.store.fs.FilePath.providers",
"org.h2.store.fs.FilePath.tempRandom",
"org.h2.tools.CompressTool.cachedBuffer",
"org.h2.util.CloseWatcher.queue",
"org.h2.util.CloseWatcher.refs",
......@@ -67,7 +68,7 @@ public class TestClearReferences extends TestBase {
// initialize the known classes
MathUtils.secureRandomLong();
ValueInt.get(1);
Class.forName("org.h2.store.fs.FileObjectMemoryData");
Class.forName("org.h2.store.fs.FileObjectMemData");
clear();
......
......@@ -17,12 +17,14 @@ import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Random;
import org.h2.dev.fs.FileSystemCrypt;
import org.h2.dev.fs.FilePathCrypt;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileSystemMemory;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
import org.h2.test.utils.FilePathDebug;
import org.h2.tools.Backup;
import org.h2.tools.DeleteDbFiles;
......@@ -45,9 +47,9 @@ public class TestFileSystem extends TestBase {
public void test() throws Exception {
testMemFsDir();
testClasspath();
FileSystemCrypt.register();
// DebugFileSystem.register().setTrace(true);
// testFileSystem("crypt:aes:x:" + getBaseDir() + "/fs");
FilePathCrypt.register();
FilePathDebug.register().setTrace(true);
testFileSystem("crypt:aes:x:" + getBaseDir() + "/fs");
testSimpleExpandTruncateSize();
testSplitDatabaseInZip();
......@@ -57,15 +59,11 @@ public class TestFileSystem extends TestBase {
String f = "split:10:" + getBaseDir() + "/fs";
FileUtils.getCanonicalPath(f);
testFileSystem(getBaseDir() + "/fs");
testFileSystem(FileSystemMemory.PREFIX);
FileSystemDatabase fs = FileSystemDatabase.register("jdbc:h2:mem:fs");
// testFileSystem("jdbc:h2:mem:fs;TRACE_LEVEL_FILE=3");
testFileSystem("jdbc:h2:mem:fs");
testFileSystem(FileSystemMemory.PREFIX_LZF);
testFileSystem("memFS:");
testFileSystem("memLZF:");
testUserHome();
fs.unregister();
try {
FileSystemCrypt.register();
FilePathCrypt.register();
testFileSystem("crypt:aes:x:" + getBaseDir() + "/fs");
testFileSystem("nio:" + getBaseDir() + "/fs");
testFileSystem("nioMapped:" + getBaseDir() + "/fs");
......@@ -228,6 +226,10 @@ public class TestFileSystem extends TestBase {
FileUtils.delete(fsBase + "/test");
FileUtils.delete(fsBase + "/test2");
assertTrue(FileUtils.createFile(fsBase + "/test"));
List<FilePath> p = FilePath.get(fsBase).listFiles();
assertEquals(1, p.size());
String can = FilePath.get(fsBase + "/test").getCanonicalPath().toString();
assertEquals(can, p.get(0).toString());
assertTrue(FileUtils.canWrite(fsBase + "/test"));
FileObject fo = FileUtils.openFileObject(fsBase + "/test", "rw");
byte[] buffer = new byte[10000];
......@@ -240,7 +242,9 @@ public class TestFileSystem extends TestBase {
assertThrows(EOFException.class, fo).readFully(buffer, 0, 1);
String path = fsBase + "/test";
assertEquals("test", FileUtils.getName(path));
assertEquals(fsBase, FileUtils.getParent(path).replace('\\', '/'));
can = FilePath.get(fsBase).getCanonicalPath().toString();
String can2 = FileUtils.getCanonicalPath(FileUtils.getParent(path));
assertEquals(can, can2);
fo.tryLock();
fo.releaseLock();
assertEquals(10000, fo.size());
......@@ -283,7 +287,7 @@ public class TestFileSystem extends TestBase {
assertTrue(FileUtils.tryDelete(fsBase + "/test2"));
FileUtils.delete(fsBase + "/test");
if (fsBase.indexOf(FileSystemMemory.PREFIX) < 0 && fsBase.indexOf(FileSystemMemory.PREFIX_LZF) < 0) {
if (fsBase.indexOf("memFS:") < 0 && fsBase.indexOf("memLZF:") < 0) {
FileUtils.createDirectories(fsBase + "/testDir");
assertTrue(FileUtils.isDirectory(fsBase + "/testDir"));
if (!fsBase.startsWith("jdbc:")) {
......
......@@ -15,9 +15,9 @@ import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.store.fs.FilePathRec;
import org.h2.store.fs.FileUtils;
import org.h2.store.fs.Recorder;
import org.h2.store.fs.RecordingFileSystem;
import org.h2.test.TestBase;
import org.h2.tools.Recover;
import org.h2.util.New;
......@@ -51,8 +51,8 @@ public class TestReopen extends TestBase implements Recorder {
public void test() throws Exception {
System.setProperty("h2.delayWrongPasswordMin", "0");
RecordingFileSystem.register();
RecordingFileSystem.setRecorder(this);
FilePathRec.register();
FilePathRec.setRecorder(this);
config.reopen = true;
long time = System.currentTimeMillis();
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.utils;
import java.io.IOException;
import org.h2.store.fs.FileObject;
/**
* A debugging file that logs all operations.
*/
public class DebugFileObject implements FileObject {
private final DebugFileSystem fs;
private final FileObject file;
private final String name;
DebugFileObject(DebugFileSystem fs, FileObject file, String name) {
this.fs = fs;
this.file = file;
this.name = fs.getPrefix() + name;
}
public void close() throws IOException {
debug("close");
file.close();
}
public long position() throws IOException {
debug("getFilePointer");
return file.position();
}
public long size() throws IOException {
debug("length");
return file.size();
}
public void readFully(byte[] b, int off, int len) throws IOException {
debug("readFully", file.position(), off, len);
file.readFully(b, off, len);
}
public void position(long pos) throws IOException {
debug("seek", pos);
file.position(pos);
}
public void truncate(long newLength) throws IOException {
checkPowerOff();
debug("truncate", newLength);
file.truncate(newLength);
}
public void sync() throws IOException {
debug("sync");
file.sync();
}
public void write(byte[] b, int off, int len) throws IOException {
checkPowerOff();
debug("write", file.position(), off, len);
file.write(b, off, len);
}
private void debug(String method, Object... params) {
fs.trace(name, method, params);
}
private void checkPowerOff() throws IOException {
try {
fs.checkPowerOff();
} catch (IOException e) {
try {
file.close();
} catch (IOException e2) {
// ignore
}
throw e;
}
}
public boolean tryLock() {
debug("tryLock");
return file.tryLock();
}
public void releaseLock() {
debug("releaseLock");
file.releaseLock();
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.utils;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileSystem;
import org.h2.store.fs.FileSystemWrapper;
/**
* A debugging file system that logs all operations.
*/
public class DebugFileSystem extends FileSystemWrapper {
/**
* The prefix used for a debugging file system.
*/
static final String PREFIX = "debug:";
private static final DebugFileSystem INSTANCE = new DebugFileSystem();
private static final IOException POWER_OFF = new IOException("Simulated power failure");
private int powerOffCount;
private boolean trace;
/**
* Register the file system.
*
* @return the instance
*/
public static DebugFileSystem register() {
FileSystem.register(INSTANCE);
return INSTANCE;
}
/**
* Check if the simulated power failure occurred.
* This call will decrement the countdown.
*
* @throws IOException if the simulated power failure occurred
*/
void checkPowerOff() throws IOException {
if (powerOffCount == 0) {
return;
}
if (powerOffCount > 1) {
powerOffCount--;
return;
}
powerOffCount = -1;
// throw new IOException("Simulated power failure");
throw POWER_OFF;
}
public void createDirectory(String directoryName) {
trace(directoryName, "createDirectory");
super.createDirectory(directoryName);
}
public boolean createFile(String fileName) {
trace(fileName, "createFile");
return super.createFile(fileName);
}
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
trace(prefix, "createTempFile", suffix, deleteOnExit, inTempDir);
return super.createTempFile(prefix, suffix, deleteOnExit, inTempDir);
}
public void delete(String fileName) {
trace(fileName, "fileName");
super.delete(fileName);
}
public boolean exists(String fileName) {
trace(fileName, "exists");
return super.exists(fileName);
}
public boolean fileStartsWith(String fileName, String prefix) {
trace(fileName, "fileStartsWith", unwrap(prefix));
return super.fileStartsWith(fileName, prefix);
}
public String getName(String path) {
trace(path, "getName");
return super.getName(path);
}
public long lastModified(String fileName) {
trace(fileName, "lastModified");
return super.lastModified(fileName);
}
public String getParent(String fileName) {
trace(fileName, "getParent");
return super.getParent(fileName);
}
public boolean isAbsolute(String fileName) {
trace(fileName, "isAbsolute");
return super.isAbsolute(fileName);
}
public boolean isDirectory(String fileName) {
trace(fileName, "isDirectory");
return super.isDirectory(fileName);
}
public boolean canWrite(String fileName) {
trace(fileName, "canWrite");
return super.canWrite(fileName);
}
public boolean setReadOnly(String fileName) {
trace(fileName, "setReadOnly");
return super.setReadOnly(fileName);
}
public long size(String fileName) {
trace(fileName, "size");
return super.size(fileName);
}
public String[] listFiles(String directory) {
trace(directory, "listFiles");
return super.listFiles(directory);
}
public String getCanonicalPath(String fileName) {
trace(fileName, "getCanonicalPath");
return super.getCanonicalPath(fileName);
}
public InputStream newInputStream(final String fileName) throws IOException {
trace(fileName, "newInputStream");
InputStream in = super.newInputStream(fileName);
if (!trace) {
return in;
}
return new FilterInputStream(in) {
public int read(byte[] b) throws IOException {
trace(fileName, "in.read(b)");
return super.read(b);
}
public int read(byte[] b, int off, int len) throws IOException {
trace(fileName, "in.read(b, " + off + ", " + len + ")");
return super.read(b, off, len);
}
public long skip(long n) throws IOException {
trace(fileName, "in.skip(" + n + ")");
return super.skip(n);
}
};
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
trace(fileName, "openFileObject", mode);
return new DebugFileObject(this, super.openFileObject(fileName, mode), fileName);
}
public OutputStream newOutputStream(String fileName, boolean append) {
trace(fileName, "newOutputStream", append);
return super.newOutputStream(fileName, append);
}
public void moveTo(String oldName, String newName) {
trace(oldName, "moveTo", unwrap(newName));
super.moveTo(oldName, newName);
}
public String getPrefix() {
return PREFIX;
}
/**
* Print a debug message.
*
* @param fileName the (wrapped) file name
* @param method the method name
* @param params parameters if any
*/
void trace(String fileName, String method, Object... params) {
if (trace) {
StringBuilder buff = new StringBuilder(" ");
buff.append(unwrap(fileName)).append(' ').append(method);
for (Object s : params) {
buff.append(' ').append(s);
}
System.out.println(buff);
}
}
public void setPowerOffCount(int count) {
this.powerOffCount = count;
}
public int getPowerOffCount() {
return powerOffCount;
}
public boolean isTrace() {
return trace;
}
public void setTrace(boolean trace) {
this.trace = trace;
}
}
......@@ -12,13 +12,13 @@ import org.h2.store.fs.FileObject;
/**
* A debugging file that logs all operations.
*/
public class DebugFile implements FileObject {
public class FileDebug implements FileObject {
private final DebugFilePath fs;
private final FilePathDebug fs;
private final FileObject file;
private final String name;
DebugFile(DebugFilePath fs, FileObject file, String name) {
FileDebug(FilePathDebug fs, FileObject file, String name) {
this.fs = fs;
this.file = file;
this.name = fs.getScheme() + ":" + name;
......
......@@ -18,9 +18,9 @@ import org.h2.store.fs.FilePathWrapper;
/**
* A debugging file system that logs all operations.
*/
public class DebugFilePath extends FilePathWrapper {
public class FilePathDebug extends FilePathWrapper {
private static final DebugFilePath INSTANCE = new DebugFilePath();
private static final FilePathDebug INSTANCE = new FilePathDebug();
private static final IOException POWER_OFF = new IOException("Simulated power failure");
......@@ -32,7 +32,7 @@ public class DebugFilePath extends FilePathWrapper {
*
* @return the instance
*/
public static DebugFilePath register() {
public static FilePathDebug register() {
FilePath.register(INSTANCE);
return INSTANCE;
}
......@@ -76,10 +76,10 @@ public class DebugFilePath extends FilePathWrapper {
return super.exists();
}
// public boolean fileStartsWith(String fileName, String prefix) {
// trace(fileName, "fileStartsWith", unwrap(prefix));
// return super.fileStartsWith(fileName, prefix);
// }
public boolean fileStartsWith(String prefix) {
trace(name, "fileStartsWith", unwrap(prefix));
return super.fileStartsWith(prefix);
}
public String getName() {
trace(name, "getName");
......@@ -158,7 +158,7 @@ public class DebugFilePath extends FilePathWrapper {
public FileObject openFileObject(String mode) throws IOException {
trace(name, "openFileObject", mode);
return new DebugFile(this, super.openFileObject(mode), name);
return new FileDebug(this, super.openFileObject(mode), name);
}
public OutputStream newOutputStream(boolean append) {
......@@ -167,7 +167,7 @@ public class DebugFilePath extends FilePathWrapper {
}
public void moveTo(FilePath newName) {
trace(name, "moveTo", unwrap(((DebugFilePath) newName).name));
trace(name, "moveTo", unwrap(((FilePathDebug) newName).name));
super.moveTo(newName);
}
......
......@@ -19,7 +19,6 @@ import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileObjectInputStream;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathDisk;
import org.h2.store.fs.FileSystemDisk;
import org.h2.store.fs.FileUtils;
import org.h2.util.New;
......@@ -295,7 +294,7 @@ public class FilePathZip2 extends FilePath {
if (idx >= 0) {
fileName = fileName.substring(0, idx);
}
return FileSystemDisk.expandUserHomeDirectory(fileName);
return FilePathDisk.expandUserHomeDirectory(fileName);
}
public boolean fileStartsWith(String prefix) {
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.fs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.h2.message.DbException;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileObjectInputStream;
import org.h2.store.fs.FileObjectOutputStream;
import org.h2.store.fs.FileSystem;
import org.h2.store.fs.FileSystemWrapper;
import org.h2.store.fs.FileUtils;
/**
* A file system that encrypts the contents of the files.
*/
public class FileSystemCrypt extends FileSystemWrapper {
/**
* The prefix to use for this file system.
*/
public static final String PREFIX = "crypt:";
private static final FileSystemCrypt INSTANCE = new FileSystemCrypt();
protected String getPrefix() {
return PREFIX;
}
/**
* Register the file system.
*
* @return the instance
*/
public static FileSystemCrypt register() {
FileSystem.register(INSTANCE);
return INSTANCE;
}
public long size(String fileName) {
long len = super.size(fileName);
return Math.max(0, len - FileObjectCrypt.HEADER_LENGTH - FileObjectCrypt.BLOCK_SIZE);
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
String[] parsed = parse(fileName);
FileObject file = FileUtils.openFileObject(parsed[2], mode);
return new FileObjectCrypt(fileName, parsed[0], parsed[1], file);
}
public OutputStream newOutputStream(String fileName, boolean append) {
try {
FileObject file = openFileObject(fileName, "rw");
return new FileObjectOutputStream(file, append);
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
public InputStream newInputStream(String fileName) {
try {
FileObject file = openFileObject(fileName, "r");
return new FileObjectInputStream(file);
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
public String getParent(String fileName) {
String[] parsed = parse(fileName);
return combine(parsed[0], parsed[1], FileUtils.getParent(parsed[2]));
}
public String[] listFiles(String directory) {
String[] parsed = parse(directory);
String[] array = FileUtils.listFiles(parsed[2]);
for (int i = 0; i < array.length; i++) {
array[i] = combine(parsed[0], parsed[1], array[i]);
}
return array;
}
public String getCanonicalPath(String fileName) {
String[] parsed = parse(fileName);
return combine(parsed[0], parsed[1], FileUtils.getCanonicalPath(parsed[2]));
}
public String unwrap(String fileName) {
return parse(fileName)[2];
}
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
String[] parsed = parse(prefix);
return combine(parsed[0], parsed[1], FileUtils.createTempFile(parsed[2], suffix, deleteOnExit, inTempDir));
}
/**
* Combine the parameters into a file name.
*
* @param algorithm the encryption algorithm
* @param password the password
* @param fileName the base file name
* @return the combined file name
*/
private String combine(String algorithm, String password, String fileName) {
return PREFIX + algorithm + ":" + password + ":" + fileName;
}
/**
* Split the file name into algorithm, password, and base file name.
*
* @param fileName the file name
* @return an array with algorithm, password, and base file name
*/
private String[] parse(String fileName) {
if (!fileName.startsWith(PREFIX)) {
DbException.throwInternalError(fileName + " doesn't start with " + PREFIX);
}
fileName = fileName.substring(PREFIX.length());
int idx = fileName.indexOf(':');
String algorithm, password;
if (idx < 0) {
DbException.throwInternalError(fileName + " doesn't contain encryption algorithm and password");
}
algorithm = fileName.substring(0, idx);
fileName = fileName.substring(idx + 1);
idx = fileName.indexOf(':');
if (idx < 0) {
DbException.throwInternalError(fileName + " doesn't contain encryption password");
}
password = fileName.substring(0, idx);
fileName = fileName.substring(idx + 1);
return new String[] { algorithm, password, fileName };
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论