package com.kryptnostic.rhizome.mapstores.cassandra; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.annotation.CheckForNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.Row; import com.datastax.driver.core.Session; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; import com.kryptnostic.rhizome.configuration.cassandra.CassandraConfiguration; import com.kryptnostic.rhizome.mappers.SelfRegisteringKeyMapper; import com.kryptnostic.rhizome.mappers.SelfRegisteringValueMapper; import com.kryptnostic.rhizome.mapstores.MappingException; public class DefaultCassandraMapStore<K, V> extends BaseCassandraMapStore<K, V> { private static final Logger logger = LoggerFactory.getLogger( DefaultCassandraMapStore.class ); private static final String KEYSPACE_QUERY = "CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class':'SimpleStrategy', 'replication_factor':%d};"; private static final String TABLE_QUERY = "CREATE TABLE IF NOT EXISTS %s.%s (id text PRIMARY KEY, data blob);"; private final PreparedStatement LOAD_QUERY; private final PreparedStatement STORE_QUERY; private final PreparedStatement DELETE_QUERY; private final Select LOAD_ALL_QUERY; private final PreparedStatement DELETE_ALL_QUERY; public DefaultCassandraMapStore( String table, String mapName, SelfRegisteringKeyMapper<K> keyMapper, SelfRegisteringValueMapper<V> mapper, CassandraConfiguration config, Session globalSession ) { super( table, mapName, keyMapper, mapper, config, globalSession ); session.execute( String.format( KEYSPACE_QUERY, keyspace, Long.valueOf( replicationFactor ) ) ); session.execute( String.format( TABLE_QUERY, keyspace, table ) ); LOAD_QUERY = session.prepare( QueryBuilder .select( DEFAULT_VALUE_COLUMN_NAME ).from( keyspace, table ) .where( QueryBuilder.eq( DEFAULT_KEY_COLUMN_NAME, QueryBuilder.bindMarker() ) ) ); STORE_QUERY = session.prepare( QueryBuilder .insertInto( keyspace, table ) .value( DEFAULT_KEY_COLUMN_NAME, QueryBuilder.bindMarker() ) .value( DEFAULT_VALUE_COLUMN_NAME, QueryBuilder.bindMarker() ) ); DELETE_QUERY = session.prepare( QueryBuilder .delete().from( keyspace, table ) .where( QueryBuilder.eq( DEFAULT_KEY_COLUMN_NAME, QueryBuilder.bindMarker() ) ) ); LOAD_ALL_QUERY = QueryBuilder .select( DEFAULT_KEY_COLUMN_NAME ).from( keyspace, table ); DELETE_ALL_QUERY = session.prepare( QueryBuilder .delete().from( keyspace, table ) .where( QueryBuilder.in( DEFAULT_KEY_COLUMN_NAME, QueryBuilder.bindMarker() ) ) ); } @Override public V load( K key ) { ResultSet s; s = session.execute( LOAD_QUERY.bind( keyMapper.fromKey( key ) ) ); Row result = s.one(); return mapToValue( result ); } @Override public Iterable<K> loadAllKeys() { return PagingCassandraIterator.asIterable( session, LOAD_ALL_QUERY, this::mapToKey ); } @Override public K mapToKey( Row row ) { return keyMapper.toKey( row.getString( "id" ) ); } @Override public void store( K key, V value ) { try { session.execute( STORE_QUERY.bind( keyMapper.fromKey( key ), ByteBuffer.wrap( valueMapper.toBytes( value ) ) ) ); } catch ( MappingException e ) { logger.error( "Unable to store key {} : value {} ", key, value, e ); } } @Override public void delete( K key ) { session.execute( DELETE_QUERY.bind( keyMapper.fromKey( key ) ) ); } @Override public void deleteAll( Collection<K> keys ) { List<String> mappedKeys = new ArrayList<>( keys.size() ); for ( K key : keys ) { mappedKeys.add( keyMapper.fromKey( key ) ); } session.execute( DELETE_ALL_QUERY.bind( mappedKeys ) ); } @Override protected ResultSetFuture asyncLoad( K key ) { return session.executeAsync( LOAD_QUERY.bind( keyMapper.fromKey( key ) ) ); } @Override protected V mapToValue( @CheckForNull Row row ) { if ( row == null ) { return null; } ByteBuffer bytes = row.getBytes( "data" ); try { return valueMapper.fromBytes( bytes.array() ); } catch ( MappingException e ) { logger.error( "cant wait to kill these valuemappers", e ); } return null; } }