package eu.europeana.cloud.service.mcs.persistent; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import eu.europeana.cloud.common.model.CompoundDataSetId; import eu.europeana.cloud.common.model.Representation; import eu.europeana.cloud.common.web.ParamConstants; import eu.europeana.cloud.service.mcs.kafka.ProducerWrapper; import eu.europeana.cloud.service.mcs.messages.AddAssignmentMessage; import eu.europeana.cloud.service.mcs.messages.InsertRepresentationMessage; import eu.europeana.cloud.service.mcs.messages.InsertRepresentationPersistentMessage; import eu.europeana.cloud.service.mcs.messages.RemoveAssignmentMessage; import eu.europeana.cloud.service.mcs.messages.RemoveAssignmentsFromDataSetMessage; import eu.europeana.cloud.service.mcs.messages.RemoveRecordRepresentationsMessage; import eu.europeana.cloud.service.mcs.messages.RemoveRepresentationMessage; import eu.europeana.cloud.service.mcs.messages.RemoveRepresentationVersionMessage; import eu.europeana.cloud.service.mcs.persistent.cassandra.CassandraDataSetDAO; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import org.apache.commons.lang.SerializationUtils; import static org.apache.commons.lang.SerializationUtils.serialize; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Receives information about modifications in record representations and their * data set assignments and sends them to index. Uses asynchronous queue to * communicate with Data Lookup Service which is responsible for Solr index * updates. */ @Component public class SolrRepresentationIndexer { private static final Logger LOGGER = LoggerFactory .getLogger(SolrRepresentationIndexer.class); @Autowired private CassandraDataSetDAO cassandraDataSetDAO; @Autowired(required = true) private ProducerWrapper producerWrapper; private final Gson gson = new GsonBuilder().setDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZZ").create(); /** * Indexes representation version (new or updated). If inserted * representation version is persistent, there there might be some data set * assignment changes involved (in cases where representation in latest * persistent version is assigned to data set) - in this case, such * reassignment is also done. * * @param representation * representation version. * @param partitionKey * using to route message to different partitions. */ public void insertRepresentation(Representation representation, int partitionKey) { if (!representation.isPersistent()) { producerWrapper .send(partitionKey, serialize(new InsertRepresentationMessage( prepareInsertRepresentationMessage(representation)))); } else { Collection<CompoundDataSetId> dataSetIds = cassandraDataSetDAO .getDataSetAssignments(representation.getCloudId(), representation.getRepresentationName(), null); producerWrapper.send( partitionKey, serialize(new InsertRepresentationPersistentMessage( prepareInsertPersistentRepresentationMessage( representation, dataSetIds)))); } } private String prepareInsertRepresentationMessage( Representation representation) { return gson.toJson(representation); } private String prepareInsertPersistentRepresentationMessage(Representation representation, Collection<CompoundDataSetId> dataSetIds) { HashMap<String, Object> map = new LinkedHashMap<>(); map.put(ParamConstants.F_REPRESENTATION, representation); map.put(ParamConstants.F_DATASETS, dataSetIds); return gson.toJson(map); } /** * Removes representation version from index. This method should be invoked * only for temporary representation versions (although no verification is * made in this method) - because no dataset reassignment is performed. * * * @param versionId * representation version id * * @param partitionKey * using to route message to different partitions. */ public void removeRepresentationVersion(String versionId, int partitionKey) { producerWrapper.send(partitionKey, serialize(new RemoveRepresentationVersionMessage(versionId))); } /** * Removes all representation's versions from index. * * @param cloudId * record id * @param representationName * representation's schema * @param partitionKey * using to route message to different partitions. */ public void removeRepresentation(String cloudId, String representationName, int partitionKey) { producerWrapper.send( partitionKey, serialize(new RemoveRepresentationMessage( prepareRemoveRepresentationMsg(cloudId, representationName)))); } private String prepareRemoveRepresentationMsg(String cloudId, String representationName) { JsonObject jo = new JsonObject(); jo.addProperty(ParamConstants.P_CLOUDID, cloudId); jo.addProperty(ParamConstants.P_REPRESENTATIONNAME, representationName); return jo.toString(); } /** * Removes all record's representations with all their versions from index. * * @param cloudId * record id * @param partitionKey * using to route message to different partitions. */ public void removeRecordRepresentations(String cloudId, int partitionKey) { producerWrapper.send(partitionKey, serialize(new RemoveRecordRepresentationsMessage(cloudId))); } /** * Removes assignment between data set and representation (regardless its * version). * * @param cloudId * record id * @param representationName * representation's schema * @param dataSetId * dataset id with owner's (provider's) id. * @param partitionKey * using to route message to different partitions. */ public void removeAssignment(String cloudId, String representationName, CompoundDataSetId dataSetId, int partitionKey) { producerWrapper.send( partitionKey, serialize(new RemoveAssignmentMessage( prepareRemoveAssignmentMessage(cloudId, representationName, dataSetId)))); } private String prepareRemoveAssignmentMessage(String cloudId, String representationName, CompoundDataSetId dataSetId) { JsonObject jo = prepareCompoundDataSetIdJson(dataSetId); jo.addProperty(ParamConstants.P_CLOUDID, cloudId); jo.addProperty(ParamConstants.P_REPRESENTATIONNAME, representationName); return jo.toString(); } /** * Adds assignment between data set and a representation version. * * @param versionId * representation version id. * @param dataSetId * dataset id with owner's (provider's) id. * @param partitionKey * using to route message to different partitions. */ public void addAssignment(String versionId, CompoundDataSetId dataSetId, int partitionKey) { producerWrapper.send(partitionKey, serialize(new AddAssignmentMessage( prepareAddAssignmentMsg(versionId, dataSetId)))); } private String prepareAddAssignmentMsg(String versionId, CompoundDataSetId dataSetId) { JsonObject jo = prepareCompoundDataSetIdJson(dataSetId); jo.addProperty(ParamConstants.P_VER, versionId); return jo.toString(); } /** * Removes data set assignments from ALL representation. This method is to * be used if whole data set is removed. * * @param compoundDataSetId * dataset id with owner's (provider's) id. * @param partitionKey * using to route message to different partitions. */ public void removeAssignmentsFromDataSet( CompoundDataSetId compoundDataSetId, int partitionKey) { producerWrapper.send( partitionKey, serialize(new RemoveAssignmentsFromDataSetMessage(gson .toJson(compoundDataSetId)))); } private JsonObject prepareCompoundDataSetIdJson(CompoundDataSetId dataSetId) { JsonElement elem = gson.toJsonTree(dataSetId, CompoundDataSetId.class); JsonObject jo = new JsonObject(); jo.add(ParamConstants.F_DATASET_PROVIDER_ID, elem); return jo; } private byte[] serialize(Serializable in) { return SerializationUtils.serialize(in); } }