提交 ab2f646c authored 作者: Thomas Mueller's avatar Thomas Mueller

Improved compatibility with the Java 7 FileSystem abstraction.

上级 6e710caa
...@@ -506,9 +506,8 @@ public class Database implements DataHandler { ...@@ -506,9 +506,8 @@ public class Database implements DataHandler {
throw DbException.get(ErrorCode.FILE_VERSION_ERROR_1, throw DbException.get(ErrorCode.FILE_VERSION_ERROR_1,
"Old database: " + dataFileName + " - please convert the database to a SQL script and re-create it."); "Old database: " + dataFileName + " - please convert the database to a SQL script and re-create it.");
} }
if (existsPage && FileUtils.isReadOnly(pageFileName)) { if (existsPage && !FileUtils.canWrite(pageFileName)) {
// if it is already read-only because ACCESS_MODE_DATA=r readOnly = true;
readOnly = readOnly | FileUtils.isReadOnly(pageFileName);
} }
if (readOnly) { if (readOnly) {
traceSystem = new TraceSystem(null); traceSystem = new TraceSystem(null);
......
...@@ -296,7 +296,7 @@ public class TraceSystem implements TraceWriter { ...@@ -296,7 +296,7 @@ public class TraceSystem implements TraceWriter {
if (printWriter == null) { if (printWriter == null) {
try { try {
FileUtils.createDirectories(FileUtils.getParent(fileName)); FileUtils.createDirectories(FileUtils.getParent(fileName));
if (FileUtils.exists(fileName) && FileUtils.isReadOnly(fileName)) { if (FileUtils.exists(fileName) && !FileUtils.canWrite(fileName)) {
// read only database: don't log error if the trace file // read only database: don't log error if the trace file
// can't be opened // can't be opened
return false; return false;
......
...@@ -132,6 +132,10 @@ public class FileObjectDiskMapped implements FileObject { ...@@ -132,6 +132,10 @@ public class FileObjectDiskMapped implements FileObject {
return pos; return pos;
} }
public String toString() {
return FileSystemDiskNioMapped.PREFIX + name;
}
public synchronized long size() throws IOException { public synchronized long size() throws IOException {
return file.length(); return file.length();
} }
...@@ -161,9 +165,24 @@ public class FileObjectDiskMapped implements FileObject { ...@@ -161,9 +165,24 @@ public class FileObjectDiskMapped implements FileObject {
if (newLength >= size()) { if (newLength >= size()) {
return; return;
} }
setFileLength(newLength);
}
public synchronized void setFileLength(long newLength) throws IOException {
checkFileSizeLimit(newLength);
int oldPos = pos; int oldPos = pos;
unMap(); unMap();
file.setLength(newLength); for (int i = 0;; i++) {
try {
file.setLength(newLength);
break;
} catch (IOException e) {
if (i > 16 || e.toString().indexOf("user-mapped section open") < 0) {
throw e;
}
}
System.gc();
}
reMap(); reMap();
pos = (int) Math.min(newLength, oldPos); pos = (int) Math.min(newLength, oldPos);
} }
...@@ -176,11 +195,7 @@ public class FileObjectDiskMapped implements FileObject { ...@@ -176,11 +195,7 @@ public class FileObjectDiskMapped implements FileObject {
public synchronized void write(byte[] b, int off, int len) throws IOException { public synchronized void write(byte[] b, int off, int len) throws IOException {
// check if need to expand file // check if need to expand file
if (mapped.capacity() < pos + len) { if (mapped.capacity() < pos + len) {
int oldPos = pos; setFileLength(pos + len);
unMap();
file.setLength(pos + len);
reMap();
pos = oldPos;
} }
mapped.position(pos); mapped.position(pos);
mapped.put(b, off, len); mapped.put(b, off, len);
...@@ -210,8 +225,4 @@ public class FileObjectDiskMapped implements FileObject { ...@@ -210,8 +225,4 @@ public class FileObjectDiskMapped implements FileObject {
} }
} }
public String toString() {
return name;
}
} }
/*
* 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 FileObjectRec implements FileObject {
private final FilePathRec fs;
private final FileObject file;
private final String name;
FileObjectRec(FilePathRec 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();
}
}
\ No newline at end of file
/*
* 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.Collections;
import java.util.List;
import java.util.Map;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
/**
* A path to a file. It similar to the Java 7 <code>java.nio.file.Path</code>,
* but simpler, and works with older versions of Java. It also implements the
* relevant methods found in <code>java.nio.file.FileSystem</code> and
* <code>FileSystems</code>
*/
public abstract class FilePath {
private static final FilePath DEFAULT = new FilePathDisk();
private static Map<String, FilePath> providers;
/**
* The prefix for temporary files. See also TestClearReferences.
*/
private static String tempRandom;
private static long tempSequence;
protected String name;
/**
* Get the file path object for the given path.
*
* @param path the path
* @return the file path object
*/
public static FilePath get(String path) {
int index = path.indexOf(':');
if (index < 2) {
// use the default provider if no prefix or
// only a single character (drive name)
return DEFAULT.getPath(path);
}
String scheme = path.substring(0, index);
registerDefaultProviders();
FilePath p = providers.get(scheme);
if (p == null) {
// provider not found - use the default
p = DEFAULT;
}
return p.getPath(path);
}
private static void registerDefaultProviders() {
if (providers == null) {
Map<String, FilePath> map = Collections.synchronizedMap(New.<String, FilePath>hashMap());
for (String c : new String[] {
"org.h2.store.fs.FilePathMem",
"org.h2.store.fs.FilePathMemLZF",
"org.h2.store.fs.FilePathSplit",
"org.h2.store.fs.FilePathNio",
"org.h2.store.fs.FilePathNioMapped",
"org.h2.store.fs.FilePathZip"
}) {
try {
FilePath p = (FilePath) Class.forName(c).newInstance();
map.put(p.getScheme(), p);
} catch (Exception e) {
// ignore - the files may be excluded in purpose
}
}
providers = map;
}
}
/**
* Register a file provider.
*
* @param provider the file provider
*/
public static void register(FilePath provider) {
registerDefaultProviders();
providers.put(provider.getScheme(), provider);
}
/**
* Unregister a file provider.
*
* @param provider the file provider
*/
public static void unregister(FilePath provider) {
registerDefaultProviders();
providers.remove(provider.getScheme());
}
/**
* Get the size of a file in bytes
*
* @return the size in bytes
*/
public abstract long size();
/**
* Rename a file if this is allowed.
*
* @param newName the new fully qualified file name
*/
public abstract void moveTo(FilePath newName);
/**
* Create a new file.
*
* @return true if creating was successful
*/
public abstract boolean createFile();
/**
* Checks if a file exists.
*
* @return true if it exists
*/
public abstract boolean exists();
/**
* Delete a file or directory if it exists.
* Directories may only be deleted if they are empty.
*/
public abstract void delete();
/**
* List the files in the given directory.
*
* @return the list of fully qualified file names
*/
public abstract List<FilePath> listFiles();
/**
* Normalize a file name.
*
* @return the normalized file name
*/
public abstract FilePath getCanonicalPath();
/**
* Get the parent directory of a file or directory.
*
* @return the parent directory name
*/
public abstract FilePath getParent();
/**
* Check if it is a file or a directory.
*
* @return true if it is a directory
*/
public abstract boolean isDirectory();
/**
* Check if the file name includes a path.
*
* @return if the file name is absolute
*/
public abstract boolean isAbsolute();
/**
* Get the last modified date of a file
*
* @return the last modified date
*/
public abstract long lastModified();
/**
* Check if the file is writable.
*
* @return if the file is writable
*/
public abstract boolean canWrite();
/**
* Create a directory (all required parent directories already exist).
*/
public abstract void createDirectory();
/**
* Get the file or directory name (the last element of the path).
*
* @return the last element of the path
*/
public String getName() {
int idx = Math.max(name.indexOf(':'), name.lastIndexOf('/'));
return idx < 0 ? name : name.substring(idx + 1);
}
/**
* Check if a file starts with a given prefix.
*
* @param prefix the prefix
* @return true if it starts with the prefix
*/
public abstract boolean fileStartsWith(String prefix);
/**
* Create an output stream to write into the file.
*
* @param append if true, the file will grow, if false, the file will be
* truncated first
* @return the output stream
*/
public abstract OutputStream newOutputStream(boolean append);
/**
* Open a random access file object.
*
* @param mode the access mode. Supported are r, rw, rws, rwd
* @return the file object
*/
public abstract FileObject openFileObject(String mode) throws IOException;
/**
* Create an input stream to read from the file.
*
* @return the input stream
*/
public abstract InputStream newInputStream() throws IOException;
/**
* Disable the ability to write.
*
* @return true if the call was successful
*/
public abstract boolean setReadOnly();
/**
* Create a new temporary file.
*
* @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 FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
while (true) {
FilePath p = getPath(name + getNextTempFileNamePart(false) + suffix);
if (p.exists() || !p.createFile()) {
// in theory, the random number could collide
getNextTempFileNamePart(true);
continue;
}
p.openFileObject("rw").close();
return p;
}
}
/**
* 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++;
}
/**
* Get the string representation. The returned string can be used to
* construct a new object.
*
* @return the path as a string
*/
public String toString() {
return name;
}
/**
* Get the scheme (prefix) for this file provider.
* This is similar to <code>java.nio.file.spi.FileSystemProvider.getScheme</code>.
*
* @return the scheme
*/
public abstract String getScheme();
/**
* Convert a file to a path. This is similar to
* <code>java.nio.file.spi.FileSystemProvider.getPath</code>, but may
* return an object even if the scheme doesn't match in case of the the
* default file provider.
*
* @param path
* @return the file path
*/
public abstract FilePath getPath(String path);
/**
* Get the unwrapped file name (without wrapper prefixes if wrapping /
* delegating file systems are used).
*
* @return the unwrapped path
*/
public FilePath unwrap() {
return this;
}
}
/*
* 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.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.message.DbException;
import org.h2.util.IOUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
/**
* This file system stores files on disk.
* This is the most common file system.
*/
public class FilePathDisk extends FilePath {
private static final boolean IS_FILE_SYSTEM_CASE_INSENSITIVE = File.separatorChar == '\\';
private static final String CLASSPATH_PREFIX = "classpath:";
public FilePathDisk getPath(String path) {
FilePathDisk p = new FilePathDisk();
p.name = path;
return p;
}
public long size() {
return new File(name).length();
}
/**
* Translate the file name to the native format.
* This will expand the home directory (~).
*
* @param fileName the file name
* @return the native file name
*/
protected static String translateFileName(String fileName) {
return expandUserHomeDirectory(fileName);
}
/**
* Expand '~' to the user home directory. It is only be expanded if the ~
* stands alone, or is followed by / or \.
*
* @param fileName the file name
* @return the native file name
*/
public static String expandUserHomeDirectory(String fileName) {
if (fileName == null) {
return null;
}
boolean prefix = false;
if (fileName.startsWith("file:")) {
prefix = true;
fileName = fileName.substring("file:".length());
}
if (fileName.startsWith("~") && (fileName.length() == 1 || fileName.startsWith("~/") || fileName.startsWith("~\\"))) {
String userDir = SysProperties.USER_HOME;
fileName = userDir + fileName.substring(1);
}
return prefix ? "file:" + fileName : fileName;
}
public void moveTo(FilePath newName) {
File oldFile = new File(name);
File newFile = new File(newName.name);
if (oldFile.getAbsolutePath().equals(newFile.getAbsolutePath())) {
DbException.throwInternalError("rename file old=new");
}
if (!oldFile.exists()) {
throw DbException.get(ErrorCode.FILE_RENAME_FAILED_2,
name + " (not found)",
newName.name);
}
if (newFile.exists()) {
throw DbException.get(ErrorCode.FILE_RENAME_FAILED_2,
new String[] { name, newName + " (exists)" });
}
for (int i = 0; i < SysProperties.MAX_FILE_RETRY; i++) {
IOUtils.trace("rename", name + " >" + newName, null);
boolean ok = oldFile.renameTo(newFile);
if (ok) {
return;
}
wait(i);
}
throw DbException.get(ErrorCode.FILE_RENAME_FAILED_2, new String[]{name, newName.name});
}
private static void wait(int i) {
if (i == 8) {
System.gc();
}
try {
// sleep at most 256 ms
long sleep = Math.min(256, i * i);
Thread.sleep(sleep);
} catch (InterruptedException e) {
// ignore
}
}
public boolean createFile() {
File file = new File(name);
for (int i = 0; i < SysProperties.MAX_FILE_RETRY; i++) {
try {
return file.createNewFile();
} catch (IOException e) {
// 'access denied' is really a concurrent access problem
wait(i);
}
}
return false;
}
public boolean exists() {
return new File(name).exists();
}
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);
boolean ok = file.delete();
if (ok || !file.exists()) {
return;
}
wait(i);
}
throw DbException.get(ErrorCode.FILE_DELETE_FAILED_1, name);
}
public List<FilePath> listFiles() {
ArrayList<FilePath> list = New.arrayList();
File f = new File(name);
try {
String[] files = f.list();
if (files != null) {
String base = f.getCanonicalPath();
if (!base.endsWith(SysProperties.FILE_SEPARATOR)) {
base += SysProperties.FILE_SEPARATOR;
}
for (int i = 0, len = files.length; i < len; i++) {
list.add(getPath(base + files[i]));
}
}
return list;
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
public boolean canWrite() {
return canWriteInternal(new File(name));
}
public boolean setReadOnly() {
File f = new File(name);
return f.setReadOnly();
}
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();
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
public FilePath getParent() {
String p = new File(name).getParent();
return p == null ? null : getPath(p);
}
public boolean isDirectory() {
return new File(name).isDirectory();
}
public boolean isAbsolute() {
return new File(name).isAbsolute();
}
public long lastModified() {
return new File(name).lastModified();
}
private static boolean canWriteInternal(File file) {
try {
if (!file.canWrite()) {
return false;
}
} catch (Exception e) {
// workaround for GAE which throws a
// java.security.AccessControlException
return false;
}
// File.canWrite() does not respect windows user permissions,
// so we must try to open it using the mode "rw".
// See also http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4420020
RandomAccessFile r = null;
try {
r = new RandomAccessFile(file, "rw");
return true;
} catch (FileNotFoundException e) {
return false;
} finally {
if (r != null) {
try {
r.close();
} catch (IOException e) {
// ignore
}
}
}
}
public void createDirectory() {
File f = new File(name);
if (!f.exists()) {
File dir = new File(name);
for (int i = 0; i < SysProperties.MAX_FILE_RETRY; i++) {
if ((dir.exists() && dir.isDirectory()) || dir.mkdir()) {
return;
}
wait(i);
}
throw DbException.get(ErrorCode.FILE_CREATION_FAILED_1, name);
}
}
public String getName(String path) {
path = translateFileName(path);
return new File(path).getName();
}
public boolean fileStartsWith(String prefix) {
prefix = translateFileName(prefix);
String fileName = name;
if (IS_FILE_SYSTEM_CASE_INSENSITIVE) {
fileName = StringUtils.toUpperEnglish(fileName);
prefix = StringUtils.toUpperEnglish(prefix);
}
return fileName.startsWith(prefix);
}
public OutputStream newOutputStream(boolean append) {
try {
File file = new File(name);
File parent = file.getParentFile();
if (parent != null) {
FileUtils.createDirectories(parent.getAbsolutePath());
}
FileOutputStream out = new FileOutputStream(name, append);
IOUtils.trace("openFileOutputStream", name, out);
return out;
} catch (IOException e) {
freeMemoryAndFinalize();
try {
return new FileOutputStream(name);
} catch (IOException e2) {
throw DbException.convertIOException(e, name);
}
}
}
public InputStream newInputStream() throws IOException {
if (name.indexOf(':') > 1) {
// if the : is in position 1, a windows file access is assumed: C:.. or D:
if (name.startsWith(CLASSPATH_PREFIX)) {
String fileName = name.substring(CLASSPATH_PREFIX.length());
if (!fileName.startsWith("/")) {
fileName = "/" + fileName;
}
InputStream in = getClass().getResourceAsStream(fileName);
if (in == null) {
Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
}
if (in == null) {
throw new FileNotFoundException("resource " + fileName);
}
return in;
}
// otherwise an URL is assumed
URL url = new URL(name);
InputStream in = url.openStream();
return in;
}
FileInputStream in = new FileInputStream(name);
IOUtils.trace("openFileInputStream", name, in);
return in;
}
/**
* Call the garbage collection and run finalization. This close all files that
* were not closed, and are no longer referenced.
*/
static void freeMemoryAndFinalize() {
IOUtils.trace("freeMemoryAndFinalize", null, null);
Runtime rt = Runtime.getRuntime();
long mem = rt.freeMemory();
for (int i = 0; i < 16; i++) {
rt.gc();
long now = rt.freeMemory();
rt.runFinalization();
if (now == mem) {
break;
}
mem = now;
}
}
public FileObject openFileObject(String mode) throws IOException {
FileObjectDisk f;
try {
f = new FileObjectDisk(name, mode);
IOUtils.trace("openFileObject", name, f);
} catch (IOException e) {
freeMemoryAndFinalize();
try {
f = new FileObjectDisk(name, mode);
} catch (IOException e2) {
throw e;
}
}
return f;
}
protected boolean accepts() {
return true;
}
public String unwrap(String fileName) {
return fileName;
}
public String getScheme() {
return "file";
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
String fileName = translateFileName(name);
fileName += ".";
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());
}
while (true) {
File f = new File(dir, prefix + getNextTempFileNamePart(false) + suffix);
if (f.exists() || !f.createNewFile()) {
// in theory, the random number could collide
getNextTempFileNamePart(true);
continue;
}
if (deleteOnExit) {
try {
f.deleteOnExit();
} catch (Throwable e) {
// sometimes this throws a NullPointerException
// at java.io.DeleteOnExitHook.add(DeleteOnExitHook.java:33)
// we can ignore it
}
}
return get(f.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 java.util.List;
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 FilePathMem extends FilePath {
private static final TreeMap<String, FileObjectMemoryData> MEMORY_FILES = new TreeMap<String, FileObjectMemoryData>();
public FilePathMem getPath(String path) {
FilePathMem p = new FilePathMem();
p.name = getCanonicalPath(path);
return p;
}
public long size() {
return getMemoryFile().length();
}
public void moveTo(FilePath newName) {
synchronized (MEMORY_FILES) {
FileObjectMemoryData f = getMemoryFile();
f.setName(newName.name);
MEMORY_FILES.remove(name);
MEMORY_FILES.put(newName.name, f);
}
}
public boolean createFile() {
synchronized (MEMORY_FILES) {
if (exists()) {
return false;
}
getMemoryFile();
}
return true;
}
public boolean exists() {
if (isRoot()) {
return true;
}
synchronized (MEMORY_FILES) {
return MEMORY_FILES.get(name) != null;
}
}
public void delete() {
if (isRoot()) {
return;
}
synchronized (MEMORY_FILES) {
MEMORY_FILES.remove(name);
}
}
public List<FilePath> listFiles() {
ArrayList<FilePath> list = New.arrayList();
synchronized (MEMORY_FILES) {
for (String n : MEMORY_FILES.tailMap(name).keySet()) {
if (n.startsWith(name)) {
list.add(getPath(n));
} else {
break;
}
}
return list;
}
}
public boolean setReadOnly() {
return getMemoryFile().setReadOnly();
}
public boolean canWrite() {
return getMemoryFile().canWrite();
}
public FilePathMem getParent() {
int idx = name.lastIndexOf('/');
return idx < 0 ? null : getPath(name.substring(0, idx));
}
public boolean isDirectory() {
// TODO in memory file system currently
// does not really support directories
return false;
}
public boolean isAbsolute() {
// TODO relative files are not supported
return true;
}
public FilePathMem getCanonicalPath() {
return this;
}
public long lastModified() {
return getMemoryFile().getLastModified();
}
public void createDirectory() {
// TODO directories are not really supported
}
public boolean fileStartsWith(String prefix) {
prefix = getCanonicalPath(prefix);
return name.startsWith(prefix);
}
public OutputStream newOutputStream(boolean append) {
try {
FileObjectMemoryData obj = getMemoryFile();
FileObjectMemory m = new FileObjectMemory(obj, false);
return new FileObjectOutputStream(m, append);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
public InputStream newInputStream() {
FileObjectMemoryData obj = getMemoryFile();
FileObjectMemory m = new FileObjectMemory(obj, true);
return new FileObjectInputStream(m);
}
public FileObject openFileObject(String mode) {
FileObjectMemoryData obj = getMemoryFile();
return new FileObjectMemory(obj, "r".equals(mode));
}
private FileObjectMemoryData getMemoryFile() {
synchronized (MEMORY_FILES) {
FileObjectMemoryData m = MEMORY_FILES.get(name);
if (m == null) {
m = new FileObjectMemoryData(name, compressed());
MEMORY_FILES.put(name, m);
}
return m;
}
}
private boolean isRoot() {
return name.equals(getScheme());
}
protected boolean accepts(String fileName) {
return fileName.startsWith(getScheme());
}
public String unwrap(String fileName) {
return fileName;
}
private static 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 getScheme() {
return "memFS";
}
boolean compressed() {
return false;
}
}
/**
* A memory file system that compresses blocks to conserve memory.
*/
class FilePathMemLZF extends FilePathMem {
boolean compressed() {
return true;
}
public String getScheme() {
return "memLZF";
}
}
/*
* 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;
/**
* This file system stores files on disk and uses java.nio to access the files.
* This class uses FileChannel.
*/
public class FilePathNio extends FilePathWrapper {
/**
* 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);
}
/**
* Get the prefix for this file system.
*
* @return the prefix
*/
public String getScheme() {
return "nio";
}
}
/*
* 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;
/**
* This file system stores files on disk and uses java.nio to access the files.
* This class used memory mapped files.
*/
public class FilePathNioMapped extends FilePathNio {
protected FileObject open(String mode) throws IOException {
return new FileObjectDiskMapped(name, mode);
}
/**
* Get the prefix for this file system.
*
* @return the prefix
*/
public String getScheme() {
return "nioMapped";
}
}
/*
* 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 FilePathRec extends FilePathWrapper {
private static final FilePathRec INSTANCE = new FilePathRec();
private static Recorder recorder;
private boolean trace;
/**
* Register the file system.
*/
public static void register() {
FilePath.register(INSTANCE);
}
/**
* Set the recorder class.
*
* @param recorder the recorder
*/
public static void setRecorder(Recorder recorder) {
FilePathRec.recorder = recorder;
}
public boolean createFile() {
log(Recorder.CREATE_NEW_FILE, name);
return super.createFile();
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
log(Recorder.CREATE_TEMP_FILE, unwrap(name) + ":" + suffix + ":" + deleteOnExit + ":" + inTempDir);
return super.createTempFile(suffix, deleteOnExit, inTempDir);
}
public void delete() {
log(Recorder.DELETE, name);
super.delete();
}
public FileObject openFileObject(String mode) throws IOException {
return new FileObjectRec(this, super.openFileObject(mode), name);
}
public OutputStream newOutputStream(boolean append) {
log(Recorder.OPEN_OUTPUT_STREAM, name);
return super.newOutputStream(append);
}
public void moveTo(FilePath newPath) {
log(Recorder.RENAME, unwrap(name) + ":" + unwrap(newPath.name));
super.moveTo(newPath);
}
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(s)
*/
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);
}
}
/**
* Get the prefix for this file system.
*
* @return the prefix
*/
public String getScheme() {
return "rec";
}
}
/*
* 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 java.util.List;
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 FilePathSplit extends FilePathWrapper {
private static final String PART_SUFFIX = ".part";
protected String getPrefix() {
return getScheme() + ":" + parse(name)[0] + ":";
}
public FilePath unwrap(String fileName) {
return FilePath.get(parse(fileName)[1]);
}
public boolean setReadOnly() {
boolean result = false;
for (int i = 0;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
result = f.setReadOnly();
} else {
break;
}
}
return result;
}
public void delete() {
for (int i = 0;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
f.delete();
} else {
break;
}
}
}
public long lastModified() {
long lastModified = 0;
for (int i = 0;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
long l = f.lastModified();
lastModified = Math.max(lastModified, l);
} else {
break;
}
}
return lastModified;
}
public long size() {
long length = 0;
for (int i = 0;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
length += f.size();
} else {
break;
}
}
return length;
}
public ArrayList<FilePath> listFiles() {
List<FilePath> list = getBase().listFiles();
ArrayList<FilePath> newList = New.arrayList();
for (int i = 0, size = list.size(); i < size; i++) {
FilePath f = list.get(i);
if (!f.getName().endsWith(PART_SUFFIX)) {
newList.add(wrap(f));
}
}
return newList;
}
public InputStream newInputStream() throws IOException {
InputStream input = getBase().newInputStream();
for (int i = 1;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
InputStream i2 = f.newInputStream();
input = new SequenceInputStream(input, i2);
} else {
break;
}
}
return input;
}
public FileObject openFileObject(String mode) throws IOException {
ArrayList<FileObject> list = New.arrayList();
FileObject o = getBase().openFileObject(mode);
list.add(o);
for (int i = 1;; i++) {
FilePath f = getBase(i);
if (f.exists()) {
o = f.openFileObject(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) {
long defaultMaxLength = getDefaultMaxLength();
if (maxLength < defaultMaxLength) {
maxLength = defaultMaxLength;
}
} else {
if (maxLength == 0) {
closeAndThrow(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(i, array, o, maxLength);
}
}
o = array[array.length - 1];
long l = o.size();
length += l;
if (l > maxLength) {
closeAndThrow(array.length - 1, array, o, maxLength);
}
}
FileObjectSplit fo = new FileObjectSplit(name, mode, array, length, maxLength);
return fo;
}
private long getDefaultMaxLength() {
return 1L << Integer.decode(parse(name)[0]).intValue();
}
private void closeAndThrow(int id, FileObject[] array, FileObject o, long maxLength) throws IOException {
String message = "Expected file length: " + maxLength + " got: " + o.size() + " for " + getName(id);
for (FileObject f : array) {
f.close();
}
throw new IOException(message);
}
public OutputStream newOutputStream(boolean append) {
try {
return new FileObjectOutputStream(openFileObject("rw"), append);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
public void moveTo(FilePath path) {
FilePathSplit newName = (FilePathSplit) path;
for (int i = 0;; i++) {
FilePath o = getBase(i);
if (o.exists()) {
o.moveTo(newName.getBase(i));
} else {
break;
}
}
}
/**
* Split the file name into size and base file name.
*
* @param fileName the file name
* @return an array with size and file name
*/
private String[] parse(String fileName) {
if (!fileName.startsWith(getScheme())) {
DbException.throwInternalError(fileName + " doesn't start with " + getScheme());
}
fileName = fileName.substring(getScheme().length() + 1);
String size;
if (fileName.length() > 0 && Character.isDigit(fileName.charAt(0))) {
int idx = fileName.indexOf(':');
size = fileName.substring(0, idx);
try {
fileName = fileName.substring(idx + 1);
} catch (NumberFormatException e) {
// ignore
}
} else {
size = Long.toString(SysProperties.SPLIT_FILE_SIZE_SHIFT);
}
return new String[] { size, fileName };
}
/**
* Get the file name of a part file.
*
* @param id the part id
* @return the file name including the part id
*/
private FilePath getBase(int id) {
return FilePath.get(getName(id));
}
private String getName(int id) {
return id > 0 ? getBase().name + "." + id + PART_SUFFIX : getBase().name;
}
public String getScheme() {
return "split";
}
}
\ No newline at end of file
/*
* 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.List;
import org.h2.message.DbException;
/**
* The base class for wrapping / delegating file systems such as
* the split file system.
*/
public abstract class FilePathWrapper extends FilePath {
private FilePath base;
public FilePathWrapper getPath(String path) {
return create(path, unwrap(path));
}
public FilePathWrapper wrap(FilePath base) {
return create(getPrefix() + base.name, base);
}
public FilePath unwrap() {
return unwrap(name);
}
private FilePathWrapper create(String path, FilePath base) {
try {
FilePathWrapper p = getClass().newInstance();
p.name = path;
p.base = base;
return p;
} catch (Exception e) {
throw DbException.convert(e);
}
}
protected String getPrefix() {
return getScheme() + ":";
}
protected FilePath unwrap(String path) {
return FilePath.get(path.substring(getScheme().length() + 1));
}
protected FilePath getBase() {
return base;
}
public boolean canWrite() {
return base.canWrite();
}
public void createDirectory() {
base.createDirectory();
}
public boolean createFile() {
return base.createFile();
}
public void delete() {
base.delete();
}
public boolean exists() {
return base.exists();
}
public boolean fileStartsWith(String prefix) {
return name.startsWith(prefix);
}
public FilePath getParent() {
return wrap(base.getParent());
}
public boolean isAbsolute() {
return base.isAbsolute();
}
public boolean isDirectory() {
return base.isDirectory();
}
public long lastModified() {
return base.lastModified();
}
public FilePath getCanonicalPath() {
return wrap(base.getCanonicalPath());
}
public List<FilePath> listFiles() {
List<FilePath> list = base.listFiles();
for (int i = 0, len = list.size(); i < len; i++) {
list.set(i, wrap(list.get(i)));
}
return list;
}
public void moveTo(FilePath newName) {
base.moveTo(((FilePathWrapper) newName).base);
}
public InputStream newInputStream() throws IOException {
return base.newInputStream();
}
public OutputStream newOutputStream(boolean append) {
return base.newOutputStream(append);
}
public FileObject openFileObject(String mode) throws IOException {
return base.openFileObject(mode);
}
public boolean setReadOnly() {
return base.setReadOnly();
}
public long size() {
return base.size();
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
return wrap(base.createTempFile(suffix, deleteOnExit, inTempDir));
}
}
/*
* 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 FilePathZip extends FilePath {
public FilePathZip getPath(String path) {
FilePathZip p = new FilePathZip();
p.name = path;
return p;
}
public void createDirectory() {
// ignore
}
public boolean createFile() {
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");
}
public boolean exists() {
try {
String entryName = getEntryName();
if (entryName.length() == 0) {
return true;
}
ZipFile file = openZipFile();
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() {
return 0;
}
public FilePath getParent() {
int idx = name.lastIndexOf('/');
return idx < 0 ? null : getPath(name.substring(0, idx));
}
public boolean isAbsolute() {
return true;
}
public boolean isDirectory() {
try {
String entryName = getEntryName();
if (entryName.length() == 0) {
return true;
}
ZipFile file = openZipFile();
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() {
return false;
}
public boolean setReadOnly() {
return true;
}
public long size() {
try {
ZipFile file = openZipFile();
ZipEntry entry = file.getEntry(getEntryName());
return entry == null ? 0 : entry.getSize();
} catch (IOException e) {
return 0;
}
}
public ArrayList<FilePath> listFiles() {
String path = name;
ArrayList<FilePath> list = New.arrayList();
try {
if (path.indexOf('!') < 0) {
path += "!";
}
if (!path.endsWith("/")) {
path += "/";
}
ZipFile file = openZipFile();
String dirName = getEntryName();
String prefix = path.substring(0, path.length() - dirName.length());
Enumeration<? extends ZipEntry> en = file.entries();
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(getPath(prefix + name));
}
}
return list;
} catch (IOException e) {
throw DbException.convertIOException(e, "listFiles " + path);
}
}
public InputStream newInputStream() throws IOException {
return new FileObjectInputStream(openFileObject("r"));
}
public FileObject openFileObject(String mode) throws IOException {
ZipFile file = openZipFile();
ZipEntry entry = file.getEntry(getEntryName());
if (entry == null) {
throw new FileNotFoundException(name);
}
return new FileObjectZip(file, entry);
}
public OutputStream newOutputStream(boolean append) {
throw DbException.getUnsupportedException("write");
}
public void moveTo(FilePath newName) {
throw DbException.getUnsupportedException("write");
}
private static String translateFileName(String fileName) {
if (fileName.startsWith("zip:")) {
fileName = fileName.substring("zip:".length());
}
int idx = fileName.indexOf('!');
if (idx >= 0) {
fileName = fileName.substring(0, idx);
}
return FileSystemDisk.expandUserHomeDirectory(fileName);
}
public FilePath getCanonicalPath() {
return this;
}
private String getEntryName() {
int idx = name.indexOf('!');
String fileName;
if (idx <= 0) {
fileName = "";
} else {
fileName = name.substring(idx + 1);
}
fileName = fileName.replace('\\', '/');
if (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
return fileName;
}
private ZipFile openZipFile() throws IOException {
String fileName = translateFileName(name);
return new ZipFile(fileName);
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
if (!inTempDir) {
throw new IOException("File system is read-only");
}
return new FilePathDisk().getPath(name).createTempFile(suffix, deleteOnExit, true);
}
// protected boolean accepts(String fileName) {
// return fileName.startsWith(PREFIX);
// }
public boolean fileStartsWith(String prefix) {
return name.startsWith(prefix);
}
public String getScheme() {
return "zip";
}
}
...@@ -143,14 +143,6 @@ public abstract class FileSystem { ...@@ -143,14 +143,6 @@ public abstract class FileSystem {
*/ */
public abstract String[] listFiles(String directory); public abstract String[] listFiles(String directory);
/**
* Check if a file is read-only.
*
* @param fileName the file name
* @return if it is read only
*/
public abstract boolean isReadOnly(String fileName);
/** /**
* Normalize a file name. * Normalize a file name.
* *
......
...@@ -162,7 +162,7 @@ public class FileSystemDisk extends FileSystem { ...@@ -162,7 +162,7 @@ public class FileSystemDisk extends FileSystem {
dir = new File(Utils.getProperty("java.io.tmpdir", ".")); dir = new File(Utils.getProperty("java.io.tmpdir", "."));
} else { } else {
dir = new File(name).getAbsoluteFile().getParentFile(); dir = new File(name).getAbsoluteFile().getParentFile();
IOUtils.mkdirs(dir); FileUtils.createDirectories(dir.getAbsolutePath());
} }
File f; File f;
while (true) { while (true) {
...@@ -207,12 +207,6 @@ public class FileSystemDisk extends FileSystem { ...@@ -207,12 +207,6 @@ public class FileSystemDisk extends FileSystem {
} }
} }
public boolean isReadOnly(String fileName) {
fileName = translateFileName(fileName);
File f = new File(fileName);
return f.exists() && !canWriteInternal(f);
}
public boolean canWrite(String fileName) { public boolean canWrite(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
return canWriteInternal(new File(fileName)); return canWriteInternal(new File(fileName));
......
...@@ -103,14 +103,14 @@ public class FileSystemMemory extends FileSystem { ...@@ -103,14 +103,14 @@ public class FileSystemMemory extends FileSystem {
} }
} }
public boolean isReadOnly(String fileName) {
return !getMemoryFile(fileName).canWrite();
}
public boolean setReadOnly(String fileName) { public boolean setReadOnly(String fileName) {
return getMemoryFile(fileName).setReadOnly(); return getMemoryFile(fileName).setReadOnly();
} }
public boolean canWrite(String fileName) {
return getMemoryFile(fileName).canWrite();
}
public String getCanonicalPath(String fileName) { public String getCanonicalPath(String fileName) {
fileName = fileName.replace('\\', '/'); fileName = fileName.replace('\\', '/');
int idx = fileName.indexOf(':') + 1; int idx = fileName.indexOf(':') + 1;
...@@ -144,10 +144,6 @@ public class FileSystemMemory extends FileSystem { ...@@ -144,10 +144,6 @@ public class FileSystemMemory extends FileSystem {
return getMemoryFile(fileName).getLastModified(); return getMemoryFile(fileName).getLastModified();
} }
public boolean canWrite(String fileName) {
return true;
}
public void createDirectory(String directoryName) { public void createDirectory(String directoryName) {
// TODO directories are not really supported // TODO directories are not really supported
} }
......
...@@ -24,10 +24,6 @@ public abstract class FileSystemWrapper extends FileSystem { ...@@ -24,10 +24,6 @@ public abstract class FileSystemWrapper extends FileSystem {
*/ */
protected abstract String getPrefix(); protected abstract String getPrefix();
public boolean canWrite(String fileName) {
return FileUtils.canWrite(unwrap(fileName));
}
public boolean setReadOnly(String fileName) { public boolean setReadOnly(String fileName) {
return FileUtils.setReadOnly(unwrap(fileName)); return FileUtils.setReadOnly(unwrap(fileName));
} }
...@@ -77,8 +73,8 @@ public abstract class FileSystemWrapper extends FileSystem { ...@@ -77,8 +73,8 @@ public abstract class FileSystemWrapper extends FileSystem {
return FileUtils.isDirectory(unwrap(fileName)); return FileUtils.isDirectory(unwrap(fileName));
} }
public boolean isReadOnly(String fileName) { public boolean canWrite(String fileName) {
return FileUtils.isReadOnly(unwrap(fileName)); return FileUtils.canWrite(unwrap(fileName));
} }
public long size(String fileName) { public long size(String fileName) {
......
...@@ -29,10 +29,6 @@ public class FileSystemZip extends FileSystem { ...@@ -29,10 +29,6 @@ public class FileSystemZip extends FileSystem {
FileSystem.register(new FileSystemZip()); FileSystem.register(new FileSystemZip());
} }
public boolean canWrite(String fileName) {
return false;
}
public void createDirectory(String directoryName) { public void createDirectory(String directoryName) {
// ignore // ignore
} }
...@@ -124,8 +120,8 @@ public class FileSystemZip extends FileSystem { ...@@ -124,8 +120,8 @@ public class FileSystemZip extends FileSystem {
} }
} }
public boolean isReadOnly(String fileName) { public boolean canWrite(String fileName) {
return true; return false;
} }
public boolean setReadOnly(String fileName) { public boolean setReadOnly(String fileName) {
......
...@@ -3,6 +3,8 @@ package org.h2.store.fs; ...@@ -3,6 +3,8 @@ package org.h2.store.fs;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
/** /**
...@@ -191,10 +193,10 @@ public class FileUtils { ...@@ -191,10 +193,10 @@ public class FileUtils {
return getFileSystem(fileName).newOutputStream(fileName, append); return getFileSystem(fileName).newOutputStream(fileName, append);
} }
// special methods =======================================
/** /**
* Check if the file is writable. * Check if the file is writable.
* This method is similar to Java 7
* <code>java.nio.file.Path.checkAccess(AccessMode.WRITE)</code>
* *
* @param fileName the file name * @param fileName the file name
* @return if the file is writable * @return if the file is writable
...@@ -203,20 +205,10 @@ public class FileUtils { ...@@ -203,20 +205,10 @@ public class FileUtils {
return getFileSystem(fileName).canWrite(fileName); return getFileSystem(fileName).canWrite(fileName);
} }
/** // special methods =======================================
* Check if a file is read-only.
* This method is similar to Java 7
* <code>java.nio.file.Path.checkAccess(AccessMode.WRITE)</code>
*
* @param fileName the file name
* @return if it is read only
*/
public static boolean isReadOnly(String fileName) {
return getFileSystem(fileName).isReadOnly(fileName);
}
/** /**
* Disable the ability to write. * Disable the ability to write. The file can still be deleted afterwards.
* *
* @param fileName the file name * @param fileName the file name
* @return true if the call was successful * @return true if the call was successful
...@@ -292,10 +284,18 @@ public class FileUtils { ...@@ -292,10 +284,18 @@ public class FileUtils {
* @param dir the directory name * @param dir the directory name
*/ */
public static void createDirectories(String dir) { public static void createDirectories(String dir) {
if (dir != null && !FileUtils.exists(dir)) { if (dir != null) {
String parent = FileUtils.getParent(dir); if (FileUtils.exists(dir)) {
createDirectories(parent); if (!FileUtils.isDirectory(dir)) {
createDirectory(dir); DbException.get(ErrorCode.FILE_CREATION_FAILED_1,
"Could not create directory, " +
"because a file with the same name already exists: " + dir);
}
} else {
String parent = FileUtils.getParent(dir);
createDirectories(parent);
createDirectory(dir);
}
} }
} }
......
/*
* 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.List;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.IOUtils;
/**
* This utility class contains utility functions that use the file system abstraction.
*/
public class FileUtils2 {
/**
* Checks if a file exists.
* This method is similar to Java 7 <code>java.nio.file.Path.exists</code>.
*
* @param fileName the file name
* @return true if it exists
*/
public static boolean exists(String fileName) {
return FilePath.get(fileName).exists();
}
/**
* Create a directory (all required parent directories must already exist).
* This method is similar to Java 7 <code>java.nio.file.Path.createDirectory</code>.
*
* @param directoryName the directory name
*/
public static void createDirectory(String directoryName) {
FilePath.get(directoryName).createDirectory();
}
/**
* Create a new file.
* This method is similar to Java 7 <code>java.nio.file.Path.createFile</code>, but returns
* false instead of throwing a exception if the file already existed.
*
* @param fileName the file name
* @return true if creating was successful
*/
public static boolean createFile(String fileName) {
return FilePath.get(fileName).createFile();
}
/**
* Delete a file or directory if it exists.
* Directories may only be deleted if they are empty.
* This method is similar to Java 7 <code>java.nio.file.Path.deleteIfExists</code>.
*
* @param path the file or directory name
*/
public static void delete(String path) {
FilePath.get(path).delete();
}
/**
* Normalize a file name.
* This method is similar to Java 7 <code>java.nio.file.Path.toRealPath</code>.
*
* @param fileName the file name
* @return the normalized file name
*/
public static String getCanonicalPath(String fileName) {
return FilePath.get(fileName).getCanonicalPath().toString();
}
/**
* Get the parent directory of a file or directory.
* This method returns null if there is no parent.
* This method is similar to Java 7 <code>java.nio.file.Path.getParent</code>.
*
* @param fileName the file or directory name
* @return the parent directory name
*/
public static String getParent(String fileName) {
FilePath p = FilePath.get(fileName).getParent();
return p == null ? null : p.toString();
}
/**
* Check if the file name includes a path.
* This method is similar to Java 7 <code>java.nio.file.Path.isAbsolute</code>.
*
* @param fileName the file name
* @return if the file name is absolute
*/
public static boolean isAbsolute(String fileName) {
return FilePath.get(fileName).isAbsolute();
}
/**
* Rename a file if this is allowed.
* This method is similar to Java 7 <code>java.nio.file.Path.moveTo</code>.
*
* @param oldName the old fully qualified file name
* @param newName the new fully qualified file name
*/
public static void moveTo(String oldName, String newName) {
FilePath.get(oldName).moveTo(FilePath.get(newName));
}
/**
* Get the file or directory name (the last element of the path).
* This method is similar to Java 7 <code>java.nio.file.Path.getName</code>.
*
* @param path the directory and file name
* @return just the file name
*/
public static String getName(String path) {
return FilePath.get(path).getName();
}
/**
* List the files in the given directory.
* This method is similar to Java 7 <code>java.nio.file.Path.newDirectoryStream</code>.
*
* @param path the directory
* @return the list of fully qualified file names
*/
public static String[] listFiles(String path) {
List<FilePath> list = FilePath.get(path).listFiles();
String[] array = new String[list.size()];
for (int i = 0, len = list.size(); i < len; i++) {
array[i] = list.get(i).toString();
}
return array;
}
/**
* Get the last modified date of a file.
* This method is similar to Java 7
* <code>java.nio.file.attribute.Attributes.readBasicFileAttributes(file).lastModified().toMillis()</code>
*
* @param fileName the file name
* @return the last modified date
*/
public static long lastModified(String fileName) {
return FilePath.get(fileName).lastModified();
}
/**
* Get the size of a file in bytes
* This method is similar to Java 7
* <code>java.nio.file.attribute.Attributes.readBasicFileAttributes(file).size()</code>
*
* @param fileName the file name
* @return the size in bytes
*/
public static long size(String fileName) {
return FilePath.get(fileName).size();
}
/**
* Check if it is a file or a directory.
* <code>java.nio.file.attribute.Attributes.readBasicFileAttributes(file).isDirectory()</code>
*
* @param fileName the file or directory name
* @return true if it is a directory
*/
public static boolean isDirectory(String fileName) {
return FilePath.get(fileName).isDirectory();
}
/**
* Open a random access file object.
* This method is similar to Java 7 <code>java.nio.channels.FileChannel.open</code>.
*
* @param fileName the file name
* @param mode the access mode. Supported are r, rw, rws, rwd
* @return the file object
*/
public static FileObject openFileObject(String fileName, String mode) throws IOException {
return FilePath.get(fileName).openFileObject(mode);
}
/**
* Create an input stream to read from the file.
* This method is similar to Java 7 <code>java.nio.file.Path.newInputStream</code>.
*
* @param fileName the file name
* @return the input stream
*/
public static InputStream newInputStream(String fileName) throws IOException {
return FilePath.get(fileName).newInputStream();
}
/**
* Create an output stream to write into the file.
* This method is similar to Java 7 <code>java.nio.file.Path.newOutputStream</code>.
*
* @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 static OutputStream newOutputStream(String fileName, boolean append) {
return FilePath.get(fileName).newOutputStream(append);
}
/**
* Check if the file is writable.
* This method is similar to Java 7
* <code>java.nio.file.Path.checkAccess(AccessMode.WRITE)</code>
*
* @param fileName the file name
* @return if the file is writable
*/
public static boolean canWrite(String fileName) {
return FilePath.get(fileName).canWrite();
}
// special methods =======================================
/**
* Disable the ability to write. The file can still be deleted afterwards.
*
* @param fileName the file name
* @return true if the call was successful
*/
public static boolean setReadOnly(String fileName) {
return FilePath.get(fileName).setReadOnly();
}
/**
* Get the unwrapped file name (without wrapper prefixes if wrapping /
* delegating file systems are used).
*
* @param fileName the file name
* @return the unwrapped
*/
public static String unwrap(String fileName) {
return FilePath.get(fileName).unwrap().toString();
}
/**
* 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 static boolean fileStartsWith(String fileName, String prefix) {
return FilePath.get(fileName).fileStartsWith(prefix);
}
// utility methods =======================================
/**
* Delete a directory or file and all subdirectories and files.
*
* @param path the path
* @param tryOnly whether errors should be ignored
*/
public static void deleteRecursive(String path, boolean tryOnly) {
if (exists(path)) {
if (isDirectory(path)) {
for (String s : listFiles(path)) {
deleteRecursive(s, tryOnly);
}
}
if (tryOnly) {
tryDelete(path);
} else {
delete(path);
}
}
}
/**
* Create the directory and all required parent directories.
*
* @param dir the directory name
*/
public static void createDirectories(String dir) {
if (dir != null) {
if (exists(dir)) {
if (!isDirectory(dir)) {
DbException.get(ErrorCode.FILE_CREATION_FAILED_1,
"Could not create directory, " +
"because a file with the same name already exists: " + dir);
}
} else {
String parent = getParent(dir);
createDirectories(parent);
createDirectory(dir);
}
}
}
/**
* Copy a file from one directory to another, or to another file.
*
* @param original the original file name
* @param copy the file name of the copy
*/
public static void copy(String original, String copy) throws IOException {
InputStream in = newInputStream(original);
OutputStream out = newOutputStream(copy, false);
IOUtils.copyAndClose(in, out);
}
/**
* Try to delete a file (ignore errors).
*
* @param fileName the file name
* @return true if it worked
*/
public static boolean tryDelete(String fileName) {
try {
FilePath.get(fileName).delete();
return true;
} catch (Exception e) {
return false;
}
}
/**
* 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 static String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
return FilePath.get(prefix).createTempFile(suffix, deleteOnExit, inTempDir).toString();
}
}
...@@ -11,7 +11,6 @@ import java.io.BufferedWriter; ...@@ -11,7 +11,6 @@ import java.io.BufferedWriter;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
...@@ -449,32 +448,6 @@ public class IOUtils { ...@@ -449,32 +448,6 @@ public class IOUtils {
} }
} }
/**
* Create the directory and all parent directories if required.
*
* @param directory the directory
* @throws IOException
*/
public static void mkdirs(File directory) throws IOException {
int todo;
// loop, to deal with race conditions (if another thread creates or
// deletes the same directory at the same time).
for (int i = 0; i < 5; i++) {
if (directory.exists()) {
if (directory.isDirectory()) {
return;
}
throw new IOException("Could not create directory, " +
"because a file with the same name already exists: " +
directory.getAbsolutePath());
}
if (directory.mkdirs()) {
return;
}
}
throw new IOException("Could not create directory: " + directory.getAbsolutePath());
}
/** /**
* Trace input or output operations if enabled. * Trace input or output operations if enabled.
* *
......
...@@ -139,11 +139,7 @@ public class SourceCompiler { ...@@ -139,11 +139,7 @@ public class SourceCompiler {
File dir = new File(compileDir); File dir = new File(compileDir);
if (packageName != null) { if (packageName != null) {
dir = new File(dir, packageName.replace('.', '/')); dir = new File(dir, packageName.replace('.', '/'));
try { FileUtils.createDirectories(dir.getAbsolutePath());
IOUtils.mkdirs(dir);
} catch (IOException e) {
throw DbException.convertIOException(e, compileDir);
}
} }
File javaFile = new File(dir, className + ".java"); File javaFile = new File(dir, className + ".java");
File classFile = new File(dir, className + ".class"); File classFile = new File(dir, className + ".class");
......
...@@ -637,7 +637,7 @@ public class TestLinkedTable extends TestBase { ...@@ -637,7 +637,7 @@ public class TestLinkedTable extends TestBase {
String name = FileUtils.getName(file); String name = FileUtils.getName(file);
if ((name.startsWith("testLinkedTableInReadOnlyDb")) && (!name.endsWith(".trace.db"))) { if ((name.startsWith("testLinkedTableInReadOnlyDb")) && (!name.endsWith(".trace.db"))) {
FileUtils.setReadOnly(file); FileUtils.setReadOnly(file);
boolean isReadOnly = FileUtils.isReadOnly(file); boolean isReadOnly = !FileUtils.canWrite(file);
if (!isReadOnly) { if (!isReadOnly) {
fail("File " + file + " is not read only. Can't test it."); fail("File " + file + " is not read only. Can't test it.");
} }
......
...@@ -440,7 +440,7 @@ public class TestLob extends TestBase { ...@@ -440,7 +440,7 @@ public class TestLob extends TestBase {
private void testTempFilesDeleted() throws Exception { private void testTempFilesDeleted() throws Exception {
String[] list; String[] list;
FileUtils.deleteRecursive(TEMP_DIR, true); FileUtils.deleteRecursive(TEMP_DIR, true);
IOUtils.mkdirs(new File(TEMP_DIR)); FileUtils.createDirectories(TEMP_DIR);
list = FileUtils.listFiles(TEMP_DIR); list = FileUtils.listFiles(TEMP_DIR);
if (list.length > 0) { if (list.length > 0) {
fail("Unexpected temp file: " + list[0]); fail("Unexpected temp file: " + list[0]);
......
...@@ -170,10 +170,6 @@ public class FileSystemDatabase extends FileSystem { ...@@ -170,10 +170,6 @@ public class FileSystemDatabase extends FileSystem {
return new RuntimeException(e.toString(), e); return new RuntimeException(e.toString(), e);
} }
public boolean canWrite(String fileName) {
return true;
}
public void createDirectory(String directoryName) { public void createDirectory(String directoryName) {
directoryName = unwrap(directoryName); directoryName = unwrap(directoryName);
try { try {
...@@ -272,8 +268,8 @@ public class FileSystemDatabase extends FileSystem { ...@@ -272,8 +268,8 @@ public class FileSystemDatabase extends FileSystem {
} }
} }
public boolean isReadOnly(String fileName) { public boolean canWrite(String fileName) {
return false; return true;
} }
public boolean setReadOnly(String fileName) { public boolean setReadOnly(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: H2 Group
*/
package org.h2.test.unit;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.sql.Connection;
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.FilePathCrypt;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FileSystemMemory;
import org.h2.store.fs.FileUtils2;
import org.h2.test.TestBase;
import org.h2.test.utils.DebugFilePath;
import org.h2.tools.Backup;
import org.h2.tools.DeleteDbFiles;
/**
* Tests various file system.
*/
public class TestFilePath extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase test = TestBase.createCaller().init();
// test.config.traceTest = true;
test.test();
}
public void test() throws Exception {
testMemFsDir();
testClasspath();
FilePathCrypt.register();
DebugFilePath.register().setTrace(true);
testFileSystem("crypt:aes:x:" + getBaseDir() + "/fs");
testSimpleExpandTruncateSize();
testSplitDatabaseInZip();
testDatabaseInMemFileSys();
testDatabaseInJar();
// set default part size to 1 << 10
String f = "split:10:" + getBaseDir() + "/fs";
FileUtils2.getCanonicalPath(f);
testFileSystem(getBaseDir() + "/fs");
testFileSystem("memFS:");
testFileSystem("memLZF:");
testUserHome();
try {
FilePathCrypt.register();
testFileSystem("crypt:aes:x:" + getBaseDir() + "/fs");
testFileSystem("nio:" + getBaseDir() + "/fs");
testFileSystem("nioMapped:" + getBaseDir() + "/fs");
if (!config.splitFileSystem) {
testFileSystem("split:" + getBaseDir() + "/fs");
testFileSystem("split:nioMapped:" + getBaseDir() + "/fs");
}
} catch (Exception e) {
e.printStackTrace();
throw e;
} catch (Error e) {
e.printStackTrace();
throw e;
} finally {
FileUtils2.delete(getBaseDir() + "/fs");
}
}
private void testMemFsDir() throws IOException {
FileUtils2.newOutputStream("memFS:data/test/a.txt", false).close();
String[] list = FileUtils2.listFiles("memFS:data/test");
assertEquals(1, list.length);
FileUtils2.deleteRecursive("memFS:", false);
}
private void testClasspath() throws IOException {
String resource = "org/h2/test/testSimple.in.txt";
InputStream in;
in = getClass().getResourceAsStream("/" + resource);
assertTrue(in != null);
in.close();
in = getClass().getClassLoader().getResourceAsStream(resource);
assertTrue(in != null);
in.close();
in = FileUtils2.newInputStream("classpath:" + resource);
assertTrue(in != null);
in.close();
in = FileUtils2.newInputStream("classpath:/" + resource);
assertTrue(in != null);
in.close();
}
private void testSimpleExpandTruncateSize() throws Exception {
String f = "memFS:" + getBaseDir() + "/fs/test.data";
FileUtils2.createDirectories("memFS:" + getBaseDir() + "/fs");
FileObject o = FileUtils2.openFileObject(f, "rw");
o.position(4000);
o.write(new byte[1], 0, 1);
o.tryLock();
o.truncate(0);
o.releaseLock();
o.close();
}
private void testSplitDatabaseInZip() throws SQLException {
String dir = getBaseDir() + "/fs";
FileUtils2.deleteRecursive(dir, false);
Connection conn;
Statement stat;
conn = DriverManager.getConnection("jdbc:h2:split:18:"+dir+"/test");
stat = conn.createStatement();
stat.execute(
"create table test(id int primary key, name varchar) " +
"as select x, space(10000) from system_range(1, 100)");
stat.execute("shutdown defrag");
conn.close();
Backup.execute(dir + "/test.zip", dir, "", true);
DeleteDbFiles.execute("split:" + dir, "test", true);
conn = DriverManager.getConnection(
"jdbc:h2:split:zip:"+dir+"/test.zip!/test");
conn.createStatement().execute("select * from test where id=1");
conn.close();
FileUtils2.deleteRecursive(dir, false);
}
private void testDatabaseInMemFileSys() throws SQLException {
org.h2.Driver.load();
deleteDb("fsMem");
String url = "jdbc:h2:" + getBaseDir() + "/fsMem";
Connection conn = DriverManager.getConnection(url, "sa", "sa");
conn.createStatement().execute("CREATE TABLE TEST AS SELECT * FROM DUAL");
conn.createStatement().execute("BACKUP TO '" + getBaseDir() + "/fsMem.zip'");
conn.close();
org.h2.tools.Restore.main("-file", getBaseDir() + "/fsMem.zip", "-dir", "memFS:");
conn = DriverManager.getConnection("jdbc:h2:memFS:fsMem", "sa", "sa");
ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM TEST");
rs.close();
conn.close();
deleteDb("fsMem");
FileUtils2.delete(getBaseDir() + "/fsMem.zip");
}
private void testDatabaseInJar() throws SQLException {
if (getBaseDir().indexOf(':') > 0) {
return;
}
if (config.networked) {
return;
}
org.h2.Driver.load();
String url = "jdbc:h2:" + getBaseDir() + "/fsJar";
Connection conn = DriverManager.getConnection(url, "sa", "sa");
Statement stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar, b blob, c clob)");
stat.execute("insert into test values(1, 'Hello', SECURE_RAND(2000), space(2000))");
ResultSet rs;
rs = stat.executeQuery("select * from test");
rs.next();
byte[] b1 = rs.getBytes(3);
String s1 = rs.getString(4);
conn.close();
conn = DriverManager.getConnection(url, "sa", "sa");
stat = conn.createStatement();
stat.execute("backup to '" + getBaseDir() + "/fsJar.zip'");
conn.close();
deleteDb("fsJar");
for (String f : FileUtils2.listFiles("zip:" + getBaseDir() + "/fsJar.zip")) {
assertTrue(FileUtils2.isAbsolute(f));
assertTrue(!FileUtils2.isDirectory(f));
assertTrue(FileUtils2.size(f) > 0);
assertTrue(f.endsWith(FileUtils2.getName(f)));
}
String urlJar = "jdbc:h2:zip:" + getBaseDir() + "/fsJar.zip!/fsJar";
conn = DriverManager.getConnection(urlJar, "sa", "sa");
stat = conn.createStatement();
rs = stat.executeQuery("select * from test");
rs.next();
assertEquals(1, rs.getInt(1));
assertEquals("Hello", rs.getString(2));
byte[] b2 = rs.getBytes(3);
String s2 = rs.getString(4);
assertEquals(2000, b2.length);
assertEquals(2000, s2.length());
assertEquals(b1, b2);
assertEquals(s1, s2);
assertFalse(rs.next());
conn.close();
FileUtils2.delete(getBaseDir() + "/fsJar.zip");
}
private void testUserHome() {
String fileName = FileUtils2.getCanonicalPath("~/test");
String userDir = System.getProperty("user.home");
assertTrue(fileName.startsWith(userDir));
}
private void testFileSystem(String fsBase) throws Exception {
testSimple(fsBase);
testTempFile(fsBase);
testRandomAccess(fsBase);
}
private void testSimple(final String fsBase) throws Exception {
long time = System.currentTimeMillis();
for (String s : FileUtils2.listFiles(fsBase)) {
FileUtils2.delete(s);
}
FileUtils2.createDirectories(fsBase + "/test");
FileUtils2.delete(fsBase + "/test");
FileUtils2.delete(fsBase + "/test2");
assertTrue(FileUtils2.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(FileUtils2.canWrite(fsBase + "/test"));
FileObject fo = FileUtils2.openFileObject(fsBase + "/test", "rw");
byte[] buffer = new byte[10000];
Random random = new Random(1);
random.nextBytes(buffer);
fo.write(buffer, 0, 10000);
assertEquals(10000, fo.size());
fo.position(20000);
assertEquals(20000, fo.position());
assertThrows(EOFException.class, fo).readFully(buffer, 0, 1);
String path = fsBase + "/test";
assertEquals("test", FileUtils2.getName(path));
can = FilePath.get(fsBase).getCanonicalPath().toString();
String can2 = FileUtils2.getCanonicalPath(FileUtils2.getParent(path));
assertEquals(can, can2);
fo.tryLock();
fo.releaseLock();
assertEquals(10000, fo.size());
fo.close();
assertEquals(10000, FileUtils2.size(fsBase + "/test"));
fo = FileUtils2.openFileObject(fsBase + "/test", "r");
byte[] test = new byte[10000];
fo.readFully(test, 0, 10000);
assertEquals(buffer, test);
assertThrows(IOException.class, fo).write(test, 0, 10);
assertThrows(IOException.class, fo).truncate(10);
fo.close();
long lastMod = FileUtils2.lastModified(fsBase + "/test");
if (lastMod < time - 1999) {
// at most 2 seconds difference
assertEquals(time, lastMod);
}
assertEquals(10000, FileUtils2.size(fsBase + "/test"));
String[] list = FileUtils2.listFiles(fsBase);
assertEquals(1, list.length);
assertTrue(list[0].endsWith("test"));
FileUtils2.copy(fsBase + "/test", fsBase + "/test3");
FileUtils2.moveTo(fsBase + "/test3", fsBase + "/test2");
assertTrue(!FileUtils2.exists(fsBase + "/test3"));
assertTrue(FileUtils2.exists(fsBase + "/test2"));
assertEquals(10000, FileUtils2.size(fsBase + "/test2"));
byte[] buffer2 = new byte[10000];
InputStream in = FileUtils2.newInputStream(fsBase + "/test2");
int pos = 0;
while (true) {
int l = in.read(buffer2, pos, Math.min(10000 - pos, 1000));
if (l <= 0) {
break;
}
pos += l;
}
in.close();
assertEquals(10000, pos);
assertEquals(buffer, buffer2);
assertTrue(FileUtils2.tryDelete(fsBase + "/test2"));
FileUtils2.delete(fsBase + "/test");
if (fsBase.indexOf(FileSystemMemory.PREFIX) < 0 && fsBase.indexOf(FileSystemMemory.PREFIX_LZF) < 0) {
FileUtils2.createDirectories(fsBase + "/testDir");
assertTrue(FileUtils2.isDirectory(fsBase + "/testDir"));
if (!fsBase.startsWith("jdbc:")) {
FileUtils2.deleteRecursive(fsBase + "/testDir", false);
assertTrue(!FileUtils2.exists(fsBase + "/testDir"));
}
}
}
private void testRandomAccess(String fsBase) throws Exception {
testRandomAccess(fsBase, 1);
}
private void testRandomAccess(String fsBase, int seed) throws Exception {
StringBuilder buff = new StringBuilder();
String s = FileUtils2.createTempFile(fsBase + "/tmp", ".tmp", false, false);
File file = new File(TestBase.BASE_TEST_DIR + "/tmp");
file.getParentFile().mkdirs();
file.delete();
RandomAccessFile ra = new RandomAccessFile(file, "rw");
FileUtils2.delete(s);
FileObject f = FileUtils2.openFileObject(s, "rw");
assertThrows(EOFException.class, f).readFully(new byte[1], 0, 1);
f.sync();
Random random = new Random(seed);
int size = getSize(100, 500);
try {
for (int i = 0; i < size; i++) {
trace("op " + i);
int pos = random.nextInt(10000);
switch(random.nextInt(7)) {
case 0: {
pos = (int) Math.min(pos, ra.length());
trace("seek " + pos);
buff.append("seek " + pos + "\n");
f.position(pos);
ra.seek(pos);
break;
}
case 1: {
byte[] buffer = new byte[random.nextInt(1000)];
random.nextBytes(buffer);
trace("write " + buffer.length);
buff.append("write " + buffer.length + "\n");
f.write(buffer, 0, buffer.length);
ra.write(buffer, 0, buffer.length);
break;
}
case 2: {
trace("truncate " + pos);
f.truncate(pos);
if (pos < ra.getFilePointer()) {
// truncate is supposed to have no effect if the
// position is larger than the current position
ra.setLength(pos);
}
assertEquals(ra.getFilePointer(), f.position());
buff.append("truncate " + pos + "\n");
break;
}
case 3: {
int len = random.nextInt(1000);
len = (int) Math.min(len, ra.length() - ra.getFilePointer());
byte[] b1 = new byte[len];
byte[] b2 = new byte[len];
trace("readFully " + len);
ra.readFully(b1, 0, len);
f.readFully(b2, 0, len);
buff.append("readFully " + len + "\n");
assertEquals(b1, b2);
break;
}
case 4: {
trace("getFilePointer");
buff.append("getFilePointer\n");
assertEquals(ra.getFilePointer(), f.position());
break;
}
case 5: {
trace("length " + ra.length());
buff.append("length " + ra.length() + "\n");
assertEquals(ra.length(), f.size());
break;
}
case 6: {
trace("reopen");
buff.append("reopen\n");
f.close();
ra.close();
ra = new RandomAccessFile(file, "rw");
f = FileUtils2.openFileObject(s, "rw");
assertEquals(ra.length(), f.size());
break;
}
default:
}
}
} catch (Throwable e) {
e.printStackTrace();
fail("Exception: " + e + "\n"+ buff.toString());
} finally {
f.close();
ra.close();
file.delete();
FileUtils2.delete(s);
}
}
private void testTempFile(String fsBase) throws Exception {
int len = 10000;
String s = FileUtils2.createTempFile(fsBase + "/tmp", ".tmp", false, false);
OutputStream out = FileUtils2.newOutputStream(s, false);
byte[] buffer = new byte[len];
out.write(buffer);
out.close();
out = FileUtils2.newOutputStream(s, true);
out.write(1);
out.close();
InputStream in = FileUtils2.newInputStream(s);
for (int i = 0; i < len; i++) {
assertEquals(0, in.read());
}
assertEquals(1, in.read());
assertEquals(-1, in.read());
in.close();
out.close();
FileUtils2.delete(s);
}
}
/*
* 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 DebugFile implements FileObject {
private final DebugFilePath fs;
private final FileObject file;
private final String name;
DebugFile(DebugFilePath fs, FileObject file, String name) {
this.fs = fs;
this.file = file;
this.name = fs.getScheme() + ":" + 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();
}
}
\ No newline at end of file
/*
* 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 java.util.List;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathWrapper;
/**
* A debugging file system that logs all operations.
*/
public class DebugFilePath extends FilePathWrapper {
private static final DebugFilePath INSTANCE = new DebugFilePath();
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 DebugFilePath register() {
FilePath.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() {
trace(name, "createDirectory");
super.createDirectory();
}
public boolean createFile() {
trace(name, "createFile");
return super.createFile();
}
public void delete() {
trace(name, "fileName");
super.delete();
}
public boolean exists() {
trace(name, "exists");
return super.exists();
}
// public boolean fileStartsWith(String fileName, String prefix) {
// trace(fileName, "fileStartsWith", unwrap(prefix));
// return super.fileStartsWith(fileName, prefix);
// }
public String getName() {
trace(name, "getName");
return super.getName();
}
public long lastModified() {
trace(name, "lastModified");
return super.lastModified();
}
public FilePath getParent() {
trace(name, "getParent");
return super.getParent();
}
public boolean isAbsolute() {
trace(name, "isAbsolute");
return super.isAbsolute();
}
public boolean isDirectory() {
trace(name, "isDirectory");
return super.isDirectory();
}
public boolean canWrite() {
trace(name, "canWrite");
return super.canWrite();
}
public boolean setReadOnly() {
trace(name, "setReadOnly");
return super.setReadOnly();
}
public long size() {
trace(name, "size");
return super.size();
}
public List<FilePath> listFiles() {
trace(name, "listFiles");
return super.listFiles();
}
public FilePath getCanonicalPath() {
trace(name, "getCanonicalPath");
return super.getCanonicalPath();
}
public InputStream newInputStream() throws IOException {
trace(name, "newInputStream");
InputStream in = super.newInputStream();
if (!trace) {
return in;
}
final String fileName = name;
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)", "in.read(b, " + off + ", " + len + ")");
return super.read(b, off, len);
}
public long skip(long n) throws IOException {
trace(fileName, "in.read(b)", "in.skip(" + n + ")");
return super.skip(n);
}
};
}
public FileObject openFileObject(String mode) throws IOException {
trace(name, "openFileObject", mode);
return new DebugFile(this, super.openFileObject(mode), name);
}
public OutputStream newOutputStream(boolean append) {
trace(name, "newOutputStream", append);
return super.newOutputStream(append);
}
public void moveTo(FilePath newName) {
trace(name, "moveTo", unwrap(((DebugFilePath) newName).name));
super.moveTo(newName);
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException {
trace(name, "createTempFile", suffix, deleteOnExit, inTempDir);
return super.createTempFile(suffix, deleteOnExit, inTempDir);
}
/**
* 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;
}
public String getScheme() {
return "debug";
}
}
...@@ -60,11 +60,6 @@ public class DebugFileSystem extends FileSystemWrapper { ...@@ -60,11 +60,6 @@ public class DebugFileSystem extends FileSystemWrapper {
throw POWER_OFF; throw POWER_OFF;
} }
public boolean canWrite(String fileName) {
trace(fileName, "canWrite");
return super.canWrite(fileName);
}
public void createDirectory(String directoryName) { public void createDirectory(String directoryName) {
trace(directoryName, "createDirectory"); trace(directoryName, "createDirectory");
super.createDirectory(directoryName); super.createDirectory(directoryName);
...@@ -121,9 +116,9 @@ public class DebugFileSystem extends FileSystemWrapper { ...@@ -121,9 +116,9 @@ public class DebugFileSystem extends FileSystemWrapper {
return super.isDirectory(fileName); return super.isDirectory(fileName);
} }
public boolean isReadOnly(String fileName) { public boolean canWrite(String fileName) {
trace(fileName, "isReadOnly"); trace(fileName, "canWrite");
return super.isReadOnly(fileName); return super.canWrite(fileName);
} }
public boolean setReadOnly(String fileName) { public boolean setReadOnly(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: 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.FilePath;
import org.h2.store.fs.FilePathWrapper;
import org.h2.store.fs.FileUtils;
/**
* A file system that encrypts the contents of the files.
*/
public class FilePathCrypt extends FilePathWrapper {
public static void register() {
FilePath.register(new FilePathCrypt());
}
protected String getPrefix() {
String[] parsed = parse(name);
return getScheme() + ":" + parsed[0] + ":" + parsed[1] + ":";
}
public FilePath unwrap(String fileName) {
return FilePath.get(parse(fileName)[2]);
}
public long size() {
long len = getBase().size();
return Math.max(0, len - FileObjectCrypt.HEADER_LENGTH - FileObjectCrypt.BLOCK_SIZE);
}
public FileObject openFileObject(String mode) throws IOException {
String[] parsed = parse(name);
FileObject file = FileUtils.openFileObject(parsed[2], mode);
return new FileObjectCrypt(name, parsed[0], parsed[1], file);
}
public OutputStream newOutputStream(boolean append) {
try {
FileObject file = openFileObject("rw");
return new FileObjectOutputStream(file, append);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
public InputStream newInputStream() {
try {
FileObject file = openFileObject("r");
return new FileObjectInputStream(file);
} catch (IOException e) {
throw DbException.convertIOException(e, name);
}
}
/**
* 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(getScheme())) {
DbException.throwInternalError(fileName + " doesn't start with " + getScheme());
}
fileName = fileName.substring(getScheme().length() + 1);
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 };
}
public String getScheme() {
return "crypt";
}
}
/*
* 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.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.h2.engine.Constants;
import org.h2.message.DbException;
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;
/**
* This is a read-only file system that allows to access databases stored in a
* .zip or .jar file. The problem of this file system is that data is always
* accessed as a stream. But unlike FileSystemZip, it is possible to stack file
* systems.
*/
public class FilePathZip2 extends FilePath {
/**
* Register the file system.
*
* @return the instance
*/
public static FilePathZip2 register() {
FilePathZip2 instance = new FilePathZip2();
FilePath.register(instance);
return instance;
}
public FilePathZip2 getPath(String path) {
FilePathZip2 p = new FilePathZip2();
p.name = path;
return p;
}
public void createDirectory() {
// ignore
}
public boolean createFile() {
throw DbException.getUnsupportedException("write");
}
public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
if (!inTempDir) {
throw new IOException("File system is read-only");
}
return new FilePathDisk().getPath(name).createTempFile(suffix, deleteOnExit, true);
}
public void delete() {
throw DbException.getUnsupportedException("write");
}
public boolean exists() {
try {
String entryName = getEntryName();
if (entryName.length() == 0) {
return true;
}
ZipInputStream file = openZip();
boolean result = false;
while (true) {
ZipEntry entry = file.getNextEntry();
if (entry == null) {
break;
}
if (entry.getName().equals(entryName)) {
result = true;
break;
}
file.closeEntry();
}
file.close();
return result;
} catch (IOException e) {
return false;
}
}
// public boolean fileStartsWith(String fileName, String prefix) {
// return fileName.startsWith(prefix);
// }
public long lastModified() {
return 0;
}
public FilePath getParent() {
int idx = name.lastIndexOf('/');
return idx < 0 ? null : getPath(name.substring(0, idx));
}
public boolean isAbsolute() {
return true;
}
public boolean isDirectory() {
try {
String entryName = getEntryName();
if (entryName.length() == 0) {
return true;
}
ZipInputStream file = openZip();
boolean result = false;
while (true) {
ZipEntry entry = file.getNextEntry();
if (entry == null) {
break;
}
String n = entry.getName();
if (n.equals(entryName)) {
result = entry.isDirectory();
break;
} else if (n.startsWith(entryName)) {
if (n.length() == entryName.length() + 1) {
if (n.equals(entryName + "/")) {
result = true;
break;
}
}
}
file.closeEntry();
}
file.close();
return result;
} catch (IOException e) {
return false;
}
}
public boolean canWrite() {
return false;
}
public boolean setReadOnly() {
return true;
}
public long size() {
try {
String entryName = getEntryName();
ZipInputStream file = openZip();
long result = 0;
while (true) {
ZipEntry entry = file.getNextEntry();
if (entry == null) {
break;
}
if (entry.getName().equals(entryName)) {
result = entry.getSize();
if (result == -1) {
result = 0;
while (true) {
long x = file.skip(16 * Constants.IO_BUFFER_SIZE);
if (x == 0) {
break;
}
result += x;
}
}
break;
}
file.closeEntry();
}
file.close();
return result;
} catch (IOException e) {
return 0;
}
}
public ArrayList<FilePath> listFiles() {
String path = name;
try {
if (path.indexOf('!') < 0) {
path += "!";
}
if (!path.endsWith("/")) {
path += "/";
}
ZipInputStream file = openZip();
String dirName = getEntryName();
String prefix = path.substring(0, path.length() - dirName.length());
ArrayList<FilePath> list = New.arrayList();
while (true) {
ZipEntry entry = file.getNextEntry();
if (entry == null) {
break;
}
String name = entry.getName();
if (name.startsWith(dirName) && name.length() > dirName.length()) {
int idx = name.indexOf('/', dirName.length());
if (idx < 0 || idx >= name.length() - 1) {
list.add(getPath(prefix + name));
}
}
file.closeEntry();
}
file.close();
return list;
} catch (IOException e) {
throw DbException.convertIOException(e, "listFiles " + path);
}
}
public FilePath getCanonicalPath() {
return this;
}
public InputStream newInputStream() throws IOException {
FileObject file = openFileObject("r");
return new FileObjectInputStream(file);
}
public FileObject openFileObject(String mode) throws IOException {
String entryName = getEntryName();
if (entryName.length() == 0) {
throw new FileNotFoundException();
}
ZipInputStream in = openZip();
while (true) {
ZipEntry entry = in.getNextEntry();
if (entry == null) {
break;
}
if (entry.getName().equals(entryName)) {
return new FileObjectZip2(name, entryName, in, size());
}
in.closeEntry();
}
in.close();
throw new FileNotFoundException(name);
}
public OutputStream newOutputStream(boolean append) {
throw DbException.getUnsupportedException("write");
}
public void moveTo(FilePath newName) {
throw DbException.getUnsupportedException("write");
}
// public String unwrap(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 String getEntryName() {
int idx = name.indexOf('!');
String fileName;
if (idx <= 0) {
fileName = "";
} else {
fileName = name.substring(idx + 1);
}
fileName = fileName.replace('\\', '/');
if (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
return fileName;
}
private ZipInputStream openZip() throws IOException {
String fileName = translateFileName(name);
return new ZipInputStream(FileUtils.newInputStream(fileName));
}
private static String translateFileName(String fileName) {
if (fileName.startsWith("zip2:")) {
fileName = fileName.substring("zip2:".length());
}
int idx = fileName.indexOf('!');
if (idx >= 0) {
fileName = fileName.substring(0, idx);
}
return FileSystemDisk.expandUserHomeDirectory(fileName);
}
public boolean fileStartsWith(String prefix) {
return name.startsWith(prefix);
}
public String getScheme() {
return "zip2";
}
}
\ No newline at end of file
...@@ -215,7 +215,7 @@ public class FileShell extends Tool { ...@@ -215,7 +215,7 @@ public class FileShell extends Tool {
for (String file : FileUtils.listFiles(dir)) { for (String file : FileUtils.listFiles(dir)) {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.append(FileUtils.isDirectory(file) ? "d" : "-"); buff.append(FileUtils.isDirectory(file) ? "d" : "-");
buff.append(FileUtils.isReadOnly(file) ? "r-" : "rw"); buff.append(FileUtils.canWrite(file) ? "rw" : "r-");
buff.append(' '); buff.append(' ');
buff.append(String.format("%10d", FileUtils.size(file))); buff.append(String.format("%10d", FileUtils.size(file)));
buff.append(' '); buff.append(' ');
......
...@@ -44,10 +44,6 @@ public class FileSystemZip2 extends FileSystem { ...@@ -44,10 +44,6 @@ public class FileSystemZip2 extends FileSystem {
return INSTANCE; return INSTANCE;
} }
public boolean canWrite(String fileName) {
return false;
}
public void createDirectory(String directoryName) { public void createDirectory(String directoryName) {
// ignore // ignore
} }
...@@ -159,8 +155,8 @@ public class FileSystemZip2 extends FileSystem { ...@@ -159,8 +155,8 @@ public class FileSystemZip2 extends FileSystem {
} }
} }
public boolean isReadOnly(String fileName) { public boolean canWrite(String fileName) {
return true; return false;
} }
public boolean setReadOnly(String fileName) { public boolean setReadOnly(String fileName) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论