/*
* Copyright 2014 WANdisco
*
* WANdisco licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package c5db.log;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import static c5db.log.LogPersistenceService.BytePersistence;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.READ;
/**
* A BytePersistence using a File, accessed by FileChannels.
*/
public class FilePersistence implements BytePersistence {
private final FileChannel appendChannel;
final Path path;
private long filePosition;
public FilePersistence(Path path) throws IOException {
this.path = path;
appendChannel = FileChannel.open(path, CREATE, APPEND);
filePosition = appendChannel.position();
}
@Override
public boolean isEmpty() throws IOException {
return filePosition == 0;
}
@Override
public long size() throws IOException {
return filePosition;
}
@Override
public void append(ByteBuffer[] buffers) throws IOException {
appendChannel.write(buffers);
filePosition += totalBytesToBeWritten(buffers);
}
@Override
public LogPersistenceService.PersistenceReader getReader() throws IOException {
return new NioReader(FileChannel.open(path, READ));
}
@Override
public void truncate(long size) throws IOException {
if (size > this.size()) {
throw new IllegalArgumentException("Truncation may not grow the file");
}
appendChannel.truncate(size);
filePosition = size;
}
@Override
public void sync() throws IOException {
appendChannel.force(true);
}
@Override
public void close() throws IOException {
appendChannel.close();
}
// TODO This should be done once, in one central place
private long totalBytesToBeWritten(ByteBuffer[] buffers) {
long sum = 0;
for (ByteBuffer b : buffers) {
sum += b.position();
}
return sum;
}
private static class NioReader implements LogPersistenceService.PersistenceReader {
private final FileChannel fileChannel;
public NioReader(FileChannel fileChannel) {
this.fileChannel = fileChannel;
}
@Override
public long position() throws IOException {
return fileChannel.position();
}
public void position(long newPos) throws IOException {
fileChannel.position(newPos);
}
@Override
public int read(ByteBuffer dst) throws IOException {
return fileChannel.read(dst);
}
@Override
public boolean isOpen() {
return fileChannel.isOpen();
}
@Override
public void close() throws IOException {
fileChannel.close();
}
}
}