package org.neo4j.kernel.impl.index; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Map; import org.neo4j.commons.collection.MapUtil; public class IndexStore { private final Map<String, Map<String, String>> config; private final File file; private ByteBuffer dontUseBuffer = ByteBuffer.allocate( 100 ); public IndexStore( String graphDbStoreDir ) { this.file = new File( new File( graphDbStoreDir ), "index.db" ); this.config = read(); } private ByteBuffer buffer( int size ) { if ( dontUseBuffer.capacity() < size ) { dontUseBuffer = ByteBuffer.allocate( size*2 ); } return dontUseBuffer; } private Map<String, Map<String, String>> read() { if ( !file.exists() ) { return new HashMap<String, Map<String,String>>(); } FileChannel channel = null; try { channel = new RandomAccessFile( file, "r" ).getChannel(); Map<String, Map<String, String>> map = new HashMap<String, Map<String,String>>(); while ( true ) { String indexName = readNextString( channel ); if ( indexName == null ) { break; } Integer propertyCount = readNextInt( channel ); if ( propertyCount == null ) { break; } Map<String, String> properties = new HashMap<String, String>(); for ( int i = 0; i < propertyCount; i++ ) { String key = readNextString( channel ); if ( key == null ) { break; } String value = readNextString( channel ); if ( value == null ) { break; } properties.put( key, value ); } map.put( indexName, properties ); } return map; } catch ( IOException e ) { throw new RuntimeException( e ); } finally { close( channel ); } } private void close( FileChannel channel ) { if ( channel != null ) { try { channel.close(); } catch ( IOException e ) { e.printStackTrace(); } } } private Integer readNextInt(FileChannel channel) throws IOException { return NioUtils.readInt( channel, buffer( 4 ) ); } private String readNextString(FileChannel channel) throws IOException { return NioUtils.readLengthAndString( channel, buffer( 100 ) ); } public synchronized Map<String, String> get( String indexName ) { return this.config.get( indexName ); } public synchronized void remove( String name ) { if ( this.config.remove( name ) == null ) { throw new RuntimeException( "Index config for '" + name + "' not found" ); } } public synchronized void setIfNecessary( String name, Map<String, String> config ) { if ( this.config.containsKey( name ) ) { return; } this.config.put( name, config ); write(); } private void write() { File tmpFile = new File( this.file.getParentFile(), this.file.getName() + ".tmp" ); write( tmpFile ); this.file.delete(); tmpFile.renameTo( this.file ); } private void write( File file ) { FileChannel channel = null; try { channel = new RandomAccessFile( file, "rw" ).getChannel(); for ( Map.Entry<String, Map<String, String>> entry : config.entrySet() ) { writeString( channel, entry.getKey() ); writeInt( channel, entry.getValue().size() ); for ( Map.Entry<String, String> propertyEntry : entry.getValue().entrySet() ) { writeString( channel, propertyEntry.getKey() ); writeString( channel, propertyEntry.getValue() ); } } } catch ( IOException e ) { throw new RuntimeException( e ); } finally { close( channel ); } } private void writeInt( FileChannel channel, int value ) throws IOException { NioUtils.writeInt( channel, buffer( 4 ), value ); } private void writeString( FileChannel channel, String value ) throws IOException { NioUtils.writeLengthAndString( channel, buffer( 200 ), value ); } public static Map<String, String> getIndexConfig( String indexName, IndexStore indexStore, Map<String, String> userConfig, Map<?, ?> dbConfig ) { // 1. Check stored config Map<String, String> storedConfig = indexStore.get( indexName ); Map<String, String> configToUse = null; if ( storedConfig != null ) { if ( userConfig != null && !storedConfig.equals( userConfig ) ) { throw new IllegalArgumentException( "User index configuration:\n" + userConfig + "\ndiffer from stored config:\n" + storedConfig + "\nfor '" + indexName + "'" ); } configToUse = storedConfig; } // 2. Check custom config if ( configToUse == null && userConfig != null ) { configToUse = userConfig; } // 3. Check db config if ( configToUse == null ) { String provider = (String) dbConfig.get( "index." + indexName ); if ( provider == null ) { provider = (String) dbConfig.get( "index" ); } // 4. Default to lucene if ( provider == null ) { provider = "org.neo4j.index.lucene.LuceneIndexProvider"; } configToUse = MapUtil.<String, String>genericOf( "provider", provider ); } indexStore.setIfNecessary( indexName, configToUse ); return configToUse; } }