package eu.europeana.cloud.service.uis.persistent.dao;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import com.datastax.driver.core.exceptions.QueryExecutionException;
import eu.europeana.cloud.cassandra.CassandraConnectionProvider;
import eu.europeana.cloud.common.model.DataProvider;
import eu.europeana.cloud.common.model.DataProviderProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Data provider repository using Cassandra nosql database.
*/
@Repository
public class CassandraDataProviderDAO {
private CassandraConnectionProvider dbService;
private PreparedStatement createDataProviderStatement;
private PreparedStatement updateDataProviderStatement;
private PreparedStatement getProviderStatement;
private PreparedStatement deleteProviderStatement;
private PreparedStatement getAllProvidersStatement;
private static final Logger LOGGER = LoggerFactory.getLogger(CassandraDataProviderDAO.class);
/**
*
* Creates a new instance of this class.
* @param dbService Connector to Cassandra cluster
*/
public CassandraDataProviderDAO(CassandraConnectionProvider dbService){
this.dbService = dbService;
prepareStatements();
}
private void prepareStatements() {
createDataProviderStatement = dbService.getSession().prepare(
"INSERT INTO data_providers(provider_id, active, properties, creation_date, partition_key) VALUES (?,true,?,?,?);");
createDataProviderStatement.setConsistencyLevel(dbService.getConsistencyLevel());
updateDataProviderStatement = dbService.getSession().prepare(
"UPDATE data_providers SET active=?, properties=? where provider_id = ?;");
updateDataProviderStatement.setConsistencyLevel(dbService.getConsistencyLevel());
getProviderStatement = dbService.getSession().prepare(
"SELECT provider_id, partition_key, active, properties FROM data_providers WHERE provider_id = ?;");
getProviderStatement.setConsistencyLevel(dbService.getConsistencyLevel());
deleteProviderStatement = dbService.getSession().prepare(
"DELETE FROM data_providers WHERE provider_id = ?;");
deleteProviderStatement.setConsistencyLevel(dbService.getConsistencyLevel());
getAllProvidersStatement = dbService.getSession().prepare(
"SELECT provider_id, active, partition_key, properties FROM data_providers WHERE token(provider_id) >= token(?) LIMIT ?;");
getAllProvidersStatement.setConsistencyLevel(dbService.getConsistencyLevel());
}
/**
* Returns a sublist of providers, starting from specified provider id.
*
* @param thresholdProviderId
* first id of provider. If null, will return beginning of the list of all providers.
* @param limit
* max size of returned list.
* @return a sublist of all providers.
* @throws NoHostAvailableException
* @throws QueryExecutionException
*/
public List<DataProvider> getProviders(String thresholdProviderId, int limit)
throws NoHostAvailableException, QueryExecutionException {
String provId = thresholdProviderId;
if (provId == null) {
provId = "";
}
BoundStatement boundStatement = getAllProvidersStatement.bind(provId, limit);
ResultSet rs = dbService.getSession().execute(boundStatement);
List<DataProvider> dataProviders = new ArrayList<>();
for (Row row : rs) {
dataProviders.add(map(row));
}
return dataProviders;
}
/**
* Returns data provider with specified id.
*
* @param providerId
* id of provider.
* @return data provider
* @throws NoHostAvailableException
* @throws QueryExecutionException
*/
public DataProvider getProvider(String providerId)
throws NoHostAvailableException, QueryExecutionException {
BoundStatement boundStatement = getProviderStatement.bind(providerId);
ResultSet rs = dbService.getSession().execute(boundStatement); //NOSONAR
Row result = rs.one();
if (result == null) {
return null;
} else {
return map(result);
}
}
/**
* Deletes provider with specified id.
*
* @param providerId
* id of provider.
* @throws NoHostAvailableException
* @throws QueryExecutionException
*/
public void deleteProvider(String providerId)
throws NoHostAvailableException, QueryExecutionException {
BoundStatement boundStatement = deleteProviderStatement.bind(providerId);
dbService.getSession().execute(boundStatement);
}
/**
*
* Creates new data-provider with specified id and properties. Newly created provider is 'active' by default.
*
* @param providerId provider id
* @param properties administrative properties of data provider
* @return created data provider object
* @throws NoHostAvailableException
* @throws QueryExecutionException
*/
public DataProvider createDataProvider(String providerId, DataProviderProperties properties)
throws NoHostAvailableException, QueryExecutionException {
int partitionKey = providerId.hashCode();
BoundStatement boundStatement = createDataProviderStatement.bind(providerId, propertiesToMap(properties), new Date(), partitionKey);
dbService.getSession().execute(boundStatement);
DataProvider dp = new DataProvider();
dp.setId(providerId);
dp.setPartitionKey(partitionKey);
dp.setProperties(properties);
return dp;
}
/**
*
* Updates data provider in DB (properties and 'active' flag)
*
* @param dataProvider data provider object
* @return updated data provider
* @throws NoHostAvailableException
* @throws QueryExecutionException
*/
public DataProvider updateDataProvider(DataProvider dataProvider) throws NoHostAvailableException, QueryExecutionException {
BoundStatement boundStatement = updateDataProviderStatement.bind(dataProvider.isActive(), propertiesToMap(dataProvider.getProperties()),dataProvider.getId());
dbService.getSession().execute(boundStatement);
return dataProvider;
}
private DataProvider map(Row row) {
DataProvider provider = new DataProvider();
String providerId = row.getString("provider_id");
int partitionKey = row.getInt("partition_key");
Map<String, String> propertiesMap = row.getMap("properties", String.class, String.class);
DataProviderProperties properties = mapToProperties(propertiesMap);
provider.setId(providerId);
provider.setPartitionKey(partitionKey);
provider.setProperties(properties);
provider.setActive(row.getBool("active"));
return provider;
}
private Map<String, String> propertiesToMap(DataProviderProperties properties) {
Map<String, String> map = new HashMap<>();
Method[] methods = DataProviderProperties.class.getDeclaredMethods();
for (Method m : methods) {
if (m.getName().startsWith("get")) {
Object value;
try {
value = m.invoke(properties);
if (value != null) {
map.put(m.getName().substring(3), value.toString());
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
LOGGER.error(ex.getMessage());
}
}
}
return map;
}
private DataProviderProperties mapToProperties(Map<String, String> map) {
DataProviderProperties properties = new DataProviderProperties();
Method[] methods = DataProviderProperties.class.getDeclaredMethods();
for (Method m : methods) {
if (m.getName().startsWith("set")) {
String propName = m.getName().substring(3);
String propValue = map.get(propName);
if (propValue != null) {
try {
m.invoke(properties, propValue);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
LOGGER.error(ex.getMessage());
}
}
}
}
return properties;
}
}