All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.bfs.AtomicBlockWriteProc Maven / Gradle / Ivy

/*

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package com.bigdata.bfs;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.nio.ByteBuffer;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import com.bigdata.btree.AbstractBTree;
import com.bigdata.btree.IIndex;
import com.bigdata.btree.keys.IKeyBuilder;
import com.bigdata.btree.proc.ISimpleIndexProcedure;
import com.bigdata.io.DataOutputBuffer;
import com.bigdata.journal.AbstractJournal;
import com.bigdata.journal.Journal;
import com.bigdata.util.Bytes;

/**
 * Atomic write of a single block for a file version.
 * 
 * @author Bryan Thompson
 */
public class AtomicBlockWriteProc implements ISimpleIndexProcedure,
        Externalizable {

    private static final long serialVersionUID = 4982851251684333327L;

    protected static transient Logger log = Logger
            .getLogger(AtomicBlockWriteProc.class);

    /**
     * True iff the {@link #log} level is INFO or less.
     */
    final public static transient boolean INFO = log.getEffectiveLevel()
            .toInt() <= Level.INFO.toInt();

    /**
     * True iff the {@link #log} level is DEBUG or less.
     */
    final public static transient boolean DEBUG = log.getEffectiveLevel()
            .toInt() <= Level.DEBUG.toInt();

    private String id;
    private int version;
    private long block;
    private int off;
    private int len;
    private byte[] b;
    
    @Override
    public final boolean isReadOnly() {
        
        return false;
        
    }
    
    /**
     * 
     * @param id
     *            The file identifier.
     * @param version
     *            The file version.
     * @param block
     *            The block identifier.
     * @param b
     *            The buffer containing the data to be written.
     * @param off
     *            The offset in the buffer of the first byte to be written.
     * @param len
     *            The #of bytes to be written.
     */
    public AtomicBlockWriteProc(BigdataFileSystem repo,String id, int version, long block, byte[] b, int off, int len) {

        assert id != null && id.length() > 0;
        assert version >= 0;
        assert block >= 0 && block <= BigdataFileSystem.MAX_BLOCK;
        assert b != null;
        assert off >= 0 : "off="+off;
        assert len >= 0 && off + len <= b.length;
        assert len <= repo.getBlockSize(): "len="+len+" exceeds blockSize="+repo.getBlockSize();

        this.id = id;
        this.version = version;
        this.block = block;
        this.off = off;
        this.len = len;
        this.b = b;

    }
    
    /**
     * This procedure runs on the unisolated index. The raw data is written
     * directly onto the {@link Journal} and the index is added/updated
     * using the given file, version and block and the address of the
     * block's data on the {@link Journal}.
     * 
     * @return A {@link Boolean} whose value is true iff the
     *         block was overwritten.
     */
    @Override
    public Object apply(final IIndex ndx) {
    
        // tunnel through to the backing journal.
        final AbstractJournal journal = (AbstractJournal)((AbstractBTree)ndx).getStore();
        
        // obtain the thread-local key builder for that journal.
        final IKeyBuilder keyBuilder = ndx.getIndexMetadata().getKeyBuilder();

        /*
         * Write the block on the journal, obtaining the address at which it
         * was written - use 0L as the address for an empty block.
         */
        final long addr = len == 0 ? 0L : journal.write(ByteBuffer.wrap(b,
                off, len));

        // form the key for the index entry for this block.
        final byte[] key = keyBuilder.reset().appendText(id,
                true/* unicode */, false/* successor */).append(version)
                .append(block).getKey();

        // record the address of the block in the index.
        final boolean overwrite;
        {

            final DataOutputBuffer out = new DataOutputBuffer(
                    Bytes.SIZEOF_LONG);

            // encode the value for the entry.
            out.reset().putLong(addr);

            final byte[] val = out.toByteArray();

            // insert the entry into the index.
            overwrite = ndx.insert(key, val) != null;

        }

        log.info("Wrote " + len + " bytes : id=" + id + ", version="
                + version + ", block#=" + block + " @ addr"
                + journal.toString(addr) + ", overwrite=" + overwrite);

        return Boolean.valueOf(overwrite);

    }
    
    @Override
    public void readExternal(final ObjectInput in) throws IOException,
            ClassNotFoundException {

        id = in.readUTF();

        version = in.readInt();

        block = in.readLong();

        off = 0; // Note: offset always zero when de-serialized.

        len = in.readInt();

        b = new byte[len];

        in.readFully(b);

    }

    @Override
    public void writeExternal(final ObjectOutput out) throws IOException {

        out.writeUTF(id);

        out.writeInt(version);

        out.writeLong(block);

        /*
         * Note: offset not written when serialized and always zero when
         * de-serialized.
         */
        
        out.writeInt(len); /* length */
        
        out.write(b, off, len); /* data */
        
    }

}