/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.store.xa;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
// Third party packages
import org.apache.log4j.Logger;
/**
* An implementation of BlockFile which uses regular (position/read/write) file
* IO.
*
* @created 2001-09-20
*
* @author David Makepeace
*
* @version $Revision: 1.10 $
*
* @modified $Date: 2005/06/30 01:14:40 $
*
* @maintenanceAuthor $Author: pgearon $
*
* @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A>
*
* @copyright ©2004 <a href="http://www.pisoftware.com/">Plugged In
* Software Pty Ltd</a>
*
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public final class IOBlockFile extends AbstractBlockFile {
/** Logger. */
@SuppressWarnings("unused")
private final static Logger logger = Logger.getLogger(IOBlockFile.class);
private static final int NOMINAL_ALLOCATION_SIZE = 1048576; // in bytes
private long allocatedNrBlocks;
private int allocationSize; // in blocks
/**
* Constructs an IOBlockFile for the file with the specified file name.
*
* @param file the block file.
* @param blockSize the size of blocks in the block file.
* @throws IOException if an I/O error occurs.
*/
public IOBlockFile(File file, int blockSize) throws IOException {
super(file, blockSize);
allocatedNrBlocks = nrBlocks;
allocationSize = blockSize < NOMINAL_ALLOCATION_SIZE ?
NOMINAL_ALLOCATION_SIZE / blockSize : 1;
}
/**
* Constructs an IOBlockFile for the file with the specified file name.
*
* @param fileName the file name of the block file.
* @param blockSize the size of blocks in the block file.
* @throws IOException if an I/O error occurs.
*/
public IOBlockFile(String fileName, int blockSize) throws IOException {
this(new File(fileName), blockSize);
}
/**
* Sets the length of the file in blocks.
*
* @param nrBlocks The number of blocks in the file.
* @throws IOException if an I/O error occurs.
*/
public void setNrBlocks(long nrBlocks) throws IOException {
if (nrBlocks == this.nrBlocks) return;
super.setNrBlocks(nrBlocks);
if (nrBlocks <= allocatedNrBlocks) return;
allocatedNrBlocks = nrBlocks - (nrBlocks % allocationSize) + allocationSize;
for (;;) {
try {
raf.setLength(allocatedNrBlocks * blockSize);
break;
} catch (ClosedChannelException ex) {
// The Channel may have been inadvertently closed by another thread
// being interrupted. Attempt to reopen the channel.
if (!ensureOpen()) {
throw ex;
}
// Loop back and retry the setLength().
}
}
}
/**
* Allocates a ByteBuffer to be used for writing to the specified block. The
* contents of the ByteBuffer are undefined. The method {@link #writeBlock}
* is called to write the buffer to the block.
*
* @param blockId The ID of the block that this buffer will be written to.
* @return a ByteBuffer to be used for writing to the specified block.
*/
public Block allocateBlock(long blockId) {
assert(blockId >= 0) && (blockId < nrBlocks);
return Block.newInstance(this, blockSize, blockId, byteOrder);
}
/**
* Allocates a ByteBuffer which is filled with the contents of the specified
* block. If the buffer is modified then the method {@link #writeBlock}
* should be called to write the buffer back to the file.
*
* @param blockId the block to read into the ByteBuffer.
* @return The allocated block, containing valid data from the file.
* @throws IOException if an I/O error occurs.
*/
public Block readBlock(long blockId) throws IOException {
// Create the buffer to read into.
Block block = allocateBlock(blockId);
ByteBuffer byteBuffer = block.getByteBuffer();
for (;;) {
try {
// Reset the position in this byte buffer.
byteBuffer.rewind();
// Read the block into the buffer.
fc.read(byteBuffer, blockId * blockSize);
break;
} catch (ClosedChannelException ex) {
// The Channel may have been inadvertently closed by another thread
// being interrupted. Attempt to reopen the channel.
if (!ensureOpen()) {
throw ex;
}
// Loop back and retry the read.
}
}
// Return the block.
return block;
}
/**
* Writes a buffer that was allocated by calling either {@link
* #allocateBlock} or {@link #readBlock} to the specified block. The buffer
* may only be written to the same block as was specified when the buffer was
* allocated.
*
* @param block the buffer to write to the file.
* @throws IOException if an I/O error occurs.
*/
public void writeBlock(Block block) throws IOException {
long blockId = block.getBlockId();
assert(blockId >= 0) && (blockId < nrBlocks);
ByteBuffer byteBuffer = block.getByteBuffer();
for (;;) {
try {
// Reset the position.
byteBuffer.rewind();
// Write the buffer to the file.
fc.write(byteBuffer, blockId * blockSize);
break;
} catch (ClosedChannelException ex) {
// The Channel may have been inadvertently closed by another thread
// being interrupted. Attempt to reopen the channel.
if (!ensureOpen()) {
throw ex;
}
// Loop back and retry the write.
}
}
}
/**
* Changes the block ID of the specified Block. This method is called
* copyBlock because a call to copyBlock() followed by a call to writeBlock()
* can be used to copy the contents of a block to a new location in the block
* file.
*
* @param block the Block to be copied.
* @param dstBlockId the ID of the block to which the Block will be written
* when writeBlock() is called.
* @throws IOException if an I/O error occurs.
*/
public void copyBlock(Block block, long dstBlockId) {
block.setBlockId(dstBlockId);
}
/**
* Attempt to re-use the given Block and wrapped ByteBuffer to read the indicated block.
* null ByteBuffer will behave like readBlock.
* @author barmintor
* @param blockId The block to read into the ByteBuffer.
* @param block The ByteBuffer to attempt to re-use
* @return The buffer that was read.
* @throws IOException if an I/O error occurs.
*/
public Block recycleBlock(long blockId, Block block) throws IOException {
if (block == null) return readBlock(blockId);
Block.recycleBuffer(block, blockId);
return block;
}
}