blob: 9ad1fa3473d2687ccdf17b968aecbd9d4158003c [file] [log] [blame]
/**
* JDBM LICENSE v1.00
*
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
* statements and notices. Redistributions must also contain a
* copy of this document.
*
* 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. The name "JDBM" must not be used to endorse or promote
* products derived from this Software without prior written
* permission of Cees de Groot. For written permission,
* please contact cg@cdegroot.com.
*
* 4. Products derived from this Software may not be called "JDBM"
* nor may "JDBM" appear in their names without prior written
* permission of Cees de Groot.
*
* 5. Due credit should be given to the JDBM Project
* (http://jdbm.sourceforge.net/).
*
* THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 2000 (C) Cees de Groot. All Rights Reserved.
* Contributions are Copyright (C) 2000 by their associated contributors.
*
* $Id: PageManager.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
*/
package jdbm.recman;
import java.io.*;
import org.apache.directory.server.i18n.I18n;
/**
* This class manages the linked lists of pages that make up a recordFile.
*/
final class PageManager
{
// our record recordFile
private RecordFile recordFile;
// header data
private FileHeader header;
// recordFile header containing block
private BlockIo headerBuf;
/**
* Creates a new page manager using the indicated record recordFile.
*/
PageManager( RecordFile recordFile ) throws IOException
{
this.recordFile = recordFile;
// Note that we hold on to the recordFile header node.
headerBuf = recordFile.get( 0 );
// Assume recordFile is new if the recordFile header's magic number is 0.
if ( headerBuf.readShort( 0 ) == 0 )
{
header = new FileHeader( headerBuf, true );
}
else // header is for existing recordFile
{
header = new FileHeader( headerBuf, false );
}
}
/**
* Allocates a page of the indicated type. Returns recid of the page.
*/
long allocate( short type ) throws IOException
{
if ( type == Magic.FREE_PAGE )
{
throw new Error( I18n.err( I18n.ERR_548 ) );
}
boolean isNew = false;
// Do we have something on the free list?
long retval = header.getFirstOf( Magic.FREE_PAGE );
if ( retval != 0 )
{
// yes. Point to it and make the next of that page the
// new first free page.
header.setFirstOf( Magic.FREE_PAGE, getNext( retval ) );
}
else
{
// nope. make a new record
retval = header.getLastOf( Magic.FREE_PAGE );
if ( retval == 0 )
{
// very new recordFile - allocate record #1
retval = 1;
}
header.setLastOf( Magic.FREE_PAGE, retval + 1 );
isNew = true;
}
// Cool. We have a record, add it to the correct list
BlockIo buf = recordFile.get( retval );
PageHeader pageHdr = null;
if ( isNew )
{
pageHdr = new PageHeader( buf, type );
}
else
{
pageHdr = PageHeader.getView( buf );
}
long oldLast = header.getLastOf( type );
// Clean data.
System.arraycopy( RecordFile.cleanData, 0, buf.getData(), 0, RecordFile.BLOCK_SIZE );
pageHdr.setType( type );
pageHdr.setPrev( oldLast );
pageHdr.setNext( 0 );
if ( oldLast == 0 )
{
// This was the first one of this type
header.setFirstOf( type, retval );
}
header.setLastOf( type, retval );
recordFile.release( retval, true );
// If there's a previous, fix up its pointer
if ( oldLast != 0 )
{
buf = recordFile.get( oldLast );
pageHdr = PageHeader.getView( buf );
pageHdr.setNext( retval );
recordFile.release( oldLast, true );
}
// remove the view, we have modified the type.
buf.setView( null );
return retval;
}
/**
* Frees a page of the indicated type.
*/
void free( short type, long recid ) throws IOException
{
if ( type == Magic.FREE_PAGE )
{
throw new Error( I18n.err( I18n.ERR_549 ) );
}
if ( recid == 0 )
{
throw new Error( I18n.err( I18n.ERR_550 ) );
}
// get the page and read next and previous pointers
BlockIo buf = recordFile.get( recid );
PageHeader pageHdr = PageHeader.getView( buf );
long prev = pageHdr.getPrev();
long next = pageHdr.getNext();
// put the page at the front of the free list.
pageHdr.setType( Magic.FREE_PAGE );
pageHdr.setNext( header.getFirstOf( Magic.FREE_PAGE ) );
pageHdr.setPrev( 0 );
header.setFirstOf( Magic.FREE_PAGE, recid );
recordFile.release( recid, true );
// remove the page from its old list
if ( prev != 0 )
{
buf = recordFile.get( prev );
pageHdr = PageHeader.getView( buf );
pageHdr.setNext( next );
recordFile.release( prev, true );
}
else
{
header.setFirstOf( type, next );
}
if ( next != 0 )
{
buf = recordFile.get( next );
pageHdr = PageHeader.getView( buf );
pageHdr.setPrev( prev );
recordFile.release( next, true );
}
else
{
header.setLastOf( type, prev );
}
}
/**
* Returns the page following the indicated block
*/
long getNext( long block ) throws IOException
{
try
{
return PageHeader.getView( recordFile.get( block ) ).getNext();
}
finally
{
recordFile.release( block, false );
}
}
/**
* Returns the page before the indicated block
*/
long getPrev( long block ) throws IOException
{
try
{
return PageHeader.getView( recordFile.get( block ) ).getPrev();
}
finally
{
recordFile.release( block, false );
}
}
/**
* Returns the first page on the indicated list.
*/
long getFirst( short type ) throws IOException
{
return header.getFirstOf( type );
}
/**
* Returns the last page on the indicated list.
*/
long getLast( short type ) throws IOException
{
return header.getLastOf( type );
}
/**
* Commit all pending (in-memory) data by flushing the page manager.
* This forces a flush of all outstanding blocks (this is an implicit
* {@link RecordFile#commit} as well).
*/
void commit() throws IOException
{
// write the header out
recordFile.release( headerBuf );
recordFile.commit();
// and obtain it again
headerBuf = recordFile.get( 0 );
header = new FileHeader( headerBuf, false );
}
/**
* Flushes the page manager. This forces a flush of all outstanding
* blocks (this is an implicit {@link RecordFile#commit} as well).
*
* @TODO completely wrong description of method
*/
void rollback() throws IOException
{
// release header
recordFile.discard( headerBuf );
recordFile.rollback();
// and obtain it again
headerBuf = recordFile.get( 0 );
if ( headerBuf.readShort( 0 ) == 0 )
{
header = new FileHeader( headerBuf, true );
}
else
{
header = new FileHeader( headerBuf, false );
}
}
/**
* Closes the page manager. This flushes the page manager and releases
* the lock on the header.
*/
void close() throws IOException
{
recordFile.release( headerBuf );
recordFile.commit();
headerBuf = null;
header = null;
recordFile = null;
}
/**
* Returns the recordFile header.
*/
FileHeader getFileHeader()
{
return header;
}
RecordFile getRecordFile()
{
return recordFile;
}
}