/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
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 <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public class AtomicBlockWriteProc implements ISimpleIndexProcedure<Object>,
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 <code>true</code> 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 */
}
}