blob: 3d6661ca204f1582c36257a68ee410e9fed7acc3 [file] [log] [blame]
/*
* Copyright 2008 Niclas Hedhman.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.qi4j.entitystore.swift;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.entitystore.map.MapEntityStore;
import org.qi4j.spi.entitystore.EntityStoreException;
public class RecordManager
implements UndoManager
{
private static final byte UNDO_DELETE = 2;
private static final byte UNDO_MODIFY = 3;
private static final byte UNDO_NEW_IDENTITY = 4;
private static final byte UNDO_DROP_IDENTITY = 5;
private static final byte UNDO_EXTEND = 6;
private DataStore dataStore;
private RandomAccessFile undoJournal;
private ArrayList<UndoCommand> commands;
public RecordManager( File dataDir, boolean recover )
throws IOException
{
File undoFile = new File( dataDir, "undo.data" );
dataStore = new DataStore( dataDir, this );
commands = new ArrayList<UndoCommand>();
if( undoFile.exists() )
{
undoJournal = new RandomAccessFile( undoFile, "rw" );
if( recover && undoJournal.length() > 0 )
{
recover();
}
}
else
{
undoFile.createNewFile();
undoJournal = new RandomAccessFile( undoFile, "rw" );
}
}
public void putData( DataBlock data )
throws IOException
{
dataStore.putData( data );
}
public void deleteData( EntityReference reference )
throws IOException
{
dataStore.delete( reference );
}
public DataBlock readData( EntityReference reference )
throws IOException
{
return dataStore.readData( reference );
}
public void commit()
throws IOException
{
dataStore.flush();
commands.clear();
undoJournal.setLength( 0 );
}
public void discard()
throws IOException
{
for( UndoCommand command : commands )
{
command.undo( dataStore.dataFile(), dataStore.identityFile() );
}
commands.clear();
undoJournal.setLength( 0 );
}
public void close()
throws IOException
{
dataStore.close();
}
public void saveUndoCommand( UndoCommand command )
{
commands.add( command );
try
{
if( command instanceof UndoDeleteCommand )
{
undoJournal.write( UNDO_DELETE );
command.save( undoJournal );
}
else if( command instanceof UndoModifyCommand )
{
undoJournal.write( UNDO_MODIFY );
command.save( undoJournal );
}
else if( command instanceof UndoDropIdentityCommand )
{
undoJournal.write( UNDO_DROP_IDENTITY );
command.save( undoJournal );
}
else if( command instanceof UndoNewIdentityCommand )
{
undoJournal.write( UNDO_NEW_IDENTITY );
command.save( undoJournal );
}
else if( command instanceof UndoExtendCommand )
{
undoJournal.write( UNDO_EXTEND );
command.save( undoJournal );
}
else
{
throw new InternalError();
}
}
catch( IOException e )
{
throw new EntityStoreException( "Undo storage medium is malfunctioning." );
}
}
private void recover()
{
try
{
undoJournal.seek( 0 );
while( undoJournal.getFilePointer() < undoJournal.length() )
{
byte type = undoJournal.readByte();
UndoCommand command;
if( type == UNDO_MODIFY )
{
command = UndoModifyCommand.load( undoJournal );
}
else if( type == UNDO_DELETE )
{
command = UndoDeleteCommand.load( undoJournal );
}
else if( type == UNDO_DROP_IDENTITY )
{
command = UndoDropIdentityCommand.load( undoJournal );
}
else if( type == UNDO_EXTEND )
{
command = UndoExtendCommand.load( undoJournal );
}
else if( type == UNDO_NEW_IDENTITY )
{
command = UndoNewIdentityCommand.load( undoJournal );
}
else
{
throw new InternalError();
}
commands.add( command );
}
discard();
}
catch( IOException e )
{
throw new EntityStoreException( "Unable to recover from previous crash." );
}
}
public <ThrowableType extends Throwable> void visitMap( MapEntityStore.MapEntityStoreVisitor<ThrowableType> visitor )
throws ThrowableType
{
dataStore.visitMap( visitor );
}
}