You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2012/02/02 13:38:42 UTC
svn commit: r1239581 [6/9] - in /directory/apacheds/trunk/jdbm2: ./ src/
src/etc/ src/examples/ src/main/ src/main/java/ src/main/java/jdbm/
src/main/java/jdbm/btree/ src/main/java/jdbm/helper/
src/main/java/jdbm/htree/ src/main/java/jdbm/recman/ src/s...
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BaseRecordManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BaseRecordManager.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BaseRecordManager.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BaseRecordManager.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,769 @@
+/**
+ * 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.
+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: BaseRecordManager.java,v 1.8 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import jdbm.RecordManager;
+import jdbm.helper.DefaultSerializer;
+import jdbm.helper.Serializer;
+
+import org.apache.directory.server.i18n.I18n;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class manages records, which are uninterpreted blobs of data. The
+ * set of operations is simple and straightforward: you communicate with
+ * the class using long "rowids" and byte[] data blocks. Rowids are returned
+ * on inserts and you can stash them away someplace safe to be able to get
+ * back to them. Data blocks can be as long as you wish, and may have
+ * lengths different from the original when updating.
+ * <p>
+ * Operations are synchronized, so that only one of them will happen
+ * concurrently even if you hammer away from multiple threads. Operations
+ * are made atomic by keeping a transaction log which is recovered after
+ * a crash, so the operations specified by this interface all have ACID
+ * properties.
+ * <p>
+ * You identify a file by just the name. The package attaches <tt>.db</tt>
+ * for the database file, and <tt>.lg</tt> for the transaction log. The
+ * transaction log is synchronized regularly and then restarted, so don't
+ * worry if you see the size going up and down.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @author <a href="cg@cdegroot.com">Cees de Groot</a>
+ */
+public final class BaseRecordManager implements RecordManager
+{
+ /** A logger for this class */
+ private static final Logger LOG = LoggerFactory.getLogger( BaseRecordManager.class.getSimpleName() );
+
+ /** Underlying record recordFile. */
+ private RecordFile recordFile;
+
+ /** Physical row identifier manager. */
+ private PhysicalRowIdManager physMgr;
+
+ /** Logical to Physical row identifier manager. */
+ private LogicalRowIdManager logMgr;
+
+ /** Page manager. */
+ private PageManager pageMgr;
+
+ /** Reserved slot for name directory. */
+ public static final int NAME_DIRECTORY_ROOT = 0;
+
+ /** Static debugging flag */
+ public static final boolean DEBUG = false;
+
+ /**
+ * Directory of named JDBMHashtables. This directory is a persistent
+ * directory, stored as a Hashtable. It can be retrieved by using
+ * the NAME_DIRECTORY_ROOT.
+ */
+ private Map<String,Long> nameDirectory;
+
+ private static enum IOType
+ {
+ READ_IO,
+ WRITE_IO
+ }
+
+ /** TODO add asserts to check internal consistency */
+ private static class LockElement
+ {
+ private int readers;
+ private int waiters;
+ private boolean writer;
+
+ private Lock lock = new ReentrantLock();
+ private Condition cv = lock.newCondition();
+
+
+ public boolean anyReaders()
+ {
+ return readers > 0;
+ }
+
+
+ public boolean anyWaiters()
+ {
+ return waiters > 0;
+ }
+
+
+ public boolean beingWritten()
+ {
+ return writer;
+ }
+
+
+ public boolean anyUser()
+ {
+ return ( readers > 0 || waiters > 0 || writer );
+ }
+
+
+ public void bumpReaders()
+ {
+ readers++;
+ }
+
+
+ public void decrementReaders()
+ {
+ readers--;
+ }
+
+
+ public void bumpWaiters()
+ {
+ waiters++;
+ }
+
+
+ public void decrementWaiters()
+ {
+ waiters--;
+ }
+
+
+ public void setWritten()
+ {
+ writer = true;
+ }
+
+
+ public void unsetWritten()
+ {
+ writer = false;
+ }
+
+
+ public Lock getLock()
+ {
+ return lock;
+ }
+
+
+ public Condition getNoConflictingIOCondition()
+ {
+ return cv;
+ }
+ }
+
+
+ /**
+ * Map used to synchronize reads and writes on the same logical
+ * recid.
+ */
+ private final ConcurrentHashMap<Long, LockElement> lockElements;
+
+
+ /**
+ * Creates a record manager for the indicated file
+ *
+ * @throws IOException when the file cannot be opened or is not
+ * a valid file content-wise.
+ */
+ public BaseRecordManager( String filename ) throws IOException
+ {
+ recordFile = new RecordFile( filename );
+ pageMgr = new PageManager( recordFile );
+ physMgr = new PhysicalRowIdManager( pageMgr );
+ logMgr = new LogicalRowIdManager( pageMgr );
+ lockElements = new ConcurrentHashMap<Long, LockElement>();
+ }
+
+
+ /**
+ * Get the underlying Transaction Manager
+ */
+ public TransactionManager getTransactionManager() throws IOException
+ {
+ checkIfClosed();
+
+ return recordFile.getTxnMgr();
+ }
+
+
+ /**
+ * Switches off transactions for the record manager. This means
+ * that a) a transaction log is not kept, and b) writes aren't
+ * synch'ed after every update. This is useful when batch inserting
+ * into a new database.
+ * <p>
+ * Only call this method directly after opening the file, otherwise
+ * the results will be undefined.
+ */
+ public void disableTransactions()
+ {
+ checkIfClosed();
+ recordFile.disableTransactions();
+ }
+
+
+ /**
+ * Closes the record manager.
+ *
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public void close() throws IOException
+ {
+ checkIfClosed();
+
+ pageMgr.close();
+ pageMgr = null;
+
+ recordFile.close();
+ recordFile = null;
+ }
+
+
+ /**
+ * Inserts a new record using standard java object serialization.
+ *
+ * @param obj the object for the new record.
+ * @return the rowid for the new record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public long insert( Object obj ) throws IOException
+ {
+ return insert( obj, DefaultSerializer.INSTANCE );
+ }
+
+
+ /**
+ * Inserts a new record using a custom serializer.
+ *
+ * @param obj the object for the new record.
+ * @param serializer a custom serializer
+ * @return the rowid for the new record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public long insert( Object obj, Serializer serializer ) throws IOException
+ {
+ byte[] data;
+ long recid;
+ Location physRowId;
+
+ checkIfClosed();
+
+ data = serializer.serialize( obj );
+ physRowId = physMgr.insert( data, 0, data.length );
+ recid = logMgr.insert( physRowId ).toLong();
+
+ LOG.debug( "BaseRecordManager.insert() recid {} length {}", recid, data.length ) ;
+
+ return recid;
+ }
+
+
+ /**
+ * Deletes a record.
+ *
+ * @param recid the rowid for the record that should be deleted.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public void delete( long recid ) throws IOException
+ {
+ LockElement element;
+ checkIfClosed();
+
+ if ( recid <= 0 )
+ {
+ throw new IllegalArgumentException( I18n.err( I18n.ERR_536, recid ) );
+ }
+
+ LOG.debug( "BaseRecordManager.delete() recid {}", recid ) ;
+
+
+ element = beginIO( recid, IOType.WRITE_IO );
+
+ try
+ {
+ Location logRowId = new Location( recid );
+ Location physRowId = logMgr.fetch( logRowId );
+ physMgr.delete( physRowId );
+ logMgr.delete( logRowId );
+ }
+ finally
+ {
+ this.endIO( recid, element, IOType.WRITE_IO );
+ }
+ }
+
+
+ /**
+ * Updates a record using standard java object serialization.
+ *
+ * @param recid the recid for the record that is to be updated.
+ * @param obj the new object for the record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public void update( long recid, Object obj ) throws IOException
+ {
+ update( recid, obj, DefaultSerializer.INSTANCE );
+ }
+
+
+ /**
+ * Updates a record using a custom serializer.
+ *
+ * @param recid the recid for the record that is to be updated.
+ * @param obj the new object for the record.
+ * @param serializer a custom serializer
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public void update( long recid, Object obj, Serializer serializer ) throws IOException
+ {
+ LockElement element;
+
+ checkIfClosed();
+
+ if ( recid <= 0 )
+ {
+ throw new IllegalArgumentException( I18n.err( I18n.ERR_536, recid ) );
+ }
+
+ element = this.beginIO( recid, IOType.WRITE_IO );
+
+ try
+ {
+ Location logRecid = new Location( recid );
+ Location physRecid = logMgr.fetch( logRecid );
+
+ byte[] data = serializer.serialize( obj );
+
+ LOG.debug( "BaseRecordManager.update() recid {} length {}", recid, data.length ) ;
+
+ Location newRecid = physMgr.update( physRecid, data, 0, data.length );
+
+ if ( ! newRecid.equals( physRecid ) )
+ {
+ logMgr.update( logRecid, newRecid );
+ }
+ }
+ finally
+ {
+ endIO( recid, element, IOType.WRITE_IO );
+ }
+ }
+
+
+ /**
+ * Fetches a record using standard java object serialization.
+ *
+ * @param recid the recid for the record that must be fetched.
+ * @return the object contained in the record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public Object fetch( long recid ) throws IOException
+ {
+ return fetch( recid, DefaultSerializer.INSTANCE );
+ }
+
+
+ /**
+ * Fetches a record using a custom serializer.
+ *
+ * @param recid the recid for the record that must be fetched.
+ * @param serializer a custom serializer
+ * @return the object contained in the record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public Object fetch( long recid, Serializer serializer ) throws IOException
+ {
+ Object result;
+ LockElement element;
+
+ checkIfClosed();
+
+ if ( recid <= 0 )
+ {
+ throw new IllegalArgumentException( I18n.err( I18n.ERR_536, recid ) );
+ }
+
+ element = this.beginIO(recid, IOType.READ_IO);
+
+ try
+ {
+ byte[] data;
+
+ Location location = logMgr.fetch( new Location( recid ) ) ;
+ data = physMgr.fetch( location );
+
+ LOG.debug( "BaseRecordManager.fetch() recid {} length {}", recid, data.length ) ;
+
+ result = serializer.deserialize( data );
+ }
+ finally
+ {
+ endIO(recid, element, IOType.READ_IO);
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Returns the number of slots available for "root" rowids. These slots
+ * can be used to store special rowids, like rowids that point to
+ * other rowids. Root rowids are useful for bootstrapping access to
+ * a set of data.
+ */
+ public int getRootCount()
+ {
+ return FileHeader.NROOTS;
+ }
+
+
+ /**
+ * Returns the indicated root rowid.
+ *
+ * @see #getRootCount
+ */
+ public long getRoot( int id ) throws IOException
+ {
+ checkIfClosed();
+
+ return pageMgr.getFileHeader().getRoot( id );
+ }
+
+
+ /**
+ * Sets the indicated root rowid.
+ *
+ * @see #getRootCount
+ */
+ public void setRoot( int id, long rowid ) throws IOException
+ {
+ checkIfClosed();
+
+ pageMgr.getFileHeader().setRoot( id, rowid );
+ }
+
+
+ /**
+ * Obtain the record id of a named object. Returns 0 if named object
+ * doesn't exist.
+ */
+ public long getNamedObject( String name ) throws IOException
+ {
+ checkIfClosed();
+
+ Map<String,Long> nameDirectory = getNameDirectory();
+ Long recid = nameDirectory.get( name );
+
+ if ( recid == null )
+ {
+ return 0;
+ }
+
+ return recid;
+ }
+
+
+ /**
+ * Set the record id of a named object.
+ */
+ public void setNamedObject( String name, long recid ) throws IOException
+ {
+ checkIfClosed();
+
+ if ( recid == 0 )
+ {
+ // remove from hashtable
+ getNameDirectory().remove( name );
+ }
+ else
+ {
+ getNameDirectory().put( name, recid );
+ }
+
+ saveNameDirectory( );
+ }
+
+
+ /**
+ * Commit (make persistent) all changes since beginning of transaction.
+ */
+ public void commit() throws IOException
+ {
+ checkIfClosed();
+
+ pageMgr.commit();
+ }
+
+
+ /**
+ * Rollback (cancel) all changes since beginning of transaction.
+ */
+ public void rollback() throws IOException
+ {
+ checkIfClosed();
+
+ pageMgr.rollback();
+ }
+
+
+ /**
+ * Load name directory
+ */
+ @SuppressWarnings("unchecked")
+ private Map<String,Long> getNameDirectory() throws IOException
+ {
+ // retrieve directory of named hashtable
+ long nameDirectory_recid = getRoot( NAME_DIRECTORY_ROOT );
+
+ if ( nameDirectory_recid == 0 )
+ {
+ nameDirectory = new HashMap<String, Long>();
+ nameDirectory_recid = insert( nameDirectory );
+ setRoot( NAME_DIRECTORY_ROOT, nameDirectory_recid );
+ }
+ else
+ {
+ nameDirectory = ( Map<String, Long> ) fetch( nameDirectory_recid );
+ }
+
+ return nameDirectory;
+ }
+
+
+ private void saveNameDirectory( ) throws IOException
+ {
+ long recid = getRoot( NAME_DIRECTORY_ROOT );
+
+ if ( recid == 0 )
+ {
+ throw new IOException( I18n.err( I18n.ERR_537 ) );
+ }
+
+ update( recid, nameDirectory );
+ }
+
+
+ /**
+ * Check if RecordManager has been closed. If so, throw an IllegalStateException.
+ */
+ private void checkIfClosed() throws IllegalStateException
+ {
+ if ( recordFile == null )
+ {
+ throw new IllegalStateException( I18n.err( I18n.ERR_538 ) );
+ }
+ }
+
+
+ /**
+ * Used to serialize reads/write on a given logical rowid. Checks if there is a
+ * ongoing conflicting IO to the same logical rowid and waits for the ongoing
+ * write if there is one.
+ *
+ * @param recid the logical rowid for which the fetch will be done.
+ * @param io type of the IO
+ * @return lock element representing the logical lock gotten
+ */
+ private LockElement beginIO( Long recid, IOType io )
+ {
+ boolean lockVerified = false;
+ LockElement element = null;
+
+ // loop until we successfully verify that there is no concurrent writer
+/*
+ element = lockElements.get( recid );
+
+ do
+ {
+ if ( element == null )
+ {
+ element = new LockElement();
+
+ if ( io == IOType.READ_IO )
+ {
+ element.bumpReaders();
+ }
+ else
+ {
+ element.setWritten();
+ }
+
+ LockElement existingElement = lockElements.putIfAbsent( recid, element );
+
+ if ( existingElement == null )
+ {
+ lockVerified = true;
+ }
+ else
+ {
+ element = existingElement;
+ }
+ }
+ else
+ {
+ Lock lock = element.getLock();
+ lock.lock();
+
+ if ( element.anyUser() )
+ {
+ if ( this.conflictingIOPredicate( io, element ) )
+ {
+ element.bumpWaiters();
+
+ do
+ {
+ element.getNoConflictingIOCondition()
+ .awaitUninterruptibly();
+ }
+ while ( this.conflictingIOPredicate( io, element ) );
+
+ element.decrementWaiters();
+ }
+
+ // no conflicting IO anymore..done
+ if ( io == IOType.READ_IO )
+ {
+ element.bumpReaders();
+ }
+ else
+ {
+ element.setWritten();
+ }
+
+ lockVerified = true;
+ }
+ else
+ {
+ if ( io == IOType.READ_IO )
+ {
+ element.bumpReaders();
+ }
+ else
+ {
+ element.setWritten();
+ }
+
+ LockElement existingElement = lockElements.get( recid );
+
+ if ( element != existingElement )
+ {
+ element = existingElement;
+ }
+ else
+ {
+ lockVerified = true; // done
+ }
+ }
+
+ lock.unlock();
+ }
+ }
+ while ( !lockVerified );
+*/
+ return element;
+ }
+
+
+ /**
+ * Ends the IO by releasing the logical lock on the given recid
+ *
+ * @param recid logical recid for which the IO is being ended
+ * @param element logical lock to be released
+ * @param io type of the io
+ */
+ private void endIO( Long recid, LockElement element, IOType io )
+ {
+ /*
+ Lock lock = element.getLock();
+ lock.lock();
+
+ if ( io == IOType.READ_IO )
+ {
+ element.decrementReaders();
+ }
+ else
+ {
+ element.unsetWritten();
+ }
+
+ if ( element.anyWaiters() )
+ {
+ element.getNoConflictingIOCondition().notifyAll();
+ }
+
+ if ( !element.anyUser() )
+ {
+ lockElements.remove( recid );
+ }
+
+ lock.unlock();
+ */
+ }
+
+
+ private boolean conflictingIOPredicate( IOType io, LockElement element )
+ {
+ if ( io == IOType.READ_IO )
+ {
+ return element.beingWritten();
+ }
+ else
+ {
+ return ( element.anyReaders() || element.beingWritten() );
+ }
+ }
+}
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BlockIo.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BlockIo.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BlockIo.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BlockIo.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,429 @@
+/**
+ * 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: BlockIo.java,v 1.2 2002/08/06 05:18:36 boisvert Exp $
+ */
+package jdbm.recman;
+
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+import org.apache.directory.server.i18n.I18n;
+
+
+/**
+ * This class wraps a page-sized byte array and provides methods to read and
+ * write data to and from it. The readers and writers are just the ones that
+ * the rest of the toolkit needs, nothing else. Values written are compatible
+ * with java.io routines.
+ *
+ * This block is never accessed directly, so it does not have to be thread-safe.
+ *
+ * @see java.io.DataInput
+ * @see java.io.DataOutput
+ */
+public final class BlockIo implements java.io.Externalizable
+{
+ public final static long serialVersionUID = 2L;
+
+ /** The block Identifier */
+ private long blockId;
+
+ /** The row data contained in this block */
+ private byte[] data;
+
+ /** A view on the BlockIo */
+ private BlockView view = null;
+
+ /** A flag set when this block has been modified */
+ private boolean dirty = false;
+
+ /** The number of pending transaction on this block */
+ private AtomicInteger transactionCount = new AtomicInteger( 0 );
+
+
+ /**
+ * Default constructor for serialization
+ */
+ public BlockIo()
+ {
+ // empty
+ }
+
+
+ /**
+ * Constructs a new BlockIo instance.
+ *
+ * @param blockId The identifier for this block
+ * @param data The data to store
+ */
+ /*No qualifier*/ BlockIo( long blockId, byte[] data )
+ {
+ // remove me for production version
+ if ( blockId < 0 )
+ {
+ throw new Error( I18n.err( I18n.ERR_539_BAD_BLOCK_ID, blockId ) );
+ }
+
+ this.blockId = blockId;
+ this.data = data;
+ }
+
+
+ /**
+ * @return the underlying array
+ */
+ /*No qualifier*/ byte[] getData()
+ {
+ return data;
+ }
+
+
+ /**
+ * Sets the block number. Should only be called by RecordFile.
+ *
+ * @param The block identifier
+ */
+ /*No qualifier*/ void setBlockId( long blockId )
+ {
+ if ( isInTransaction() )
+ {
+ throw new Error( I18n.err( I18n.ERR_540 ) );
+ }
+
+ if ( blockId < 0 )
+ {
+ throw new Error( I18n.err( I18n.ERR_539_BAD_BLOCK_ID, blockId ) );
+ }
+
+ this.blockId = blockId;
+ }
+
+
+ /**
+ * @return the block number.
+ */
+ /*No qualifier*/ long getBlockId()
+ {
+ return blockId;
+ }
+
+
+ /**
+ * @return the current view of the block.
+ */
+ public BlockView getView()
+ {
+ return view;
+ }
+
+
+ /**
+ * Sets the current view of the block.
+ *
+ * @param view the current view
+ */
+ public void setView( BlockView view )
+ {
+ this.view = view;
+ }
+
+
+ /**
+ * Sets the dirty flag
+ */
+ /*No qualifier*/ void setDirty()
+ {
+ dirty = true;
+ }
+
+
+ /**
+ * Clears the dirty flag
+ */
+ /*No qualifier*/ void setClean()
+ {
+ dirty = false;
+ }
+
+
+ /**
+ * Returns true if the dirty flag is set.
+ */
+ /*No qualifier*/ boolean isDirty()
+ {
+ return dirty;
+ }
+
+
+ /**
+ * Returns true if the block is still dirty with respect to the
+ * transaction log.
+ */
+ /*No qualifier*/ boolean isInTransaction()
+ {
+ return transactionCount.get() != 0;
+ }
+
+
+ /**
+ * Increments transaction count for this block, to signal that this
+ * block is in the log but not yet in the data recordFile. The method also
+ * takes a snapshot so that the data may be modified in new transactions.
+ */
+ /*No qualifier*/ void incrementTransactionCount()
+ {
+ transactionCount.getAndIncrement();
+ }
+
+
+ /**
+ * Decrements transaction count for this block, to signal that this
+ * block has been written from the log to the data recordFile.
+ */
+ /*No qualifier*/ void decrementTransactionCount()
+ {
+ if ( transactionCount.decrementAndGet() < 0 )
+ {
+ throw new Error( I18n.err( I18n.ERR_541, getBlockId() ) );
+ }
+ }
+
+
+ /**
+ * Reads a byte from the indicated position
+ *
+ * @param pos the position at which we will read the byte
+ * @return the read byte
+ */
+ public byte readByte( int pos )
+ {
+ return data[pos];
+ }
+
+
+ /**
+ * Writes a byte to the indicated position
+ *
+ * @param pos The position where we want to write the value to
+ * @param value the byte value we want to write into the BlockIo
+ */
+ public void writeByte( int pos, byte value )
+ {
+ data[pos] = value;
+ dirty = true;
+ }
+
+
+ /**
+ * Reads a short from the indicated position
+ *
+ * @param pos the position at which we will read the short
+ * @return the read short
+ */
+ public short readShort( int pos )
+ {
+ return ( short )
+ ( ( ( data[pos+0] & 0xff ) << 8 ) |
+ ( ( data[pos+1] & 0xff ) << 0 ) );
+ }
+
+
+ /**
+ * Writes a short to the indicated position
+ *
+ * @param pos The position where we want to write the value to
+ * @param value the short value we want to write into the BlockIo
+ */
+ public void writeShort( int pos, short value )
+ {
+ data[pos+0] = ( byte ) ( 0xff & ( value >> 8 ) );
+ data[pos+1] = ( byte ) ( 0xff & ( value >> 0 ) );
+ dirty = true;
+ }
+
+
+ /**
+ * Reads an int from the indicated position
+ *
+ * @param pos the position at which we will read the int
+ * @return the read int
+ */
+ public int readInt( int pos )
+ {
+ return
+ ( data[pos+0] << 24) |
+ ( ( data[pos+1] & 0xff ) << 16) |
+ ( ( data[pos+2] & 0xff ) << 8) |
+ ( ( data[pos+3] & 0xff ) << 0 );
+ }
+
+
+ /**
+ * Writes an int to the indicated position
+ *
+ * @param pos The position where we want to write the value to
+ * @param value the int value we want to write into the BlockIo
+ */
+ public void writeInt( int pos, int value )
+ {
+ data[pos+0] = ( byte ) ( 0xff & ( value >> 24 ) );
+ data[pos+1] = ( byte ) ( 0xff & ( value >> 16 ) );
+ data[pos+2] = ( byte ) ( 0xff & ( value >> 8 ) );
+ data[pos+3] = ( byte ) ( 0xff & ( value >> 0 ) );
+ dirty = true;
+ }
+
+
+ /**
+ * Reads a long from the indicated position
+ *
+ * @param pos the position at which we will read the long
+ * @return the read long
+ */
+ public long readLong( int pos )
+ {
+ return
+ ( ( long )( (long)data[pos+0] << 56 ) |
+ ( (long)( data[pos+1] & 0xff ) << 48 ) |
+ ( (long)( data[pos+2] & 0xff ) << 40 ) |
+ ( (long)( data[pos+3] & 0xff ) << 32 ) |
+ ( (long)( data[pos+4] & 0xff ) << 24 ) |
+ ( (long)( data[pos+5] & 0xff ) << 16 ) |
+ ( (long)( data[pos+6] & 0xff ) << 8 ) |
+ ( (long)( data[pos+7] & 0xff ) ) );
+ }
+
+
+ /**
+ * Writes a long to the indicated position
+ *
+ * @param pos The position where we want to write the value to
+ * @param value the long value we want to write into the BlockIo
+ */
+ public void writeLong(int pos, long value) {
+ data[pos+0] = (byte)(0xff & (value >> 56));
+ data[pos+1] = (byte)(0xff & (value >> 48));
+ data[pos+2] = (byte)(0xff & (value >> 40));
+ data[pos+3] = (byte)(0xff & (value >> 32));
+ data[pos+4] = (byte)(0xff & (value >> 24));
+ data[pos+5] = (byte)(0xff & (value >> 16));
+ data[pos+6] = (byte)(0xff & (value >> 8));
+ data[pos+7] = (byte)(0xff & (value >> 0));
+ dirty = true;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString()
+ {
+ if ( view != null )
+ {
+ return view.toString();
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "BlockIO ( " );
+
+ // The blockID
+ sb.append( blockId ).append( ", " );
+
+ // Is it dirty ?
+ if ( dirty )
+ {
+ sb.append( "dirty, " );
+ }
+ else
+ {
+ sb.append( "clean, " );
+ }
+
+ // The view
+ if ( view != null )
+ {
+ sb.append( view.getClass().getSimpleName() ).append( ", " );
+ }
+ else
+ {
+ sb.append( "no view, " );
+ }
+
+ // The transaction count
+ sb.append( "tx: " ).append( transactionCount.get() );
+
+ sb.append( " )" );
+
+ return sb.toString();
+ }
+
+
+ /**
+ * implement externalizable interface
+ */
+ public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
+ {
+ blockId = in.readLong();
+ int length = in.readInt();
+ data = new byte[length];
+ in.readFully(data);
+ }
+
+
+ /**
+ * implement externalizable interface
+ */
+ public void writeExternal( ObjectOutput out ) throws IOException
+ {
+ out.writeLong( blockId );
+ out.writeInt( data.length );
+ out.write( data );
+ }
+}
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BlockView.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BlockView.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BlockView.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/BlockView.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,59 @@
+/**
+ * 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: BlockView.java,v 1.2 2005/06/25 23:12:32 doomdark Exp $
+ */
+package jdbm.recman;
+
+
+
+/**
+ * This is a marker interface that is implemented by classes that
+ * interpret blocks of data by pretending to be an overlay.
+ *
+ * @see BlockIo#setView
+ */
+public interface BlockView
+{
+}
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/CacheRecordManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/CacheRecordManager.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/CacheRecordManager.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/CacheRecordManager.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,468 @@
+/**
+ * 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.
+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: CacheRecordManager.java,v 1.9 2005/06/25 23:12:32 doomdark Exp $
+ */
+package jdbm.recman;
+
+
+import jdbm.RecordManager;
+import jdbm.helper.CacheEvictionException;
+import jdbm.helper.CachePolicy;
+import jdbm.helper.CachePolicyListener;
+import jdbm.helper.DefaultSerializer;
+import jdbm.helper.Serializer;
+import jdbm.helper.WrappedRuntimeException;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.i18n.I18n;
+
+
+/**
+ * A RecordManager wrapping and caching another RecordManager.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @author <a href="cg@cdegroot.com">Cees de Groot</a>
+ */
+public class CacheRecordManager implements RecordManager
+{
+ /** Wrapped RecordManager */
+ protected RecordManager recordManager;
+
+ /** Cache for underlying RecordManager */
+ protected CachePolicy<Long, CacheEntry> cache;
+
+
+ /**
+ * Construct a CacheRecordManager wrapping another RecordManager and
+ * using a given cache policy.
+ *
+ * @param recordManager Wrapped RecordManager
+ * @param cache Cache policy
+ */
+ public CacheRecordManager( RecordManager recordManager, CachePolicy<Long,CacheEntry> cache )
+ {
+ if ( recordManager == null )
+ {
+ throw new IllegalArgumentException( I18n.err( I18n.ERR_517 ) );
+ }
+
+ if ( cache == null )
+ {
+ throw new IllegalArgumentException( I18n.err( I18n.ERR_542 ) );
+ }
+
+ this.recordManager = recordManager;
+ this.cache = cache;
+ this.cache.addListener( new CacheListener() );
+ }
+
+
+ /**
+ * Get the underlying Record Manager.
+ *
+ * @return underlying RecordManager or null if CacheRecordManager has
+ * been closed.
+ */
+ public RecordManager getRecordManager()
+ {
+ return recordManager;
+ }
+
+
+ /**
+ * Get the underlying cache policy
+ *
+ * @return underlying CachePolicy or null if CacheRecordManager has
+ * been closed.
+ */
+ public CachePolicy<Long,CacheEntry> getCachePolicy()
+ {
+ return cache;
+ }
+
+
+ /**
+ * Inserts a new record using a custom serializer.
+ *
+ * @param obj the object for the new record.
+ * @return the rowid for the new record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public long insert( Object obj ) throws IOException
+ {
+ return insert( obj, DefaultSerializer.INSTANCE );
+ }
+
+
+ /**
+ * Inserts a new record using a custom serializer.
+ *
+ * @param obj the object for the new record.
+ * @param serializer a custom serializer
+ * @return the rowid for the new record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public synchronized long insert( Object obj, Serializer serializer ) throws IOException
+ {
+ checkIfClosed();
+
+ long recid = recordManager.insert( obj, serializer );
+
+ try
+ {
+ cache.put( recid, new CacheEntry( recid, obj, serializer, false ) );
+ }
+ catch ( CacheEvictionException except )
+ {
+ throw new WrappedRuntimeException( except );
+ }
+
+ return recid;
+ }
+
+
+ /**
+ * Deletes a record.
+ *
+ * @param recid the rowid for the record that should be deleted.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public synchronized void delete( long recid ) throws IOException
+ {
+ checkIfClosed();
+
+ // Remove the entry from the underlying storage
+ recordManager.delete( recid );
+
+ // And now update the cache
+ cache.remove( recid );
+ }
+
+
+ /**
+ * Updates a record using standard Java serialization.
+ *
+ * @param recid the recid for the record that is to be updated.
+ * @param obj the new object for the record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public void update( long recid, Object obj ) throws IOException
+ {
+ update( recid, obj, DefaultSerializer.INSTANCE );
+ }
+
+
+ /**
+ * Updates a record using a custom serializer.
+ *
+ * @param recid the recid for the record that is to be updated.
+ * @param obj the new object for the record.
+ * @param serializer a custom serializer
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public synchronized void update( long recid, Object obj, Serializer serializer ) throws IOException
+ {
+ checkIfClosed();
+
+ try {
+ CacheEntry entry = cache.get( recid );
+
+ if ( entry != null )
+ {
+ // reuse existing cache entry
+ entry.obj = obj;
+ entry.serializer = serializer;
+ entry.isDirty = true;
+ }
+ else
+ {
+ cache.put( recid, new CacheEntry( recid, obj, serializer, true ) );
+ }
+ }
+ catch ( CacheEvictionException except )
+ {
+ throw new IOException( except.getLocalizedMessage() );
+ }
+ }
+
+
+ /**
+ * Fetches a record using standard Java serialization.
+ *
+ * @param recid the recid for the record that must be fetched.
+ * @return the object contained in the record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public Object fetch( long recid ) throws IOException
+ {
+ return fetch( recid, DefaultSerializer.INSTANCE );
+ }
+
+
+ /**
+ * Fetches a record using a custom serializer.
+ *
+ * @param recid the recid for the record that must be fetched.
+ * @param serializer a custom serializer
+ * @return the object contained in the record.
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public synchronized Object fetch( long recid, Serializer serializer ) throws IOException
+ {
+ checkIfClosed();
+
+ CacheEntry entry = cache.get( recid );
+
+ if ( entry == null )
+ {
+ entry = new CacheEntry( recid, null, serializer, false );
+ entry.obj = recordManager.fetch( recid, serializer );
+
+ try
+ {
+ cache.put( recid, entry );
+ }
+ catch ( CacheEvictionException except )
+ {
+ throw new WrappedRuntimeException( except );
+ }
+ }
+
+ if ( entry.obj instanceof byte[] )
+ {
+ byte[] copy = new byte[ ( ( byte[] ) entry.obj ).length ];
+ System.arraycopy( entry.obj, 0, copy, 0, ( ( byte[] ) entry.obj ).length );
+ return copy;
+ }
+
+ return entry.obj;
+ }
+
+
+ /**
+ * Closes the record manager.
+ *
+ * @throws IOException when one of the underlying I/O operations fails.
+ */
+ public synchronized void close() throws IOException
+ {
+ checkIfClosed();
+
+ updateCacheEntries();
+ recordManager.close();
+ recordManager = null;
+ cache = null;
+ }
+
+
+ /**
+ * Returns the number of slots available for "root" rowids. These slots
+ * can be used to store special rowids, like rowids that point to
+ * other rowids. Root rowids are useful for bootstrapping access to
+ * a set of data.
+ */
+ public synchronized int getRootCount()
+ {
+ checkIfClosed();
+
+ return recordManager.getRootCount();
+ }
+
+
+ /**
+ * Returns the indicated root rowid.
+ *
+ * @see #getRootCount
+ */
+ public synchronized long getRoot( int id ) throws IOException
+ {
+ checkIfClosed();
+
+ return recordManager.getRoot( id );
+ }
+
+
+ /**
+ * Sets the indicated root rowid.
+ *
+ * @see #getRootCount
+ */
+ public synchronized void setRoot( int id, long rowid ) throws IOException
+ {
+ checkIfClosed();
+
+ recordManager.setRoot( id, rowid );
+ }
+
+
+ /**
+ * Commit (make persistent) all changes since beginning of transaction.
+ */
+ public synchronized void commit() throws IOException
+ {
+ checkIfClosed();
+ updateCacheEntries();
+ recordManager.commit();
+ }
+
+
+ /**
+ * Rollback (cancel) all changes since beginning of transaction.
+ */
+ public synchronized void rollback() throws IOException
+ {
+ checkIfClosed();
+
+ recordManager.rollback();
+
+ // discard all cache entries since we don't know which entries
+ // where part of the transaction
+ cache.removeAll();
+ }
+
+
+ /**
+ * Obtain the record id of a named object. Returns 0 if named object
+ * doesn't exist.
+ */
+ public synchronized long getNamedObject( String name ) throws IOException
+ {
+ checkIfClosed();
+
+ return recordManager.getNamedObject( name );
+ }
+
+
+ /**
+ * Set the record id of a named object.
+ */
+ public synchronized void setNamedObject( String name, long recid ) throws IOException
+ {
+ checkIfClosed();
+
+ recordManager.setNamedObject( name, recid );
+ }
+
+
+ /**
+ * Check if RecordManager has been closed. If so, throw an IllegalStateException
+ */
+ private void checkIfClosed() throws IllegalStateException
+ {
+ if ( recordManager == null )
+ {
+ throw new IllegalStateException( I18n.err( I18n.ERR_538 ) );
+ }
+ }
+
+
+ /**
+ * Update all dirty cache objects to the underlying RecordManager.
+ */
+ protected void updateCacheEntries() throws IOException
+ {
+ Enumeration<CacheEntry> enume = cache.elements();
+
+ while ( enume.hasMoreElements() )
+ {
+ CacheEntry entry = enume.nextElement();
+
+ if ( entry.isDirty )
+ {
+ recordManager.update( entry.recid, entry.obj, entry.serializer );
+ entry.isDirty = false;
+ }
+ }
+ }
+
+ /**
+ * A class to store a cached entry.
+ */
+ private static class CacheEntry
+ {
+ long recid;
+ Object obj;
+ Serializer serializer;
+ boolean isDirty;
+
+ CacheEntry( long recid, Object obj, Serializer serializer, boolean isDirty )
+ {
+ this.recid = recid;
+ this.obj = obj;
+ this.serializer = serializer;
+ this.isDirty = isDirty;
+ }
+
+ } // class CacheEntry
+
+
+ private class CacheListener implements CachePolicyListener<CacheEntry>
+ {
+
+ /**
+ * Notification that cache is evicting an object
+ *
+ * @param obj object evicted from cache
+ */
+ public void cacheObjectEvicted( CacheEntry obj ) throws CacheEvictionException
+ {
+ CacheEntry entry = obj;
+ if ( entry.isDirty )
+ {
+ try
+ {
+ recordManager.update( entry.recid, entry.obj, entry.serializer );
+ }
+ catch ( IOException except )
+ {
+ throw new CacheEvictionException( except );
+ }
+ }
+ }
+ }
+}
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/DataPage.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/DataPage.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/DataPage.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/DataPage.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,145 @@
+/**
+ * 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: DataPage.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+package jdbm.recman;
+
+
+
+import org.apache.directory.server.i18n.I18n;
+
+
+/**
+ * Class describing a page that holds data.
+ */
+final class DataPage extends PageHeader
+{
+ // offsets
+ /** first short in the file after the page header info: 18 byte offset */
+ private static final short O_FIRST = PageHeader.SIZE; // short firstrowid
+
+ /** start of the data in this block: 20 byte offset */
+ static final short O_DATA = ( short ) ( O_FIRST + Magic.SZ_SHORT );
+
+ /** total amount of data in this page/block: BLOCK_SIZE - 20 bytes */
+ static final short DATA_PER_PAGE = ( short ) ( RecordFile.BLOCK_SIZE - O_DATA );
+
+
+ /**
+ * Constructs a data page view from the indicated block.
+ */
+ DataPage( BlockIo block )
+ {
+ super( block );
+ }
+
+
+ /**
+ * Factory method to create or return a data page for the indicated block.
+ */
+ static DataPage getDataPageView( BlockIo blockIo )
+ {
+ BlockView view = blockIo.getView();
+
+ if ( ( view != null ) && ( view instanceof DataPage ) )
+ {
+ return ( DataPage ) view;
+ }
+ else
+ {
+ return new DataPage( blockIo );
+ }
+ }
+
+
+ /**
+ * @return the first rowid's offset
+ */
+ short getFirst()
+ {
+ return blockIo.readShort( O_FIRST );
+ }
+
+
+ /**
+ * Sets the first rowid's offset
+ */
+ void setFirst( short value )
+ {
+ paranoiaMagicOk();
+
+ if ( value > 0 && value < O_DATA )
+ {
+ throw new Error( I18n.err( I18n.ERR_543, value ) );
+ }
+
+ blockIo.writeShort( O_FIRST, value );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "DataPage ( " );
+
+ // The blockIO
+ sb.append( super.toString() ).append( ", " );
+
+ // The first rowId
+ sb.append( "first rowId: " ).append( getFirst() ).append( ", " );
+
+ // The data per page
+ sb.append( "[p:" ).append( getPrev() ).append( ", " );
+
+ // The next page
+ sb.append( "n:" ).append( getNext() ).append( "] )" );
+
+ return sb.toString();
+ }
+}
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FileHeader.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FileHeader.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FileHeader.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FileHeader.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,258 @@
+/**
+ * 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: FileHeader.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
+ */
+package jdbm.recman;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.i18n.I18n;
+
+
+/**
+ * This class represents a file header. It is a 1:1 representation of
+ * the data that appears in block 0 of a file.<br/>
+ * The FileHeader is stored into a BlockIo.<br/>
+ * The BlockIo will always contain the following bytes : <br/>
+ * <ul>
+ * <li>[0..3] 0x1350 : a marker for a FILE_HEADER</li>
+ * <li>[4..11] : The BlockIo reference to the first FREE_PAGE ID</li>
+ * <li>[12..19] : The BlockIo reference to the last FREE_PAGE ID</li>
+ * <li>[20..27] : The BlockIo reference to the first USED_PAGE ID</li>
+ * <li>[28..35] : The BlockIo reference to the last USED_PAGE ID</li>
+ * <li>[36..43] : The BlockIo reference to the first TRANSLATION_PAGE ID</li>
+ * <li>[44..51] : The BlockIo reference to the last TRANSLATION_PAGE ID</li>
+ * <li>[52..59] : The BlockIo reference to the first FREELOGIDS_PAGE ID</li>
+ * <li>[60..67] : The BlockIo reference to the last FREELOGIDS_PAGE ID</li>
+ * <li>[68..71] : The BlockIo reference to the first FREEPHYSIDS_PAGE ID</li>
+ * <li>[72..79] : The BlockIo reference to the last FREEPHYSIDS_PAGE ID</li>
+ * <li>[80..87]* : The reference to the BlockIo which is the root for the data contained in this File.
+ * We may have more than one, but no more than 1014, if the BLOCK_SIZE is 8192</li>
+ * </ul>
+ */
+class FileHeader implements BlockView
+{
+ /** Position of the Magic number for FileHeader */
+ private static final short O_MAGIC = 0; // short magic
+
+ /** Position of the Lists in the blockIo */
+ private static final short O_LISTS = Magic.SZ_SHORT; // long[2*NLISTS]
+
+ /** Position of the ROOTs in the blockIo */
+ private static final int O_ROOTS = O_LISTS + ( Magic.NLISTS * 2 * Magic.SZ_LONG );
+
+ /** The BlockIo used to store the FileHeader */
+ private BlockIo block;
+
+ /** The number of "root" rowids available in the file. */
+ static final int NROOTS = ( RecordFile.BLOCK_SIZE - O_ROOTS ) / Magic.SZ_LONG;
+
+
+ /**
+ * Constructs a FileHeader object from a block.
+ *
+ * @param block The block that contains the file header
+ * @param isNew If true, the file header is for a new file.
+ * @throws IOException if the block is too short to keep the file
+ * header.
+ */
+ FileHeader( BlockIo block, boolean isNew )
+ {
+ this.block = block;
+
+ if ( isNew )
+ {
+ block.writeShort( O_MAGIC, Magic.FILE_HEADER );
+ }
+ else if ( block.readShort( O_MAGIC ) != Magic.FILE_HEADER )
+ {
+ throw new Error( I18n.err( I18n.ERR_544, block.readShort( O_MAGIC ) ) );
+ }
+ }
+
+
+ /**
+ * Returns the offset of the "first" block of the indicated list
+ */
+ private short offsetOfFirst( int list )
+ {
+ return ( short ) ( O_LISTS + ( 2 * Magic.SZ_LONG * list ) );
+ }
+
+
+ /**
+ * Returns the offset of the "last" block of the indicated list
+ */
+ private short offsetOfLast( int list )
+ {
+ return ( short ) ( offsetOfFirst( list ) + Magic.SZ_LONG );
+ }
+
+
+ /**
+ * Returns the offset of the indicated root
+ */
+ private short offsetOfRoot( int root )
+ {
+ return ( short ) ( O_ROOTS + ( root * Magic.SZ_LONG ) );
+ }
+
+
+ /**
+ * Returns the first block of the indicated list
+ */
+ long getFirstOf( int list )
+ {
+ return block.readLong( offsetOfFirst( list ) );
+ }
+
+
+ /**
+ * Sets the first block of the indicated list
+ */
+ void setFirstOf( int list, long value )
+ {
+ block.writeLong( offsetOfFirst( list ), value );
+ }
+
+
+ /**
+ * Returns the last block of the indicated list
+ */
+ long getLastOf( int list )
+ {
+ return block.readLong( offsetOfLast( list ) );
+ }
+
+
+ /**
+ * Sets the last block of the indicated list
+ */
+ void setLastOf( int list, long value )
+ {
+ block.writeLong( offsetOfLast( list ), value );
+ }
+
+
+ /**
+ * Returns the indicated root rowid. A root rowid is a special rowid
+ * that needs to be kept between sessions. It could conceivably be
+ * stored in a special file, but as a large amount of space in the
+ * block header is wasted anyway, it's more useful to store it where
+ * it belongs.
+ *
+ * @see #NROOTS
+ */
+ long getRoot( int root )
+ {
+ return block.readLong( offsetOfRoot( root ) );
+ }
+
+
+ /**
+ * Sets the indicated root rowid.
+ *
+ * @see #getRoot
+ * @see #NROOTS
+ */
+ void setRoot( int root, long rowid )
+ {
+ block.writeLong( offsetOfRoot( root ), rowid );
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "FileHeader ( " );
+
+ // The blockIO
+ sb.append( block ).append( ", " );
+
+ // The free pages
+ sb.append( "free[" );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.FREE_PAGE ) ) ) );
+ sb.append( ", " );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.FREE_PAGE ) + Magic.SZ_LONG ) ) );
+ sb.append( "], " );
+
+ // The used pages
+ sb.append( "used[" );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.USED_PAGE ) ) ) );
+ sb.append( ", " );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.USED_PAGE ) + Magic.SZ_LONG ) ) );
+ sb.append( "], " );
+
+ // The translation pages
+ sb.append( "translation[" );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.TRANSLATION_PAGE ) ) ) );
+ sb.append( ", " );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.TRANSLATION_PAGE ) + Magic.SZ_LONG ) ) );
+ sb.append( "], " );
+
+ // The freeLogIds pages
+ sb.append( "freeLogIds[" );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.FREELOGIDS_PAGE ) ) ) );
+ sb.append( ", " );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.FREELOGIDS_PAGE ) + Magic.SZ_LONG ) ) );
+ sb.append( "], " );
+
+ // The freePhysIds pages
+ sb.append( "freePhysIds[" );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.FREEPHYSIDS_PAGE ) ) ) );
+ sb.append( ", " );
+ sb.append( block.readLong( ( short ) ( 2 + ( 2 * Magic.SZ_LONG * Magic.FREEPHYSIDS_PAGE ) + Magic.SZ_LONG ) ) );
+ sb.append( "]" );
+
+ sb.append( " )" );
+
+ return sb.toString();
+ }
+}
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,242 @@
+/**
+ * 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: FreeLogicalRowIdPage.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+package jdbm.recman;
+
+
+
+/**
+ * Class describing a page that holds logical rowids that were freed. Note
+ * that the methods have *physical* rowids in their signatures - this is
+ * because logical and physical rowids are internally the same, only their
+ * external representation (i.e. in the client API) differs.
+ */
+class FreeLogicalRowIdPage extends PageHeader
+{
+ /** The offset for the number of free pages */
+ private static final short O_COUNT = PageHeader.SIZE; // short count
+
+ /** Offset of the number of free row Ids */
+ static final short O_FREE = O_COUNT + Magic.SZ_SHORT;
+
+ /** The number of elements by page */
+ static final short ELEMS_PER_PAGE = ( RecordFile.BLOCK_SIZE - O_FREE ) / PhysicalRowId.SIZE;
+
+ /** */
+ final PhysicalRowId[] slots = new PhysicalRowId[ELEMS_PER_PAGE];
+
+
+ /**
+ * Constructs a data page view from the indicated block.
+ */
+ FreeLogicalRowIdPage( BlockIo blockIo )
+ {
+ super( blockIo );
+ }
+
+
+ /**
+ * Factory method to create or return a data page for the indicated block.
+ */
+ static FreeLogicalRowIdPage getFreeLogicalRowIdPageView( BlockIo blockIo )
+ {
+ BlockView view = blockIo.getView();
+
+ if ( ( view != null ) && ( view instanceof FreeLogicalRowIdPage ) )
+ {
+ return ( FreeLogicalRowIdPage ) view;
+ }
+ else
+ {
+ return new FreeLogicalRowIdPage( blockIo );
+ }
+ }
+
+
+ /**
+ * @return the number of free rowids
+ */
+ short getCount()
+ {
+ return blockIo.readShort( O_COUNT );
+ }
+
+
+ /**
+ * Sets the number of free rowids
+ */
+ private void setCount( short i )
+ {
+ blockIo.writeShort( O_COUNT, i );
+ }
+
+
+ /**
+ * Frees a slot
+ */
+ void free( int slot )
+ {
+ get( slot ).setBlock( 0 );
+ setCount( (short) ( getCount() - 1 ) );
+ }
+
+
+ /**
+ * Allocates a slot
+ */
+ PhysicalRowId alloc( int slot )
+ {
+ setCount( (short) ( getCount() + 1 ) );
+ get( slot ).setBlock( -1 );
+
+ return get( slot );
+ }
+
+
+ /**
+ * Returns true if a slot is allocated
+ */
+ private boolean isAllocated( int slot )
+ {
+ return get( slot ).getBlock() > 0;
+ }
+
+
+ /**
+ * Returns true if a slot is free
+ */
+ private boolean isFree( int slot )
+ {
+ return !isAllocated(slot);
+ }
+
+
+ /**
+ * Returns the value of the indicated slot
+ */
+ PhysicalRowId get( int slot )
+ {
+ if ( slots[slot] == null )
+ {
+ slots[slot] = new PhysicalRowId( blockIo, slotToOffset( slot ) );
+ }
+
+ return slots[slot];
+ }
+
+
+ /**
+ * Converts slot to offset
+ */
+ private short slotToOffset( int slot )
+ {
+ return (short) ( O_FREE + ( slot * PhysicalRowId.SIZE ) );
+ }
+
+
+ /**
+ * Returns first free slot, -1 if no slots are available
+ */
+ int getFirstFree()
+ {
+ for ( int i = 0; i < ELEMS_PER_PAGE; i++ )
+ {
+ if ( isFree( i ) )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+ /**
+ * @return The first allocated slot, -1 if no slots are available.
+ */
+ int getFirstAllocated()
+ {
+ for ( int i = 0; i < ELEMS_PER_PAGE; i++ )
+ {
+ if ( isAllocated( i ) )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "FreeLogRowIdPage ( " );
+
+ // The blockIO
+ sb.append( super.toString() ).append( ", " );
+
+ // The first rowId
+ sb.append( "count: " ).append( getCount() );
+
+ // Dump the Physical row id
+ for ( int i = 0; i < ELEMS_PER_PAGE; i++ )
+ {
+ if ( slots[i] != null )
+ {
+ sb.append( ", [" ).append( i ).append( "]=<" ).append( slots[i].getBlock() ).append( ", " ).append( slots[i].getOffset() ).append( ">" );
+ }
+ }
+
+ sb.append( ")" );
+
+ return sb.toString();
+ }
+}
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,171 @@
+/**
+ * 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: FreeLogicalRowIdPageManager.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.IOException;
+
+/**
+ * This class manages free Logical rowid pages and provides methods
+ * to free and allocate Logical rowids on a high level.
+ */
+final class FreeLogicalRowIdPageManager
+{
+ /** our record recordFile */
+ private RecordFile recordFile;
+
+ /** our page manager */
+ private PageManager pageManager;
+
+ /**
+ * Creates a new instance using the indicated record file and
+ * page manager.
+ */
+ FreeLogicalRowIdPageManager( PageManager pageManager) throws IOException
+ {
+ this.pageManager = pageManager;
+ this.recordFile = pageManager.getRecordFile();
+ }
+
+
+ /**
+ * Returns a free Logical rowid, or null if nothing was found.
+ */
+ Location get() throws IOException
+ {
+ // Loop through the free Logical rowid list until we find
+ // the first rowid.
+ // Create a cursor to browse the pages
+ PageCursor cursor = new PageCursor( pageManager, Magic.FREELOGIDS_PAGE );
+
+ // Loop on the pages now
+ while ( cursor.next() != 0 )
+ {
+ // Get the blockIo associated with the blockId
+ BlockIo blockIo = recordFile.get( cursor.getBlockId() );
+ FreeLogicalRowIdPage fp = FreeLogicalRowIdPage.getFreeLogicalRowIdPageView( blockIo );
+
+ // Get the first allocated FreeLogicalRowId
+ int slot = fp.getFirstAllocated();
+
+ if ( slot != -1 )
+ {
+ // got one!
+ Location location = new Location( fp.get( slot ) );
+
+ // Remove the block from the page
+ fp.free( slot );
+
+ boolean hasMore = fp.getCount() != 0;
+
+ // Upate the recordFile
+ recordFile.release( cursor.getBlockId(), hasMore );
+
+ if ( !hasMore )
+ {
+ // page became empty - free it
+ pageManager.free( Magic.FREELOGIDS_PAGE, cursor.getBlockId() );
+ }
+
+ return location;
+ }
+ else
+ {
+ // no luck, go to next page
+ recordFile.release( cursor.getBlockId(), false );
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Puts the indicated rowid on the free list
+ *
+ * @param rowId The Location where we will store the rowId
+ */
+ void put( Location rowId ) throws IOException
+ {
+
+ PhysicalRowId free = null;
+
+ // Create a cursor on the FREELOGIDs list
+ PageCursor curs = new PageCursor( pageManager, Magic.FREELOGIDS_PAGE );
+ long freePage = 0;
+
+ // Loop on all the list
+ while ( curs.next() != 0 )
+ {
+ freePage = curs.getBlockId();
+ BlockIo curBlockIo = recordFile.get( freePage );
+ FreeLogicalRowIdPage fp = FreeLogicalRowIdPage.getFreeLogicalRowIdPageView( curBlockIo );
+ int slot = fp.getFirstFree();
+
+ if ( slot != -1 )
+ {
+ free = fp.alloc(slot);
+ break;
+ }
+
+ recordFile.release( curBlockIo );
+ }
+
+ if ( free == null )
+ {
+ // No more space on the free list, add a page.
+ freePage = pageManager.allocate( Magic.FREELOGIDS_PAGE );
+ BlockIo curBlockIo = recordFile.get( freePage );
+ FreeLogicalRowIdPage fp = FreeLogicalRowIdPage.getFreeLogicalRowIdPageView( curBlockIo );
+ free = fp.alloc( 0 );
+ }
+
+ free.setBlock( rowId.getBlock() );
+ free.setOffset( rowId.getOffset() );
+ recordFile.release( freePage, true );
+ }
+}
\ No newline at end of file
Added: directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreePhysicalRowId.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreePhysicalRowId.java?rev=1239581&view=auto
==============================================================================
--- directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreePhysicalRowId.java (added)
+++ directory/apacheds/trunk/jdbm2/src/main/java/jdbm/recman/FreePhysicalRowId.java Thu Feb 2 12:38:39 2012
@@ -0,0 +1,83 @@
+/**
+ * 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: FreePhysicalRowId.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+package jdbm.recman;
+
+
+/**
+ * This class extends the physical rowid with a size value to indicate
+ * the size of a free rowid on the free rowid list.
+ */
+final class FreePhysicalRowId extends PhysicalRowId
+{
+ // offsets
+ private static final short O_SIZE = PhysicalRowId.SIZE; // int size
+ static final short SIZE = O_SIZE + Magic.SZ_INT;
+
+
+ /**
+ * Constructs a physical rowid from the indicated data starting at
+ * the indicated position.
+ */
+ FreePhysicalRowId( BlockIo block, short pos )
+ {
+ super( block, pos );
+ }
+
+
+ /** Returns the size */
+ int getSize()
+ {
+ return block.readInt( pos + O_SIZE );
+ }
+
+
+ /** Sets the size */
+ void setSize( int value )
+ {
+ block.writeInt( pos + O_SIZE, value );
+ }
+}