package eu.openaire.urls_controller.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; import com.google.gson.Gson; import eu.openaire.urls_controller.util.GenericUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @JsonInclude(JsonInclude.Include.NON_NULL) public class BulkImportReport { private static final Logger logger = LoggerFactory.getLogger(BulkImportReport.class); private static final Gson gson = new Gson(); // This is "transient" by default. It won't be included in any json object. @JsonProperty private String provenance; @JsonProperty private String reportLocation; @JsonProperty private String reportID; // This will not be serialized, since Gson cannot serialize Multimaps. Instead, it will be converted to the "simpler" map below. transient private SetMultimap eventsMultimap = Multimaps.synchronizedSetMultimap(LinkedHashMultimap.create()); // We need a "LinkedHashMultimap", se that the order of the keys (timestamps) stay ascending, so the final report makes sense in chronological order. // We need for one key (timestamp) to have multiple values (events), in order to not lose events happening at the same time. @JsonProperty private Map> eventsMap; transient private final Lock reportLock = new ReentrantLock(true); public BulkImportReport(String provenance, String reportLocation, String reportID) { this.provenance = provenance; this.reportLocation = reportLocation; this.reportID = reportID; } public void addEvent(String event) { eventsMultimap.put(GenericUtils.getReadableCurrentTimeAndZone(), event); // This is synchronized. } /** * Synchronize it with a lock, to avoid concurrency issues when concurrent calls are made to the same bulkImport-Report object. * */ public String getJsonReport() { String reportToReturn = null; reportLock.lock(); try { //Convert the SetMultimap to Map>, since Gson cannot serialize Multimaps. eventsMap = new LinkedHashMap<>(eventsMultimap.asMap()); // Make sure we use a clone of the original data, in order to avoid any exception in the "gson.toJson()" method, when at the same time another thread modifies the "eventsMultimap". reportToReturn = gson.toJson(this, BulkImportReport.class); } catch (Exception e) { logger.error("Problem when producing the JSON-string with the BulkImportReport! | reportID: '" + reportID + "'", e); } finally { reportLock.unlock(); } return reportToReturn; // It may be null. } public String getProvenance() { return provenance; } public void setProvenance(String provenance) { this.provenance = provenance; } public String getReportLocation() { return reportLocation; } public void setReportLocation(String reportLocation) { this.reportLocation = reportLocation; } public String getReportID() { return reportID; } public void setReportID(String reportID) { this.reportID = reportID; } public SetMultimap getEventsMultimap() { return eventsMultimap; } public void setEventsMultimap(SetMultimap eventsMultimap) { this.eventsMultimap = eventsMultimap; } public Map> getEventsMap() { return eventsMap; } public void setEventsMap(Map> eventsMap) { this.eventsMap = eventsMap; } @Override public String toString() { return "BulkImportReport{" + "provenance='" + provenance + '\'' + ", reportLocation='" + reportLocation + '\'' + ", reportID='" + reportID + '\'' + ", eventsMultimap=" + eventsMultimap + ", eventsMap=" + eventsMap + '}'; } }