提交 36c69f54 authored 作者: Thomas Mueller's avatar Thomas Mueller

File system: new method FileSystem.setReadOnly.

上级 b193172c
...@@ -74,4 +74,5 @@ public interface FileObject { ...@@ -74,4 +74,5 @@ public interface FileObject {
* @return the name * @return the name
*/ */
String getName(); String getName();
} }
...@@ -9,7 +9,6 @@ package org.h2.store.fs; ...@@ -9,7 +9,6 @@ package org.h2.store.fs;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
......
...@@ -6,235 +6,43 @@ ...@@ -6,235 +6,43 @@
*/ */
package org.h2.store.fs; package org.h2.store.fs;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.h2.compress.CompressLZF;
import org.h2.util.MathUtils;
/** /**
* This class is an abstraction of an in-memory random access file. * This class represents an in-memory file.
* Data compression using the LZF algorithm is supported as well.
*/ */
public class FileObjectMemory implements FileObject { public class FileObjectMemory implements FileObject {
private static final int CACHE_SIZE = 8;
private static final int BLOCK_SIZE_SHIFT = 10;
private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT;
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
private static final CompressLZF LZF = new CompressLZF();
private static final byte[] BUFFER = new byte[BLOCK_SIZE * 2];
private static final byte[] COMPRESSED_EMPTY_BLOCK;
//## Java 1.4 begin ##
private static final Cache<CompressItem, CompressItem> COMPRESS_LATER = new Cache<CompressItem, CompressItem>(CACHE_SIZE);
//## Java 1.4 end ##
private String name; private final FileObjectMemoryData data;
private final boolean compress;
private long length;
private long pos; private long pos;
private byte[][] data;
private long lastModified;
static {
byte[] n = new byte[BLOCK_SIZE];
int len = LZF.compress(n, BLOCK_SIZE, BUFFER, 0);
COMPRESSED_EMPTY_BLOCK = new byte[len];
System.arraycopy(BUFFER, 0, COMPRESSED_EMPTY_BLOCK, 0, len);
}
/**
* This small cache compresses the data if an element leaves the cache.
*/
//## Java 1.4 begin ##
static class Cache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 1L;
private int size;
Cache(int size) {
super(size, (float) 0.75, true);
this.size = size;
}
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
if (size() < size) {
return false;
}
CompressItem c = (CompressItem) eldest.getKey();
compress(c.data, c.page);
return true;
}
}
/** FileObjectMemory(FileObjectMemoryData data) {
* Represents a compressed item. this.data = data;
*/
static class CompressItem {
/**
* The file data.
*/
byte[][] data;
/**
* The page to compress.
*/
int page;
public int hashCode() {
return page;
}
public boolean equals(Object o) {
if (o instanceof CompressItem) {
CompressItem c = (CompressItem) o;
return c.data == data && c.page == page;
}
return false;
}
}
//## Java 1.4 end ##
FileObjectMemory(String name, boolean compress) {
this.name = name;
this.compress = compress;
data = new byte[0][];
touch();
}
private static void compressLater(byte[][] data, int page) {
//## Java 1.4 begin ##
CompressItem c = new CompressItem();
c.data = data;
c.page = page;
synchronized (LZF) {
COMPRESS_LATER.put(c, c);
}
//## Java 1.4 end ##
}
private static void expand(byte[][] data, int page) {
byte[] d = data[page];
if (d.length == BLOCK_SIZE) {
return;
}
byte[] out = new byte[BLOCK_SIZE];
if (d != COMPRESSED_EMPTY_BLOCK) {
synchronized (LZF) {
LZF.expand(d, 0, d.length, out, 0, BLOCK_SIZE);
}
}
data[page] = out;
}
/**
* Compress the data in a byte array.
*
* @param data the page array
* @param page which page to compress
*/
static void compress(byte[][] data, int page) {
byte[] d = data[page];
synchronized (LZF) {
int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0);
if (len <= BLOCK_SIZE) {
d = new byte[len];
System.arraycopy(BUFFER, 0, d, 0, len);
data[page] = d;
}
}
}
private void touch() {
lastModified = System.currentTimeMillis();
} }
public long length() { public long length() {
return length; return data.length();
} }
public void setFileLength(long newLength) { public void setFileLength(long newLength) throws IOException {
touch(); data.touch();
if (newLength < length) { if (newLength < length()) {
pos = Math.min(pos, newLength); pos = Math.min(pos, newLength);
changeLength(newLength);
long end = MathUtils.roundUpLong(newLength, BLOCK_SIZE);
if (end != newLength) {
int lastPage = (int) (newLength >>> BLOCK_SIZE_SHIFT);
expand(data, lastPage);
byte[] d = data[lastPage];
for (int i = (int) (newLength & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
d[i] = 0;
}
if (compress) {
compressLater(data, lastPage);
}
}
} else {
changeLength(newLength);
} }
data.setFileLength(newLength);
} }
public void seek(long newPos) { public void seek(long newPos) {
this.pos = (int) newPos; this.pos = (int) newPos;
} }
private void changeLength(long len) {
length = len;
len = MathUtils.roundUpLong(len, BLOCK_SIZE);
int blocks = (int) (len >>> BLOCK_SIZE_SHIFT);
if (blocks != data.length) {
byte[][] n = new byte[blocks][];
System.arraycopy(data, 0, n, 0, Math.min(data.length, n.length));
for (int i = data.length; i < blocks; i++) {
n[i] = COMPRESSED_EMPTY_BLOCK;
}
data = n;
}
}
private void readWrite(byte[] b, int off, int len, boolean write) throws IOException {
long end = pos + len;
if (end > length) {
if (write) {
changeLength(end);
} else {
if (len == 0) {
return;
}
throw new EOFException("File: " + name);
}
}
while (len > 0) {
int l = (int) Math.min(len, BLOCK_SIZE - (pos & BLOCK_SIZE_MASK));
int page = (int) (pos >>> BLOCK_SIZE_SHIFT);
expand(data, page);
byte[] block = data[page];
int blockOffset = (int) (pos & BLOCK_SIZE_MASK);
if (write) {
System.arraycopy(b, off, block, blockOffset, l);
} else {
System.arraycopy(block, blockOffset, b, off, l);
}
if (compress) {
compressLater(data, page);
}
off += l;
pos += l;
len -= l;
}
}
public void write(byte[] b, int off, int len) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
touch(); data.touch();
readWrite(b, off, len, true); pos = data.readWrite(pos, b, off, len, true);
} }
public void readFully(byte[] b, int off, int len) throws IOException { public void readFully(byte[] b, int off, int len) throws IOException {
readWrite(b, off, len, false); pos = data.readWrite(pos, b, off, len, false);
} }
public long getFilePointer() { public long getFilePointer() {
...@@ -246,18 +54,27 @@ public class FileObjectMemory implements FileObject { ...@@ -246,18 +54,27 @@ public class FileObjectMemory implements FileObject {
} }
public void sync() { public void sync() {
// nothing to do data.sync();
} }
public void setName(String name) { public void setName(String name) {
this.name = name; data.setName(name);
} }
public String getName() { public String getName() {
return name; return data.getName();
} }
public long getLastModified() { public long getLastModified() {
return lastModified; return data.getLastModified();
} }
boolean canWrite() {
return data.canWrite();
}
boolean setReadOnly() {
return data.setReadOnly();
}
} }
/*
* Copyright 2004-2010 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.EOFException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.h2.compress.CompressLZF;
import org.h2.util.MathUtils;
/**
* This class contains the data of an in-memory random access file.
* Data compression using the LZF algorithm is supported as well.
*/
public class FileObjectMemoryData {
private static final int CACHE_SIZE = 8;
private static final int BLOCK_SIZE_SHIFT = 10;
private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT;
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
private static final CompressLZF LZF = new CompressLZF();
private static final byte[] BUFFER = new byte[BLOCK_SIZE * 2];
private static final byte[] COMPRESSED_EMPTY_BLOCK;
//## Java 1.4 begin ##
private static final Cache<CompressItem, CompressItem> COMPRESS_LATER = new Cache<CompressItem, CompressItem>(CACHE_SIZE);
//## Java 1.4 end ##
private String name;
private final boolean compress;
private long length;
private byte[][] data;
private long lastModified;
private boolean isReadOnly;
static {
byte[] n = new byte[BLOCK_SIZE];
int len = LZF.compress(n, BLOCK_SIZE, BUFFER, 0);
COMPRESSED_EMPTY_BLOCK = new byte[len];
System.arraycopy(BUFFER, 0, COMPRESSED_EMPTY_BLOCK, 0, len);
}
/**
* This small cache compresses the data if an element leaves the cache.
*/
//## Java 1.4 begin ##
static class Cache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 1L;
private int size;
Cache(int size) {
super(size, (float) 0.75, true);
this.size = size;
}
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
if (size() < size) {
return false;
}
CompressItem c = (CompressItem) eldest.getKey();
compress(c.data, c.page);
return true;
}
}
/**
* Represents a compressed item.
*/
static class CompressItem {
/**
* The file data.
*/
byte[][] data;
/**
* The page to compress.
*/
int page;
public int hashCode() {
return page;
}
public boolean equals(Object o) {
if (o instanceof CompressItem) {
CompressItem c = (CompressItem) o;
return c.data == data && c.page == page;
}
return false;
}
}
//## Java 1.4 end ##
FileObjectMemoryData(String name, boolean compress) {
this.name = name;
this.compress = compress;
data = new byte[0][];
lastModified = System.currentTimeMillis();
}
private static void compressLater(byte[][] data, int page) {
//## Java 1.4 begin ##
CompressItem c = new CompressItem();
c.data = data;
c.page = page;
synchronized (LZF) {
COMPRESS_LATER.put(c, c);
}
//## Java 1.4 end ##
}
private static void expand(byte[][] data, int page) {
byte[] d = data[page];
if (d.length == BLOCK_SIZE) {
return;
}
byte[] out = new byte[BLOCK_SIZE];
if (d != COMPRESSED_EMPTY_BLOCK) {
synchronized (LZF) {
LZF.expand(d, 0, d.length, out, 0, BLOCK_SIZE);
}
}
data[page] = out;
}
/**
* Compress the data in a byte array.
*
* @param data the page array
* @param page which page to compress
*/
static void compress(byte[][] data, int page) {
byte[] d = data[page];
synchronized (LZF) {
int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0);
if (len <= BLOCK_SIZE) {
d = new byte[len];
System.arraycopy(BUFFER, 0, d, 0, len);
data[page] = d;
}
}
}
void touch() throws IOException {
if (isReadOnly) {
throw new IOException("Read only");
}
lastModified = System.currentTimeMillis();
}
long length() {
return length;
}
void setFileLength(long newLength) {
if (newLength < length) {
changeLength(newLength);
long end = MathUtils.roundUpLong(newLength, BLOCK_SIZE);
if (end != newLength) {
int lastPage = (int) (newLength >>> BLOCK_SIZE_SHIFT);
expand(data, lastPage);
byte[] d = data[lastPage];
for (int i = (int) (newLength & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
d[i] = 0;
}
if (compress) {
compressLater(data, lastPage);
}
}
} else {
changeLength(newLength);
}
}
private void changeLength(long len) {
length = len;
len = MathUtils.roundUpLong(len, BLOCK_SIZE);
int blocks = (int) (len >>> BLOCK_SIZE_SHIFT);
if (blocks != data.length) {
byte[][] n = new byte[blocks][];
System.arraycopy(data, 0, n, 0, Math.min(data.length, n.length));
for (int i = data.length; i < blocks; i++) {
n[i] = COMPRESSED_EMPTY_BLOCK;
}
data = n;
}
}
long readWrite(long pos, byte[] b, int off, int len, boolean write) throws IOException {
long end = pos + len;
if (end > length) {
if (write) {
changeLength(end);
} else {
if (len == 0) {
return pos;
}
throw new EOFException("File: " + name);
}
}
while (len > 0) {
int l = (int) Math.min(len, BLOCK_SIZE - (pos & BLOCK_SIZE_MASK));
int page = (int) (pos >>> BLOCK_SIZE_SHIFT);
expand(data, page);
byte[] block = data[page];
int blockOffset = (int) (pos & BLOCK_SIZE_MASK);
if (write) {
System.arraycopy(b, off, block, blockOffset, l);
} else {
System.arraycopy(block, blockOffset, b, off, l);
}
if (compress) {
compressLater(data, page);
}
off += l;
pos += l;
len -= l;
}
return pos;
}
public void sync() {
// nothing to do
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public long getLastModified() {
return lastModified;
}
public boolean canWrite() {
return !isReadOnly;
}
public boolean setReadOnly() {
isReadOnly = true;
return true;
}
}
...@@ -290,6 +290,13 @@ public abstract class FileSystem { ...@@ -290,6 +290,13 @@ public abstract class FileSystem {
*/ */
public abstract InputStream openFileInputStream(String fileName) throws IOException; public abstract InputStream openFileInputStream(String fileName) throws IOException;
/**
* Disable the ability to write.
*
* @return true if the call was successful
*/
public abstract boolean setReadOnly(String fileName);
/** /**
* Get the next temporary file name part (the part in the middle). * Get the next temporary file name part (the part in the middle).
* *
......
...@@ -247,6 +247,12 @@ public class FileSystemDisk extends FileSystem { ...@@ -247,6 +247,12 @@ public class FileSystemDisk extends FileSystem {
return f.exists() && !canWriteInternal(f); return f.exists() && !canWriteInternal(f);
} }
public boolean setReadOnly(String fileName) {
fileName = translateFileName(fileName);
File f = new File(fileName);
return f.setReadOnly();
}
public String normalize(String fileName) { public String normalize(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
File f = new File(fileName); File f = new File(fileName);
......
...@@ -33,7 +33,7 @@ public class FileSystemMemory extends FileSystem { ...@@ -33,7 +33,7 @@ public class FileSystemMemory extends FileSystem {
public static final String PREFIX_LZF = "memLZF:"; public static final String PREFIX_LZF = "memLZF:";
private static final FileSystemMemory INSTANCE = new FileSystemMemory(); private static final FileSystemMemory INSTANCE = new FileSystemMemory();
private static final TreeMap<String, FileObjectMemory> MEMORY_FILES = new TreeMap<String, FileObjectMemory>(); private static final TreeMap<String, FileObjectMemoryData> MEMORY_FILES = new TreeMap<String, FileObjectMemoryData>();
private FileSystemMemory() { private FileSystemMemory() {
// don't allow construction // don't allow construction
...@@ -51,7 +51,7 @@ public class FileSystemMemory extends FileSystem { ...@@ -51,7 +51,7 @@ public class FileSystemMemory extends FileSystem {
oldName = normalize(oldName); oldName = normalize(oldName);
newName = normalize(newName); newName = normalize(newName);
synchronized (MEMORY_FILES) { synchronized (MEMORY_FILES) {
FileObjectMemory f = getMemoryFile(oldName); FileObjectMemoryData f = getMemoryFile(oldName);
f.setName(newName); f.setName(newName);
MEMORY_FILES.remove(oldName); MEMORY_FILES.remove(oldName);
MEMORY_FILES.put(newName, f); MEMORY_FILES.put(newName, f);
...@@ -119,7 +119,11 @@ public class FileSystemMemory extends FileSystem { ...@@ -119,7 +119,11 @@ public class FileSystemMemory extends FileSystem {
} }
public boolean isReadOnly(String fileName) { public boolean isReadOnly(String fileName) {
return false; return !getMemoryFile(fileName).canWrite();
}
public boolean setReadOnly(String fileName) {
return getMemoryFile(fileName).setReadOnly();
} }
public String normalize(String fileName) { public String normalize(String fileName) {
...@@ -191,33 +195,32 @@ public class FileSystemMemory extends FileSystem { ...@@ -191,33 +195,32 @@ public class FileSystemMemory extends FileSystem {
public OutputStream openFileOutputStream(String fileName, boolean append) { public OutputStream openFileOutputStream(String fileName, boolean append) {
try { try {
FileObjectMemory obj = getMemoryFile(fileName); FileObjectMemoryData obj = getMemoryFile(fileName);
obj.seek(0); FileObjectMemory m = new FileObjectMemory(obj);
return new FileObjectOutputStream(obj, append); return new FileObjectOutputStream(m, append);
} catch (IOException e) { } catch (IOException e) {
throw DbException.convertIOException(e, fileName); throw DbException.convertIOException(e, fileName);
} }
} }
public InputStream openFileInputStream(String fileName) { public InputStream openFileInputStream(String fileName) {
FileObjectMemory obj = getMemoryFile(fileName); FileObjectMemoryData obj = getMemoryFile(fileName);
obj.seek(0); FileObjectMemory m = new FileObjectMemory(obj);
return new FileObjectInputStream(obj); return new FileObjectInputStream(m);
} }
public FileObject openFileObject(String fileName, String mode) { public FileObject openFileObject(String fileName, String mode) {
FileObjectMemory obj = getMemoryFile(fileName); FileObjectMemoryData obj = getMemoryFile(fileName);
obj.seek(0); return new FileObjectMemory(obj);
return obj;
} }
private FileObjectMemory getMemoryFile(String fileName) { private FileObjectMemoryData getMemoryFile(String fileName) {
fileName = normalize(fileName); fileName = normalize(fileName);
synchronized (MEMORY_FILES) { synchronized (MEMORY_FILES) {
FileObjectMemory m = MEMORY_FILES.get(fileName); FileObjectMemoryData m = MEMORY_FILES.get(fileName);
if (m == null) { if (m == null) {
boolean compress = fileName.startsWith(PREFIX_LZF); boolean compress = fileName.startsWith(PREFIX_LZF);
m = new FileObjectMemory(fileName, compress); m = new FileObjectMemoryData(fileName, compress);
MEMORY_FILES.put(fileName, m); MEMORY_FILES.put(fileName, m);
} }
return m; return m;
......
...@@ -36,6 +36,20 @@ public class FileSystemSplit extends FileSystem { ...@@ -36,6 +36,20 @@ public class FileSystemSplit extends FileSystem {
return getFileSystem(fileName).canWrite(fileName); return getFileSystem(fileName).canWrite(fileName);
} }
public boolean setReadOnly(String fileName) {
fileName = translateFileName(fileName);
boolean result = false;
for (int i = 0;; i++) {
String f = getFileName(fileName, i);
if (getFileSystem(f).exists(f)) {
result = getFileSystem(f).setReadOnly(f);
} else {
break;
}
}
return result;
}
public void copy(String original, String copy) { public void copy(String original, String copy) {
original = translateFileName(original); original = translateFileName(original);
copy = translateFileName(copy); copy = translateFileName(copy);
......
...@@ -140,6 +140,10 @@ public class FileSystemZip extends FileSystem { ...@@ -140,6 +140,10 @@ public class FileSystemZip extends FileSystem {
return true; return true;
} }
public boolean setReadOnly(String fileName) {
return true;
}
public long length(String fileName) { public long length(String fileName) {
try { try {
ZipFile file = openZipFile(fileName); ZipFile file = openZipFile(fileName);
......
...@@ -320,6 +320,10 @@ public class FileSystemDatabase extends FileSystem { ...@@ -320,6 +320,10 @@ public class FileSystemDatabase extends FileSystem {
return false; return false;
} }
public boolean setReadOnly(String fileName) {
return false;
}
public synchronized long length(String fileName) { public synchronized long length(String fileName) {
try { try {
long id = getId(fileName, false); long id = getId(fileName, false);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论