package eu.europeana.cloud.service.mcs.persistent;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.io.BaseEncoding;
import eu.europeana.cloud.common.model.*;
import eu.europeana.cloud.common.response.CloudTagsResponse;
import eu.europeana.cloud.common.response.CloudVersionRevisionResponse;
import eu.europeana.cloud.common.response.ResultSlice;
import eu.europeana.cloud.common.utils.RevisionUtils;
import eu.europeana.cloud.service.mcs.DataSetService;
import eu.europeana.cloud.service.mcs.UISClientHandler;
import eu.europeana.cloud.service.mcs.exception.DataSetAlreadyExistsException;
import eu.europeana.cloud.service.mcs.exception.DataSetNotExistsException;
import eu.europeana.cloud.service.mcs.exception.ProviderNotExistsException;
import eu.europeana.cloud.service.mcs.exception.RepresentationNotExistsException;
import eu.europeana.cloud.service.mcs.persistent.cassandra.CassandraDataSetDAO;
import eu.europeana.cloud.service.mcs.persistent.cassandra.CassandraRecordDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.nio.charset.Charset;
import java.util.*;
/**
* Implementation of data set service using Cassandra database.
*/
@Service
public class CassandraDataSetService implements DataSetService {
@Autowired
private CassandraDataSetDAO dataSetDAO;
@Autowired
private CassandraRecordDAO recordDAO;
@Autowired
private SolrRepresentationIndexer representationIndexer;
@Autowired
private UISClientHandler uis;
/**
* @inheritDoc
*/
@Override
public ResultSlice<Representation> listDataSet(String providerId, String dataSetId, String thresholdParam, int limit)
throws DataSetNotExistsException {
DataSet ds = dataSetDAO.getDataSet(providerId, dataSetId);
if (ds == null) {
throw new DataSetNotExistsException();
}
// now - decode parameters encoded in thresholdParam
String thresholdCloudId = null;
String thresholdSchemaId = null;
if (thresholdParam != null) {
List<String> thresholdCloudIdAndSchema = decodeParams(thresholdParam);
if (thresholdCloudIdAndSchema.size() != 2) {
throw new IllegalArgumentException("Wrong threshold param!");
}
thresholdCloudId = thresholdCloudIdAndSchema.get(0);
thresholdSchemaId = thresholdCloudIdAndSchema.get(1);
}
// get representation stubs from data set
List<Representation> representationStubs = dataSetDAO.listDataSet(providerId, dataSetId, thresholdCloudId,
thresholdSchemaId, limit + 1);
// if this is not last slice of result - add reference to next one by
// encoding parameters in thresholdParam
String nextResultToken = null;
if (representationStubs.size() == limit + 1) {
Representation nextResult = representationStubs.get(limit);
nextResultToken = encodeParams(nextResult.getCloudId(), nextResult.getRepresentationName());
representationStubs.remove(limit);
}
// replace representation stubs with real representations
List<Representation> representations = new ArrayList<>(representationStubs.size());
for (Representation stub : representationStubs) {
if (stub.getVersion() == null) {
representations.add(recordDAO.getLatestPersistentRepresentation(stub.getCloudId(),
stub.getRepresentationName()));
} else {
representations.add(recordDAO.getRepresentation(stub.getCloudId(), stub.getRepresentationName(),
stub.getVersion()));
}
}
return new ResultSlice<Representation>(nextResultToken, representations);
}
/**
* @inheritDoc
*/
@Override
public void addAssignment(String providerId, String dataSetId,
String recordId, String schema, String version)
throws DataSetNotExistsException, RepresentationNotExistsException {
isDataSetExists(providerId, dataSetId);
Representation rep = getRepresentationIfExist(recordId, schema, version);
// now - when everything is validated - add assignment
dataSetDAO.addAssignment(providerId, dataSetId, recordId, schema,
rep.getVersion());
DataProvider dataProvider = uis.getProvider(providerId);
dataSetDAO.addDataSetsRepresentationName(providerId, dataSetId, schema);
representationIndexer.addAssignment(rep.getVersion(),
new CompoundDataSetId(providerId, dataSetId),
dataProvider.getPartitionKey());
Map<String, Revision> latestRevisions = new HashMap<>();
for (Revision revision : rep.getRevisions()) {
String revisionKey = revision.getRevisionName() + "_" + revision.getRevisionProviderId();
Revision currentRevision = latestRevisions.get(revisionKey);
if (currentRevision == null || revision.getCreationTimeStamp().getTime() > currentRevision.getCreationTimeStamp().getTime()) {
latestRevisions.put(revisionKey, revision);
}
dataSetDAO.addDataSetsRevision(providerId, dataSetId, revision,
schema, recordId);
dataSetDAO.insertProviderDatasetRepresentationInfo(dataSetId, providerId, recordId, rep.getVersion(), schema,
RevisionUtils.getRevisionKey(revision), revision.getCreationTimeStamp(),
revision.isAcceptance(), revision.isPublished(), revision.isDeleted());
dataSetDAO.addLatestRevisionForDatasetAssignment(dataSetDAO.getDataSet(providerId, dataSetId), rep, revision);
}
updateLatestProviderDatasetRevisions(providerId, dataSetId, recordId, schema, version, latestRevisions);
}
private void updateLatestProviderDatasetRevisions(String providerId, String dataSetId, String recordId, String schema, String version, Map<String, Revision> latestRevisions) {
if (!latestRevisions.isEmpty()) {
for (String revisionKey : latestRevisions.keySet()) {
Revision latestRevision = latestRevisions.get(revisionKey);
Date latestStoredRevisionTimestamp = dataSetDAO.getLatestRevisionTimeStamp(dataSetId, providerId, schema, latestRevision.getRevisionName(), latestRevision.getRevisionProviderId(), recordId);
if (latestStoredRevisionTimestamp == null || latestStoredRevisionTimestamp.getTime() < latestRevision.getCreationTimeStamp().getTime()) {
dataSetDAO.insertLatestProviderDatasetRepresentationInfo(dataSetId, providerId,
recordId, schema, latestRevision.getRevisionName(), latestRevision.getRevisionProviderId(), latestRevision.getCreationTimeStamp(), version,
latestRevision.isAcceptance(), latestRevision.isPublished(), latestRevision.isDeleted());
}
}
}
}
private Representation getRepresentationIfExist(String recordId, String schema, String version) throws RepresentationNotExistsException {
Representation rep;
if (version == null) {
rep = recordDAO.getLatestPersistentRepresentation(recordId, schema);
if (rep == null) {
throw new RepresentationNotExistsException();
}
} else {
rep = recordDAO.getRepresentation(recordId, schema, version);
if (rep == null) {
throw new RepresentationNotExistsException();
}
}
return rep;
}
/**
* @inheritDoc
*/
@Override
public void removeAssignment(String providerId, String dataSetId,
String recordId, String schema, String versionId) throws DataSetNotExistsException {
isDataSetExists(providerId, dataSetId);
dataSetDAO.removeAssignment(providerId, dataSetId, recordId, schema, versionId);
DataProvider dataProvider = uis.getProvider(providerId);
if (!dataSetDAO.hasMoreRepresentations(providerId, dataSetId, schema)) {
dataSetDAO.removeRepresentationNameForDataSet(schema, providerId, dataSetId);
}
Representation representation = recordDAO.getRepresentation(recordId, schema, versionId);
representationIndexer.removeAssignment(recordId, schema,
new CompoundDataSetId(providerId, dataSetId),
dataProvider.getPartitionKey());
Set<String> deletedRevisions = new HashSet<>();
if (representation != null) {
for (Revision revision : representation.getRevisions()) {
String revisionName = revision.getRevisionName();
String revisionProvider = revision.getRevisionProviderId();
String revisionId = revisionName + "_" + revisionProvider;
if (!deletedRevisions.contains(revisionId)) {
dataSetDAO.deleteLatestProviderDatasetRepresentationInfo(dataSetId, providerId,
recordId, schema, revisionName, revisionProvider);
deletedRevisions.add(revisionId);
}
dataSetDAO.removeDataSetsRevision(providerId, dataSetId, revision, schema, recordId);
dataSetDAO.deleteProviderDatasetRepresentationInfo(dataSetId, providerId, recordId, schema, revision.getCreationTimeStamp());
DataSet ds = dataSetDAO.getDataSet(providerId, dataSetId);
dataSetDAO.removeLatestRevisionForDatasetAssignment(ds, representation, revision);
}
}
//In here to update the latest provider revisions table
if (!deletedRevisions.isEmpty()) {
}
}
/**
* >>>>>>> develop
*
* @inheritDoc
*/
@Override
public DataSet createDataSet(String providerId, String dataSetId,
String description) throws ProviderNotExistsException,
DataSetAlreadyExistsException {
Date now = new Date();
if (uis.getProvider(providerId) == null) {
throw new ProviderNotExistsException();
}
// check if dataset exists
DataSet ds = dataSetDAO.getDataSet(providerId, dataSetId);
if (ds != null) {
throw new DataSetAlreadyExistsException();
}
return dataSetDAO
.createDataSet(providerId, dataSetId, description, now);
}
/**
* @inheritDoc
*/
@Override
public DataSet updateDataSet(String providerId, String dataSetId,
String description) throws DataSetNotExistsException {
Date now = new Date();
// check if dataset exists
DataSet ds = dataSetDAO.getDataSet(providerId, dataSetId);
if (ds == null) {
throw new DataSetNotExistsException("Provider " + providerId
+ " does not have data set with id " + dataSetId);
}
return dataSetDAO
.createDataSet(providerId, dataSetId, description, now);
}
/**
* @inheritDoc
*/
@Override
public ResultSlice<DataSet> getDataSets(String providerId,
String thresholdDatasetId, int limit) {
List<DataSet> dataSets = dataSetDAO.getDataSets(providerId,
thresholdDatasetId, limit + 1);
String nextDataSet = null;
if (dataSets.size() == limit + 1) {
DataSet nextResult = dataSets.get(limit);
nextDataSet = nextResult.getId();
dataSets.remove(limit);
}
return new ResultSlice<DataSet>(nextDataSet, dataSets);
}
@Override
public Map<String, Set<String>> getDataSets(String cloudId, String representationName, String version) {
return dataSetDAO.getDataSets(cloudId, representationName, version);
}
@Override
public ResultSlice<CloudTagsResponse> getDataSetsRevisions(String providerId, String dataSetId, String revisionProviderId, String revisionName, Date revisionTimestamp, String representationName, String startFrom, int limit)
throws ProviderNotExistsException, DataSetNotExistsException {
// check whether provider exists
if (!uis.existsProvider(providerId))
throw new ProviderNotExistsException("Provider doesn't exist " + providerId);
// check whether data set exists
if (dataSetDAO.getDataSet(providerId, dataSetId) == null)
throw new DataSetNotExistsException("Data set " + dataSetId + " doesn't exist for provider " + providerId);
// run the query requesting one more element than items per page to determine the starting cloud id for the next slice
List<Properties> list = dataSetDAO.getDataSetsRevisions(providerId, dataSetId, revisionProviderId, revisionName, revisionTimestamp, representationName, startFrom, limit);
String nextToken = null;
// when the list size is one element bigger than requested it means there is going to be next slice
if (list.size() == limit + 1) {
// set token to the last from list
nextToken = list.get(limit).getProperty("nextSlice");
// remove last element of the list
list.remove(limit);
}
return new ResultSlice<>(nextToken, prepareCloudTagsResponseList(list));
}
private List<CloudTagsResponse> prepareCloudTagsResponseList(List<Properties> list) {
List<CloudTagsResponse> result = new ArrayList<>(list.size());
for (Properties properties : list) {
result.add(new CloudTagsResponse(properties.getProperty("cloudId"),
Boolean.valueOf(properties.getProperty("published")), Boolean.valueOf(properties.getProperty("deleted")), Boolean.valueOf(properties.getProperty("acceptance"))));
}
return result;
}
/**
* @inheritDoc
*/
@Override
public void addDataSetsRevisions(String providerId, String dataSetId, Revision revision,
String representationName, String cloudId)
throws ProviderNotExistsException {
if (uis.getProvider(providerId) == null) {
throw new ProviderNotExistsException();
}
dataSetDAO.addDataSetsRevision(providerId, dataSetId, revision, representationName, cloudId);
}
@Override
public ResultSlice<CloudVersionRevisionResponse> getDataSetCloudIdsByRepresentationPublished(String
dataSetId, String providerId, String representationName, Date dateFrom, String startFrom,
int numberOfElementsPerPage)
throws ProviderNotExistsException, DataSetNotExistsException {
// check whether provider and data set exist
validateRequest(dataSetId, providerId);
// run the query requesting one more element than items per page to determine the starting cloud id for the next slice
List<Properties> list = dataSetDAO.getDataSetCloudIdsByRepresentationPublished(providerId, dataSetId, representationName, dateFrom, startFrom, numberOfElementsPerPage);
String nextToken = null;
// when the list size is one element bigger than requested it means there is going to be next slice
if (list.size() == numberOfElementsPerPage + 1) {
// set token to the last from list
nextToken = list.get(numberOfElementsPerPage).getProperty("nextSlice");
// remove last element of the list
list.remove(numberOfElementsPerPage);
}
return new ResultSlice<>(nextToken, prepareResponseList(list));
}
@Override
public void updateAllRevisionDatasetsEntries(String globalId, String schema, String
version, Revision revision)
throws RepresentationNotExistsException {
Representation rep = recordDAO.getRepresentation(globalId, schema, version);
if (rep == null)
throw new RepresentationNotExistsException(schema);
// collect data sets the version is assigned to
Collection<CompoundDataSetId> dataSets = dataSetDAO.getDataSetAssignments(globalId, schema, version);
// now we have to insert rows for each data set
for (CompoundDataSetId dsID : dataSets) {
String datasetName = dsID.getDataSetId();
String datasetProvider = dsID.getDataSetProviderId();
String revisionId = RevisionUtils.getRevisionKey(revision);
dataSetDAO.insertProviderDatasetRepresentationInfo(datasetName, datasetProvider,
globalId, version, schema, revisionId, revision.getCreationTimeStamp(),
revision.isAcceptance(), revision.isPublished(), revision.isDeleted());
dataSetDAO.insertLatestProviderDatasetRepresentationInfo(datasetName, datasetProvider,
globalId, schema, revision.getRevisionName(), revision.getRevisionProviderId(), revision.getCreationTimeStamp(), version,
revision.isAcceptance(), revision.isPublished(), revision.isDeleted());
dataSetDAO.addDataSetsRevision(datasetProvider, datasetName, revision, schema, globalId);
dataSetDAO.addLatestRevisionForDatasetAssignment(dataSetDAO.getDataSet(datasetProvider, datasetName), rep, revision);
}
}
@Override
public void deleteDataSet(String providerId, String dataSetId)
throws DataSetNotExistsException {
DataSet ds = dataSetDAO.getDataSet(providerId, dataSetId);
if (ds == null) {
throw new DataSetNotExistsException();
}
dataSetDAO.deleteDataSet(providerId, dataSetId);
DataProvider dataProvider = uis.getProvider(providerId);
dataSetDAO.removeAllRepresentationsNamesForDataSet(providerId, dataSetId);
representationIndexer.removeAssignmentsFromDataSet(
new CompoundDataSetId(providerId, dataSetId),
dataProvider.getPartitionKey());
}
@Override
public Set<String> getAllDataSetRepresentationsNames(String providerId, String dataSetId) throws
ProviderNotExistsException, DataSetNotExistsException {
if (isProviderExists(providerId) && isDataSetExists(providerId, dataSetId)) {
return dataSetDAO.getAllRepresentationsNamesForDataSet(providerId, dataSetId);
}
return Collections.emptySet();
}
private List<CloudVersionRevisionResponse> prepareResponseList(List<Properties> list) {
List<CloudVersionRevisionResponse> result = new ArrayList<>(list.size());
for (Properties properties : list) {
result.add(new CloudVersionRevisionResponse(properties.getProperty("cloudId"),
properties.getProperty("versionId"),
properties.getProperty("revisionId"),
(Boolean) properties.get("published"),
(Boolean) properties.get("deleted"),
(Boolean) properties.get("acceptance")));
}
return result;
}
@Override
public void updateProviderDatasetRepresentation(String globalId, String schema, String version, Revision revision)
throws RepresentationNotExistsException {
// check whether representation exists
Representation rep = recordDAO.getRepresentation(globalId, schema, version);
if (rep == null)
throw new RepresentationNotExistsException(schema);
// collect data sets the version is assigned to
Collection<CompoundDataSetId> dataSets = dataSetDAO.getDataSetAssignments(globalId, schema, version);
// now we have to insert rows for each data set
for (CompoundDataSetId dsID : dataSets) {
dataSetDAO.insertProviderDatasetRepresentationInfo(dsID.getDataSetId(), dsID.getDataSetProviderId(),
globalId, version, schema, RevisionUtils.getRevisionKey(revision), revision.getCreationTimeStamp(),
revision.isAcceptance(), revision.isPublished(), revision.isDeleted());
}
}
@Override
public String getLatestVersionForGivenRevision(String dataSetId, String providerId, String cloudId, String
representationName, String revisionName, String revisionProviderId) throws DataSetNotExistsException {
if (isDataSetExists(providerId, dataSetId)) {
DataSet dataset = new DataSet();
dataset.setProviderId(providerId);
dataset.setId(dataSetId);
//
Representation rep = new Representation();
rep.setCloudId(cloudId);
rep.setRepresentationName(representationName);
//
Revision revision = new Revision();
revision.setRevisionName(revisionName);
revision.setRevisionProviderId(revisionProviderId);
DataSetRepresentationForLatestRevision result = dataSetDAO.getRepresentationForLatestRevisionFromDataset(dataset, rep, revision);
if (result != null) {
return result.getRepresentation().getVersion();
}
}
return null;
}
@Override
public void addLatestRevisionForGivenVersionInDataset(DataSet dataSet, Representation representation, Revision
revision) {
dataSetDAO.addLatestRevisionForDatasetAssignment(dataSet, representation, revision);
}
private boolean isProviderExists(String providerId) throws ProviderNotExistsException {
if (!uis.existsProvider(providerId))
throw new ProviderNotExistsException();
return true;
}
private boolean isDataSetExists(String providerId, String dataSetId) throws DataSetNotExistsException {
DataSet ds = dataSetDAO.getDataSet(providerId, dataSetId);
if (ds == null) {
throw new DataSetNotExistsException();
}
return true;
}
private String encodeParams(String... parts) {
byte[] paramsJoined = Joiner.on('\n').join(parts)
.getBytes(Charset.forName("UTF-8"));
return BaseEncoding.base32().encode(paramsJoined);
}
private List<String> decodeParams(String encodedParams) {
byte[] paramsDecoded = BaseEncoding.base32().decode(encodedParams);
String paramsDecodedString = new String(paramsDecoded,
Charset.forName("UTF-8"));
return Splitter.on('\n').splitToList(paramsDecodedString);
}
/**
* get a list of the latest cloud identifier,revision timestamp that belong to data set of a specified provider for a specific representation and revision.
* This list will contain one row per revision per cloudId;
*
* @param dataSetId data set identifier
* @param providerId provider identifier
* @param revisionName revision name
* @param revisionProvider revision provider
* @param representationName representation name
* @param startFrom cloudId to start from
* @param isDeleted revision marked-deleted
* @param numberOfElementsPerPage number of elements in a slice
* @return slice of the latest cloud identifier,revision timestamp that belong to data set of a specified provider for a specific representation and revision.
* This list will contain one row per revision per cloudId ;
* @throws ProviderNotExistsException
* @throws DataSetNotExistsException
*/
@Override
public ResultSlice<CloudIdAndTimestampResponse> getLatestDataSetCloudIdByRepresentationAndRevision(String dataSetId, String providerId, String revisionName, String revisionProvider, String representationName, String startFrom, Boolean isDeleted, int numberOfElementsPerPage)
throws ProviderNotExistsException, DataSetNotExistsException {
validateRequest(dataSetId, providerId);
List<CloudIdAndTimestampResponse> list = dataSetDAO.getLatestDataSetCloudIdByRepresentationAndRevision(providerId, dataSetId, revisionName, revisionProvider, representationName, startFrom, isDeleted, numberOfElementsPerPage);
String nextToken = null;
if (list.size() == numberOfElementsPerPage + 1) {
nextToken = list.get(numberOfElementsPerPage).getCloudId();
list.remove(numberOfElementsPerPage);
}
return new ResultSlice<>(nextToken, list);
}
private void validateRequest(String dataSetId, String providerId) throws ProviderNotExistsException, DataSetNotExistsException {
if (!uis.existsProvider(providerId))
throw new ProviderNotExistsException("Provider doesn't exist " + providerId);
if (dataSetDAO.getDataSet(providerId, dataSetId) == null)
throw new DataSetNotExistsException("Data set " + dataSetId + " doesn't exist for provider " + providerId);
}
}