/*
* Copyright (c) 2002-2009 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.nioneo.store;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Implementation of the relationship store.
*/
public class RelationshipStore extends AbstractStore implements Store
{
// relationship store version, each rel store ends with this
// string (byte encoded)
private static final String VERSION = "RelationshipStore v0.9.5";
// record header size
// directed|in_use(byte)+first_node(int)+second_node(int)+rel_type(int)+
// first_prev_rel_id(int)+first_next_rel_id+second_prev_rel_id(int)+
// second_next_rel_id+next_prop_id(int)
private static final int RECORD_SIZE = 33;
/**
* See {@link AbstractStore#AbstractStore(String, Map)}
*/
public RelationshipStore( String fileName, Map<?,?> config )
{
super( fileName, config );
}
/**
* See {@link AbstractStore#AbstractStore(String)}
*/
public RelationshipStore( String fileName )
{
super( fileName );
}
public String getTypeAndVersionDescriptor()
{
return VERSION;
}
public int getRecordSize()
{
return RECORD_SIZE;
}
@Override
public void close()
{
super.close();
}
/**
* Creates a new relationship store contained in <CODE>fileName</CODE> If
* filename is <CODE>null</CODE> or the file already exists an <CODE>IOException</CODE>
* is thrown.
*
* @param fileName
* File name of the new relationship store
* @throws IOException
* If unable to create relationship store or name null
*/
public static void createStore( String fileName )
{
createEmptyStore( fileName, VERSION );
}
public RelationshipRecord getRecord( int id )
{
PersistenceWindow window = acquireWindow( id, OperationType.READ );
try
{
RelationshipRecord record = getRecord( id, window, false );
return record;
}
finally
{
releaseWindow( window );
}
}
public RelationshipRecord getLightRel( int id )
{
PersistenceWindow window = null;
try
{
window = acquireWindow( id, OperationType.READ );
}
catch ( InvalidRecordException e )
{
// ok to high id
return null;
}
try
{
RelationshipRecord record = getRecord( id, window, true );
return record;
}
finally
{
releaseWindow( window );
}
}
public void updateRecord( RelationshipRecord record, boolean recovered )
{
assert recovered;
setRecovered();
try
{
updateRecord( record );
}
finally
{
unsetRecovered();
}
}
public void updateRecord( RelationshipRecord record )
{
PersistenceWindow window = acquireWindow( record.getId(),
OperationType.WRITE );
try
{
updateRecord( record, window );
}
finally
{
releaseWindow( window );
}
}
private void updateRecord( RelationshipRecord record,
PersistenceWindow window )
{
int id = record.getId();
Buffer buffer = window.getOffsettedBuffer( id );
if ( record.inUse() )
{
byte inUse = Record.IN_USE.byteValue();
buffer.put( inUse ).putInt( record.getFirstNode() ).putInt(
record.getSecondNode() ).putInt( record.getType() ).putInt(
record.getFirstPrevRel() ).putInt( record.getFirstNextRel() )
.putInt( record.getSecondPrevRel() ).putInt(
record.getSecondNextRel() ).putInt( record.getNextProp() );
}
else
{
buffer.put( Record.NOT_IN_USE.byteValue() );
if ( !isInRecoveryMode() )
{
freeId( id );
}
}
}
private RelationshipRecord getRecord( int id, PersistenceWindow window,
boolean check )
{
Buffer buffer = window.getOffsettedBuffer( id );
byte inUse = buffer.get();
boolean inUseFlag = ((inUse & Record.IN_USE.byteValue()) ==
Record.IN_USE.byteValue());
if ( !inUseFlag )
{
if ( check )
{
return null;
}
throw new InvalidRecordException( "Record[" + id + "] not in use" );
}
RelationshipRecord record = new RelationshipRecord( id,
buffer.getInt(), buffer.getInt(), buffer.getInt() );
record.setInUse( inUseFlag );
record.setFirstPrevRel( buffer.getInt() );
record.setFirstNextRel( buffer.getInt() );
record.setSecondPrevRel( buffer.getInt() );
record.setSecondNextRel( buffer.getInt() );
record.setNextProp( buffer.getInt() );
return record;
}
private RelationshipRecord getFullRecord( int id, PersistenceWindow window )
{
Buffer buffer = window.getOffsettedBuffer( id );
byte inUse = buffer.get();
boolean inUseFlag = ((inUse & Record.IN_USE.byteValue()) ==
Record.IN_USE.byteValue());
RelationshipRecord record = new RelationshipRecord( id,
buffer.getInt(), buffer.getInt(), buffer.getInt() );
record.setInUse( inUseFlag );
record.setFirstPrevRel( buffer.getInt() );
record.setFirstNextRel( buffer.getInt() );
record.setSecondPrevRel( buffer.getInt() );
record.setSecondNextRel( buffer.getInt() );
record.setNextProp( buffer.getInt() );
return record;
}
public String toString()
{
return "RelStore";
}
@Override
protected boolean versionFound( String version )
{
if ( !version.startsWith( "RelationshipStore" ) )
{
// non clean shutdown, need to do recover with right neo
return false;
}
if ( version.equals( "RelationshipStore v0.9.3" ) )
{
rebuildIdGenerator();
closeIdGenerator();
return true;
}
throw new IllegalStoreVersionException( "Store version [" + version +
"]. Please make sure you are not running old Neo4j kernel " +
" towards a store that has been created by newer version " +
" of Neo4j." );
}
public RelationshipRecord getChainRecord( int relId )
{
PersistenceWindow window = null;
try
{
window = acquireWindow( relId, OperationType.READ );
}
catch ( InvalidRecordException e )
{
// ok to high id
return null;
}
try
{
return getFullRecord( relId, window );
}
finally
{
releaseWindow( window );
}
}
public List<WindowPoolStats> getAllWindowPoolStats()
{
List<WindowPoolStats> list = new ArrayList<WindowPoolStats>();
list.add( getWindowPoolStats() );
return list;
}
}