/**
* 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.plugins.characterization;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.xmlbeans.XmlException;
import org.roda.core.RodaCoreFactory;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.common.RodaConstants.PreservationEventType;
import org.roda.core.data.exceptions.AlreadyExistsException;
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.RODAException;
import org.roda.core.data.exceptions.RequestNotValidException;
import org.roda.core.data.v2.IsRODAObject;
import org.roda.core.data.v2.ip.AIP;
import org.roda.core.data.v2.ip.AIPState;
import org.roda.core.data.v2.ip.File;
import org.roda.core.data.v2.ip.Representation;
import org.roda.core.data.v2.jobs.Job;
import org.roda.core.data.v2.jobs.PluginType;
import org.roda.core.data.v2.jobs.Report;
import org.roda.core.data.v2.jobs.Report.PluginState;
import org.roda.core.data.v2.validation.ValidationException;
import org.roda.core.index.IndexService;
import org.roda.core.model.ModelService;
import org.roda.core.plugins.AbstractAIPComponentsPlugin;
import org.roda.core.plugins.Plugin;
import org.roda.core.plugins.PluginException;
import org.roda.core.plugins.orchestrate.SimpleJobPluginInfo;
import org.roda.core.plugins.plugins.PluginHelper;
import org.roda.core.storage.StorageService;
import org.roda.core.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PremisSkeletonPlugin<T extends IsRODAObject> extends AbstractAIPComponentsPlugin<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(PremisSkeletonPlugin.class);
@Override
public void init() throws PluginException {
// do nothing
}
@Override
public void shutdown() {
// do nothing
}
public static String getStaticName() {
return "Fixity information computation";
}
@Override
public String getName() {
return getStaticName();
}
public static String getStaticDescription() {
return "Computes file fixity information (also known as checksum) for all data files within an AIP, representation or file and stores this information in PREMIS objects "
+ "within the corresponding entity. This task uses SHA-256 as the default checksum algorithm, however, other algorithms can be configured in “roda-core.properties”."
+ "\nFile fixity is the property of a digital file being fixed, or unchanged. “AIP corruption risk assessment” is the process of validating that a file has not changed or been "
+ "altered from a previous state. In order to validate the fixity of an AIP or file, fixity information has to be generated beforehand.";
}
@Override
public String getDescription() {
return getStaticDescription();
}
@Override
public String getVersionImpl() {
return "1.0";
}
@Override
public Report executeOnAIP(IndexService index, ModelService model, StorageService storage, Report report,
SimpleJobPluginInfo jobPluginInfo, List<AIP> list, Job job) throws PluginException {
try {
for (AIP aip : list) {
LOGGER.debug("Processing AIP {}", aip.getId());
Report reportItem = PluginHelper.initPluginReportItem(this, aip.getId(), AIP.class, AIPState.INGEST_PROCESSING);
PluginHelper.updatePartialJobReport(this, model, reportItem, false, job);
try {
for (Representation representation : aip.getRepresentations()) {
LOGGER.debug("Processing representation {} from AIP {}", representation.getId(), aip.getId());
List<String> algorithms = RodaCoreFactory.getFixityAlgorithms();
PremisSkeletonPluginUtils.createPremisSkeletonOnRepresentation(model, aip.getId(), representation.getId(),
algorithms);
model.notifyRepresentationUpdated(representation);
}
jobPluginInfo.incrementObjectsProcessedWithSuccess();
reportItem.setPluginState(PluginState.SUCCESS);
} catch (RODAException | XmlException | IOException e) {
LOGGER.error("Error processing AIP {}", aip.getId(), e);
jobPluginInfo.incrementObjectsProcessedWithFailure();
reportItem.setPluginState(PluginState.FAILURE).setPluginDetails(e.getMessage());
}
try {
boolean notify = true;
PluginHelper.createPluginEvent(this, aip.getId(), model, index, reportItem.getPluginState(), "", notify);
} catch (ValidationException | RequestNotValidException | NotFoundException | GenericException
| AuthorizationDeniedException | AlreadyExistsException e) {
LOGGER.error("Error creating event: {}", e.getMessage(), e);
}
report.addReport(reportItem);
PluginHelper.updatePartialJobReport(this, model, reportItem, true, job);
}
} catch (ClassCastException e) {
LOGGER.error("Trying to execute an AIP-only plugin with other objects");
jobPluginInfo.incrementObjectsProcessedWithFailure(list.size());
}
return report;
}
@Override
public Report executeOnRepresentation(IndexService index, ModelService model, StorageService storage, Report report,
SimpleJobPluginInfo jobPluginInfo, List<Representation> list, Job job) throws PluginException {
for (Representation representation : list) {
LOGGER.debug("Processing representation {} from AIP {}", representation.getId(), representation.getAipId());
Report reportItem = PluginHelper.initPluginReportItem(this, IdUtils.getRepresentationId(representation),
Representation.class, AIPState.ACTIVE);
PluginHelper.updatePartialJobReport(this, model, reportItem, false, job);
reportItem.setPluginState(PluginState.SUCCESS);
try {
List<String> algorithms = RodaCoreFactory.getFixityAlgorithms();
PremisSkeletonPluginUtils.createPremisSkeletonOnRepresentation(model, representation.getAipId(),
representation.getId(), algorithms);
model.notifyRepresentationUpdated(representation);
jobPluginInfo.incrementObjectsProcessedWithSuccess();
} catch (RODAException | XmlException | IOException e) {
LOGGER.error("Error processing representation {}", representation.getId(), e);
jobPluginInfo.incrementObjectsProcessedWithFailure();
reportItem.setPluginState(PluginState.FAILURE).addPluginDetails(e.getMessage() + "\n");
}
try {
boolean notify = true;
PluginHelper.createPluginEvent(this, representation.getAipId(), representation.getId(), model, index, null,
null, reportItem.getPluginState(), "", notify);
} catch (ValidationException | RequestNotValidException | NotFoundException | GenericException
| AuthorizationDeniedException | AlreadyExistsException e) {
LOGGER.error("Error creating event: {}", e.getMessage(), e);
}
report.addReport(reportItem);
PluginHelper.updatePartialJobReport(this, model, reportItem, true, job);
}
return report;
}
@Override
public Report executeOnFile(IndexService index, ModelService model, StorageService storage, Report report,
SimpleJobPluginInfo jobPluginInfo, List<File> list, Job job) throws PluginException {
for (File file : list) {
LOGGER.debug("Processing file {} from representation {} from AIP {}", file.getId(), file.getRepresentationId(),
file.getAipId());
Report reportItem = PluginHelper.initPluginReportItem(this, IdUtils.getFileId(file), File.class, AIPState.ACTIVE);
PluginHelper.updatePartialJobReport(this, model, reportItem, false, job);
reportItem.setPluginState(PluginState.SUCCESS);
try {
List<String> algorithms = RodaCoreFactory.getFixityAlgorithms();
PremisSkeletonPluginUtils.createPremisSkeletonOnFile(model, file, algorithms);
jobPluginInfo.incrementObjectsProcessedWithSuccess();
} catch (RODAException | XmlException | IOException e) {
LOGGER.error("Error processing file {}", file.getId(), e);
jobPluginInfo.incrementObjectsProcessedWithFailure();
reportItem.setPluginState(PluginState.FAILURE).addPluginDetails(e.getMessage() + "\n");
}
try {
boolean notify = true;
PluginHelper.createPluginEvent(this, file.getAipId(), file.getRepresentationId(), file.getPath(), file.getId(),
model, index, null, null, reportItem.getPluginState(), "", notify);
} catch (ValidationException | RequestNotValidException | NotFoundException | GenericException
| AuthorizationDeniedException | AlreadyExistsException e) {
LOGGER.error("Error creating event: {}", e.getMessage(), e);
}
report.addReport(reportItem);
PluginHelper.updatePartialJobReport(this, model, reportItem, true, job);
}
return report;
}
@Override
public Plugin<T> cloneMe() {
return new PremisSkeletonPlugin<>();
}
@Override
public PluginType getType() {
return PluginType.AIP_TO_AIP;
}
@Override
public boolean areParameterValuesValid() {
return true;
}
@Override
public PreservationEventType getPreservationEventType() {
return PreservationEventType.MESSAGE_DIGEST_CALCULATION;
}
@Override
public String getPreservationEventDescription() {
return "Created base PREMIS objects with file original name and file fixity information (SHA-256).";
}
@Override
public String getPreservationEventSuccessMessage() {
return "PREMIS objects were successfully created.";
}
@Override
public String getPreservationEventFailureMessage() {
return "Failed to create PREMIS objects from files.";
}
@Override
public Report beforeAllExecute(IndexService index, ModelService model, StorageService storage)
throws PluginException {
// do nothing
return null;
}
@Override
public Report afterAllExecute(IndexService index, ModelService model, StorageService storage) throws PluginException {
// do nothing
return null;
}
@Override
public List<String> getCategories() {
return Arrays.asList(RodaConstants.PLUGIN_CATEGORY_CHARACTERIZATION);
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public List<Class<T>> getObjectClasses() {
List<Class<? extends IsRODAObject>> list = new ArrayList<>();
list.add(AIP.class);
list.add(Representation.class);
list.add(File.class);
return (List) list;
}
}