/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE file at the root of the source
* tree and available online at
*
* https://github.com/keeps/roda
*/
package org.roda.core.plugins.orchestrate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.roda.core.data.exceptions.AuthorizationDeniedException;
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.exceptions.NotFoundException;
import org.roda.core.data.exceptions.RequestNotValidException;
import org.roda.core.data.v2.IsRODAObject;
import org.roda.core.data.v2.jobs.Job;
import org.roda.core.data.v2.jobs.Report;
import org.roda.core.data.v2.jobs.Report.PluginState;
import org.roda.core.index.IndexService;
import org.roda.core.model.ModelService;
import org.roda.core.plugins.Plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class IngestJobPluginInfo extends JobPluginInfo {
private static final long serialVersionUID = -7993848868644990995L;
private static final Logger LOGGER = LoggerFactory.getLogger(IngestJobPluginInfo.class);
private int stepsCompleted = 0;
private int totalSteps = 0;
// transferredResourceId > map<aipId, report>
private Map<String, Map<String, Report>> allReports = new HashMap<>();
// transferredResourceId > map<aipId, report>
private Map<String, Map<String, Report>> reportsFromBeingProcessed = new HashMap<>();
// transferredResourceId > list<aipId>
private Map<String, List<String>> transferredResourceToAipIds = new HashMap<>();
// aipId > transferredResourceId
private Map<String, String> aipIdToTransferredResourceId = new HashMap<>();
public IngestJobPluginInfo() {
super();
}
public int getStepsCompleted() {
return stepsCompleted;
}
public IngestJobPluginInfo setStepsCompleted(int stepsCompleted) {
this.stepsCompleted = stepsCompleted;
return this;
}
public int getTotalSteps() {
return totalSteps;
}
public IngestJobPluginInfo setTotalSteps(int totalSteps) {
this.totalSteps = totalSteps;
return this;
}
public IngestJobPluginInfo incrementStepsCompletedByOne() {
this.stepsCompleted += 1;
return this;
}
public void update(IngestJobPluginInfo ingestJobPluginInfo) {
this.setTotalSteps(ingestJobPluginInfo.getTotalSteps());
this.setStepsCompleted(ingestJobPluginInfo.getStepsCompleted());
this.setCompletionPercentage(ingestJobPluginInfo.getCompletionPercentage());
this.setSourceObjectsCount(ingestJobPluginInfo.getSourceObjectsCount());
this.setSourceObjectsBeingProcessed(ingestJobPluginInfo.getSourceObjectsBeingProcessed());
this.setSourceObjectsWaitingToBeProcessed(ingestJobPluginInfo.getSourceObjectsWaitingToBeProcessed());
this.setSourceObjectsProcessedWithSuccess(ingestJobPluginInfo.getSourceObjectsProcessedWithSuccess());
this.setSourceObjectsProcessedWithFailure(ingestJobPluginInfo.getSourceObjectsProcessedWithFailure());
this.setOutcomeObjectsWithManualIntervention(ingestJobPluginInfo.getOutcomeObjectsWithManualIntervention());
}
@Override
public <T extends IsRODAObject> JobPluginInfo processJobPluginInformation(Plugin<T> plugin, JobInfo jobInfo) {
int taskObjectsCount = jobInfo.getObjectsCount();
Map<Integer, JobPluginInfo> jobInfos = jobInfo.getJobInfo();
// update information in the map<plugin, pluginInfo>
// FIXME/INFO 20160601 hsilva: the following code would be necessary in a
// distributed architecture
// IngestJobPluginInfo jobPluginInfo = (IngestJobPluginInfo)
// jobInfos.get(plugin);
// jobPluginInfo.update(this);
// calculate general counters
float percentage = 0f;
int sourceObjectsCount = 0;
int sourceObjectsBeingProcessed = 0;
int sourceObjectsProcessedWithSuccess = 0;
int sourceObjectsProcessedWithFailure = 0;
int outcomeObjectsWithManualIntervention = 0;
for (JobPluginInfo jpi : jobInfos.values()) {
IngestJobPluginInfo pluginInfo = (IngestJobPluginInfo) jpi;
if (pluginInfo.getTotalSteps() > 0) {
float pluginPercentage = pluginInfo.getCompletionPercentage() == 100 ? 1.0f : 0.0f;
if (pluginInfo.getCompletionPercentage() != 100) {
pluginPercentage = ((float) pluginInfo.getStepsCompleted()) / pluginInfo.getTotalSteps();
}
float pluginWeight = ((float) pluginInfo.getSourceObjectsCount()) / taskObjectsCount;
percentage += (pluginPercentage * pluginWeight);
sourceObjectsProcessedWithSuccess += pluginInfo.getSourceObjectsProcessedWithSuccess();
sourceObjectsProcessedWithFailure += pluginInfo.getSourceObjectsProcessedWithFailure();
outcomeObjectsWithManualIntervention += pluginInfo.getOutcomeObjectsWithManualIntervention();
}
sourceObjectsBeingProcessed += pluginInfo.getSourceObjectsBeingProcessed();
sourceObjectsCount += pluginInfo.getSourceObjectsCount();
}
IngestJobPluginInfo ingestInfoUpdated = new IngestJobPluginInfo();
ingestInfoUpdated.setCompletionPercentage(Math.round((percentage * 100)));
ingestInfoUpdated.setSourceObjectsCount(sourceObjectsCount);
ingestInfoUpdated.setSourceObjectsBeingProcessed(sourceObjectsBeingProcessed);
ingestInfoUpdated.setSourceObjectsProcessedWithSuccess(sourceObjectsProcessedWithSuccess);
ingestInfoUpdated.setSourceObjectsProcessedWithFailure(sourceObjectsProcessedWithFailure);
ingestInfoUpdated.setOutcomeObjectsWithManualIntervention(outcomeObjectsWithManualIntervention);
return ingestInfoUpdated;
}
public Map<String, Map<String, Report>> getAllReports() {
return allReports;
}
public Map<String, Map<String, Report>> getReportsFromBeingProcessed() {
return reportsFromBeingProcessed;
}
public Map<String, List<String>> getTransferredResourceToAipIds() {
return transferredResourceToAipIds;
}
public List<String> getAipIds() {
return transferredResourceToAipIds.values().stream().flatMap(l -> l.stream()).collect(Collectors.toList());
}
public List<String> getAipIds(String transferredResource) {
return transferredResourceToAipIds.get(transferredResource);
}
public Map<String, String> getAipIdToTransferredResourceId() {
return aipIdToTransferredResourceId;
}
public int getBeingProcessedCounter() {
return transferredResourceToAipIds.size();
}
public void addReport(String sourceObjectId, String outcomeObjectId, Report report) {
if (StringUtils.isNotBlank(sourceObjectId) && StringUtils.isNotBlank(outcomeObjectId)) {
aipIdToTransferredResourceId.put(outcomeObjectId, sourceObjectId);
if (transferredResourceToAipIds.get(sourceObjectId) != null) {
transferredResourceToAipIds.get(sourceObjectId).add(outcomeObjectId);
} else {
List<String> aipIds = new ArrayList<>();
aipIds.add(outcomeObjectId);
transferredResourceToAipIds.put(sourceObjectId, aipIds);
}
}
if (reportsFromBeingProcessed.get(sourceObjectId) != null) {
reportsFromBeingProcessed.get(sourceObjectId).put(outcomeObjectId, report);
allReports.get(sourceObjectId).put(outcomeObjectId, report);
} else {
Map<String, Report> innerReports = new HashMap<>();
innerReports.put(outcomeObjectId, report);
reportsFromBeingProcessed.put(sourceObjectId, innerReports);
allReports.put(sourceObjectId, innerReports);
}
}
public void addReport(String outcomeObjectId, Report report) {
reportsFromBeingProcessed.get(aipIdToTransferredResourceId.get(outcomeObjectId)).get(outcomeObjectId)
.addReport(report);
}
public void remove(String transferredResourceId) {
reportsFromBeingProcessed.remove(transferredResourceId);
transferredResourceToAipIds.remove(transferredResourceId);
}
public void updateCounters() {
int beingProcessed = getBeingProcessedCounter();
setSourceObjectsBeingProcessed(beingProcessed);
setSourceObjectsProcessedWithFailure(getSourceObjectsCount() - beingProcessed);
}
@Override
public void finalizeInfo() {
super.finalizeInfo();
// INFO 20160601 hsilva: the following line is needed because we only mark,
// during ingest processing, the failure and therefore in the end we have to
// set the success counter
setSourceObjectsProcessedWithSuccess(getSourceObjectsCount() - getSourceObjectsProcessedWithFailure());
setStepsCompleted(getTotalSteps());
// 20161220 hsilva: preparing maps for garbage collection
allReports = null;
reportsFromBeingProcessed = null;
}
public void failOtherTransferredResourceAIPs(ModelService model, IndexService index, String transferredResourceId) {
for (Entry<String, Report> aipReportEntry : allReports.get(transferredResourceId).entrySet()) {
Report report = aipReportEntry.getValue();
if (report.getPluginState() != PluginState.FAILURE) {
List<Report> reportItems = report.getReports();
Report reportItem = reportItems.get(reportItems.size() - 1);
reportItem.setPluginState(PluginState.FAILURE)
.setPluginDetails("This AIP processing failed because a related AIP also failed");
reportItems.remove(reportItems.size() - 1);
reportItems.add(reportItem);
report.setPluginState(PluginState.FAILURE);
try {
Job job = model.retrieveJob(report.getJobId());
model.createOrUpdateJobReport(report, job);
} catch (GenericException | RequestNotValidException | NotFoundException | AuthorizationDeniedException e) {
LOGGER.error("Error updating last job report indicating other AIP failure.");
}
}
}
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}