org.b1.pack.standard.writer.VolumeWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.b1.pack.standard.writer.VolumeWriter.java

Source

/*
 * Copyright 2012 b1.org
 *
 * Licensed 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 org.b1.pack.standard.writer;

import com.google.common.collect.Maps;
import com.google.common.io.Closeables;
import org.b1.pack.api.builder.Writable;
import org.b1.pack.api.writer.WriterVolume;
import org.b1.pack.standard.common.*;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.SortedMap;

class VolumeWriter {

    private final SortedMap<Long, Writable> suspendedBlocks = Maps.newTreeMap();
    private final long volumeNumber;
    private final long maxVolumeSize;
    private final WriterVolume volume;
    private final VolumeCipher volumeCipher;
    private OutputStream outputStream;
    private RecordPointer catalogPointer;
    private long sizeLimit;
    private long spaceLimit;
    private long streamEnd;
    private boolean streamAtEnd;

    public VolumeWriter(String archiveId, long volumeNumber, Long objectCount, String method, long maxVolumeSize,
            WriterVolume volume, RecordPointer catalogPointer, VolumeCipher volumeCipher) throws IOException {
        this.volumeNumber = volumeNumber;
        this.maxVolumeSize = maxVolumeSize;
        this.volume = volume;
        this.catalogPointer = catalogPointer;
        this.volumeCipher = volumeCipher;
        byte[] volumeHead = Volumes.createVolumeHead(archiveId, volumeNumber, objectCount, method, volumeCipher);
        streamEnd = volumeHead.length;
        streamAtEnd = true;
        setLimits();
        boolean pending = true;
        outputStream = volume.getOutputStream();
        try {
            outputStream.write(volumeHead);
            pending = false;
        } finally {
            if (pending)
                cleanup();
        }
    }

    public long getVolumeNumber() {
        return volumeNumber;
    }

    public long getStreamEnd() {
        return streamEnd;
    }

    public long getFreeSpace() {
        return spaceLimit - streamEnd;
    }

    public boolean isSuspended() {
        return !suspendedBlocks.isEmpty();
    }

    public void setCatalogPointer(RecordPointer catalogPointer) throws IOException {
        this.catalogPointer = catalogPointer;
        if (catalogPointer != null) {
            setLimits();
        }
    }

    public void suspendBlock(PbBlock block) throws IOException {
        Writable writable = encrypt(streamEnd, block);
        suspendedBlocks.put(streamEnd, writable);
        streamAtEnd = false;
        streamEnd += writable.getSize();
    }

    public void writeBlock(PbBlock block) throws IOException {
        Writable writable = encrypt(streamEnd, block);
        seekToEnd();
        writeToStream(writable);
        streamEnd += writable.getSize();
    }

    public void flush() throws IOException {
        if (suspendedBlocks.isEmpty())
            return;
        streamAtEnd = false;
        for (Map.Entry<Long, Writable> entry : suspendedBlocks.entrySet()) {
            volume.seek(outputStream, entry.getKey());
            writeToStream(entry.getValue());
        }
        suspendedBlocks.clear();
    }

    public void close(boolean lastVolume) throws IOException {
        flush();
        seekToEnd();
        writeToStream(PbInt.NULL);
        long minSize = lastVolume ? 0 : sizeLimit - streamEnd - PbInt.NULL.getSize();
        outputStream.write(Volumes.createVolumeTail(lastVolume, catalogPointer, minSize, volumeCipher));
        outputStream.close();
        volume.save();
    }

    public void cleanup() {
        Closeables.closeQuietly(outputStream);
    }

    private Writable encrypt(long offset, PbBlock block) {
        return volumeCipher == null ? block : new AesBlock(volumeCipher, offset, block);
    }

    private void setLimits() throws IOException {
        sizeLimit = Math.min(maxVolumeSize, volume.getMaxSize());
        spaceLimit = sizeLimit - Volumes.createVolumeTail(false, catalogPointer, 0, volumeCipher).length
                - PbInt.NULL.getSize();
    }

    private void seekToEnd() throws IOException {
        if (streamAtEnd)
            return;
        volume.seek(outputStream, streamEnd);
        streamAtEnd = true;
    }

    private void writeToStream(Writable writable) throws IOException {
        writable.writeTo(outputStream, 0, writable.getSize());
    }
}