/* * 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 java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.util.List; import org.h2.store.fs.FileBase; import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePathWrapper; /** * An unstable file system. It is used to simulate file system problems (for * example out of disk space). */ public class FilePathUnstable extends FilePathWrapper { private static final FilePathUnstable INSTANCE = new FilePathUnstable(); private static final IOException DISK_FULL = new IOException("Disk full"); private static int diskFullOffCount; /** * Register the file system. * * @return the instance */ public static FilePathUnstable register() { FilePath.register(INSTANCE); return INSTANCE; } /** * Check if the simulated problem occurred. * This call will decrement the countdown. * * @throws IOException if the simulated power failure occurred */ void checkError() throws IOException { if (diskFullOffCount == 0) { return; } if (--diskFullOffCount > 0) { return; } if (diskFullOffCount >= -4) { diskFullOffCount--; throw DISK_FULL; } } public void createDirectory() { super.createDirectory(); } public boolean createFile() { return super.createFile(); } public void delete() { super.delete(); } public boolean exists() { return super.exists(); } public String getName() { return super.getName(); } public long lastModified() { return super.lastModified(); } public FilePath getParent() { return super.getParent(); } public boolean isAbsolute() { return super.isAbsolute(); } public boolean isDirectory() { return super.isDirectory(); } public boolean canWrite() { return super.canWrite(); } public boolean setReadOnly() { return super.setReadOnly(); } public long size() { return super.size(); } public List<FilePath> newDirectoryStream() { return super.newDirectoryStream(); } public FilePath toRealPath() { return super.toRealPath(); } public InputStream newInputStream() throws IOException { return super.newInputStream(); } public FileChannel open(String mode) throws IOException { return new FileUnstable(this, super.open(mode)); } public OutputStream newOutputStream(boolean append) { return super.newOutputStream(append); } public void moveTo(FilePath newName) { super.moveTo(newName); } public FilePath createTempFile(String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException { return super.createTempFile(suffix, deleteOnExit, inTempDir); } public void setDiskFullCount(int count) { diskFullOffCount = count; } public int getDiskFullCount() { return diskFullOffCount; } public String getScheme() { return "unstable"; } } /** * An file that checks for errors before each write operation. */ class FileUnstable extends FileBase { private final FilePathUnstable file; private final FileChannel channel; private boolean closed; FileUnstable(FilePathUnstable file, FileChannel channel) { this.file = file; this.channel = channel; } public void implCloseChannel() throws IOException { channel.close(); closed = true; } public long position() throws IOException { return channel.position(); } public long size() throws IOException { return channel.size(); } public int read(ByteBuffer dst) throws IOException { return channel.read(dst); } public FileChannel position(long pos) throws IOException { channel.position(pos); return this; } public FileChannel truncate(long newLength) throws IOException { checkError(); channel.truncate(newLength); return this; } public void force(boolean metaData) throws IOException { checkError(); channel.force(metaData); } public int write(ByteBuffer src) throws IOException { checkError(); return channel.write(src); } private void checkError() throws IOException { if (closed) { throw new IOException("Closed"); } file.checkError(); } public synchronized FileLock tryLock(long position, long size, boolean shared) throws IOException { return channel.tryLock(); } }