DirectoryContainer.java

package doss.local;

import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.nio.channels.WritableByteChannel;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

import doss.Writable;

/**
 * A very simple container that just stores blobs as files in a directory.
 */
class DirectoryContainer implements Container {

    final long id;
    final Path dir;

    public DirectoryContainer(long id, Path dir) throws IOException {
        this.id = id;
        this.dir = dir;
        try {
            Files.createDirectory(dir);
        } catch (FileAlreadyExistsException e) {
            // okay
        }
    }

    @Override
    public FileBlob get(long offset) throws IOException {
        String id = new String(Files.readAllBytes(idPathFor(offset)), "UTF-8");
        return new FileBlob(Long.parseLong(id), dataPathFor(offset));
    }

    @Override
    public long put(long id, Writable output) throws IOException {
        Long offset = 0L;
        while (true) {
            try (WritableByteChannel channel = Files.newByteChannel(dataPathFor(offset), CREATE_NEW, WRITE)) {
                output.writeTo(channel);
                Files.write(idPathFor(offset), Long.toString(id).getBytes("UTF-8"), CREATE_NEW, WRITE);
                return offset;
            } catch (FileAlreadyExistsException e) {
                offset++;
            }
        }
    }

    Path dataPathFor(long offset) {
        return dir.resolve(Long.toString(offset));
    }

    protected Path idPathFor(long offset) {
        return dir.resolve(Long.toString(offset) + ".id");
    }

    @Override
    public void close() {
    }

    @Override
    public long id() {
        return id;
    }

    @Override
    public long size() throws IOException {
        // TODO optimize
        SizeCalculator counter = new SizeCalculator();
        Files.walkFileTree(dir, counter);
        return counter.size;
    }

    private static class SizeCalculator extends SimpleFileVisitor<Path> {
        long size = 0;

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            size += Files.size(file);
            return CONTINUE;
        }
    }

}