/**
* 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;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.roda.core.RodaCoreFactory;
import org.roda.core.common.PremisV3Utils;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.common.RodaConstants.RODA_TYPE;
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.IsStillUpdatingException;
import org.roda.core.data.exceptions.JobAlreadyStartedException;
import org.roda.core.data.exceptions.JobException;
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.utils.JsonUtils;
import org.roda.core.data.v2.IsRODAObject;
import org.roda.core.data.v2.LinkingObjectUtils;
import org.roda.core.data.v2.LiteOptionalWithCause;
import org.roda.core.data.v2.LiteRODAObject;
import org.roda.core.data.v2.Void;
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.formats.Format;
import org.roda.core.data.v2.index.IndexResult;
import org.roda.core.data.v2.index.filter.Filter;
import org.roda.core.data.v2.index.filter.OneOfManyFilterParameter;
import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
import org.roda.core.data.v2.index.select.SelectedItems;
import org.roda.core.data.v2.index.select.SelectedItemsList;
import org.roda.core.data.v2.index.sort.Sorter;
import org.roda.core.data.v2.index.sublist.Sublist;
import org.roda.core.data.v2.ip.AIP;
import org.roda.core.data.v2.ip.AIPState;
import org.roda.core.data.v2.ip.DIP;
import org.roda.core.data.v2.ip.IndexedAIP;
import org.roda.core.data.v2.ip.Permissions;
import org.roda.core.data.v2.ip.StoragePath;
import org.roda.core.data.v2.ip.TransferredResource;
import org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent;
import org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent;
import org.roda.core.data.v2.ip.metadata.LinkingIdentifier;
import org.roda.core.data.v2.ip.metadata.PreservationMetadata;
import org.roda.core.data.v2.ip.metadata.PreservationMetadata.PreservationMetadataType;
import org.roda.core.data.v2.jobs.Job;
import org.roda.core.data.v2.jobs.PluginParameter;
import org.roda.core.data.v2.jobs.Report;
import org.roda.core.data.v2.jobs.Report.PluginState;
import org.roda.core.data.v2.log.LogEntry;
import org.roda.core.data.v2.notifications.Notification;
import org.roda.core.data.v2.risks.Risk;
import org.roda.core.data.v2.risks.RiskIncidence;
import org.roda.core.data.v2.user.RODAMember;
import org.roda.core.data.v2.validation.ValidationException;
import org.roda.core.index.IndexService;
import org.roda.core.index.utils.IterableIndexResult;
import org.roda.core.model.LiteRODAObjectFactory;
import org.roda.core.model.ModelService;
import org.roda.core.plugins.Plugin;
import org.roda.core.plugins.PluginException;
import org.roda.core.plugins.RODAObjectProcessingLogic;
import org.roda.core.plugins.RODAObjectsProcessingLogic;
import org.roda.core.plugins.RODAProcessingLogic;
import org.roda.core.plugins.orchestrate.IngestJobPluginInfo;
import org.roda.core.plugins.orchestrate.JobPluginInfo;
import org.roda.core.plugins.orchestrate.SimpleJobPluginInfo;
import org.roda.core.plugins.orchestrate.akka.Messages;
import org.roda.core.plugins.plugins.reindex.ReindexAIPPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexActionLogPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexDIPPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexFormatPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexIncidencePlugin;
import org.roda.core.plugins.plugins.reindex.ReindexJobPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexNotificationPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexPreservationAgentPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexPreservationRepositoryEventPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexRiskPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexRodaMemberPlugin;
import org.roda.core.plugins.plugins.reindex.ReindexTransferredResourcePlugin;
import org.roda.core.storage.ContentPayload;
import org.roda.core.storage.DefaultStoragePath;
import org.roda.core.storage.StorageService;
import org.roda.core.storage.fs.FSUtils;
import org.roda.core.storage.fs.FileStorageService;
import org.roda.core.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class PluginHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(PluginHelper.class);
private PluginHelper() {
// do nothing
}
public static <T extends IsRODAObject> Report processObjects(Plugin<T> plugin,
RODAObjectsProcessingLogic<T> objectsLogic, IndexService index, ModelService model, StorageService storage,
List<LiteOptionalWithCause> liteList) throws PluginException {
Report report = PluginHelper.initPluginReport(plugin);
try {
SimpleJobPluginInfo jobPluginInfo = PluginHelper.getInitialJobInformation(plugin, liteList.size());
PluginHelper.updateJobInformation(plugin, jobPluginInfo);
Job job = PluginHelper.getJob(plugin, model);
List<T> list = PluginHelper.transformLitesIntoObjects(model, plugin, report, jobPluginInfo, liteList, job);
try {
objectsLogic.process(index, model, storage, report, job, jobPluginInfo, plugin, list);
} catch (Exception e) {
LOGGER.error("Unexpected exception during 'objectsLogic' execution", e);
}
jobPluginInfo.finalizeInfo();
PluginHelper.updateJobInformation(plugin, jobPluginInfo);
} catch (JobException | AuthorizationDeniedException | RequestNotValidException | GenericException
| NotFoundException e) {
throw new PluginException("A job exception has occurred", e);
}
return report;
}
public static <T extends IsRODAObject> Report processObjects(Plugin<T> plugin, RODAProcessingLogic<T> beforeLogic,
RODAObjectProcessingLogic<T> perObjectLogic, RODAProcessingLogic<T> afterLogic, IndexService index,
ModelService model, StorageService storage, List<LiteOptionalWithCause> liteList) throws PluginException {
Report report = PluginHelper.initPluginReport(plugin);
try {
SimpleJobPluginInfo jobPluginInfo = PluginHelper.getInitialJobInformation(plugin, liteList.size());
PluginHelper.updateJobInformation(plugin, jobPluginInfo);
Job job = PluginHelper.getJob(plugin, model);
List<T> list = PluginHelper.transformLitesIntoObjects(model, plugin, report, jobPluginInfo, liteList, job);
if (beforeLogic != null) {
try {
beforeLogic.process(index, model, storage, report, job, jobPluginInfo, plugin);
} catch (Exception e) {
LOGGER.error("Unexpected exception during 'beforeLogic' execution", e);
}
}
for (T object : list) {
try {
perObjectLogic.process(index, model, storage, report, job, jobPluginInfo, plugin, object);
} catch (Exception e) {
LOGGER.error("Unexpected exception during 'perObjectLogic' execution", e);
}
}
if (afterLogic != null) {
try {
afterLogic.process(index, model, storage, report, job, jobPluginInfo, plugin);
} catch (Exception e) {
LOGGER.error("Unexpected exception during 'afterLogic' execution", e);
}
}
jobPluginInfo.finalizeInfo();
PluginHelper.updateJobInformation(plugin, jobPluginInfo);
} catch (JobException | AuthorizationDeniedException | RequestNotValidException | GenericException
| NotFoundException e) {
throw new PluginException("A job exception has occurred", e);
}
return report;
}
public static <T extends IsRODAObject> Report processObjects(Plugin<T> plugin, RODAProcessingLogic<T> beforeLogic,
RODAObjectProcessingLogic<T> perObjectLogic, IndexService index, ModelService model, StorageService storage,
List<LiteOptionalWithCause> liteList) throws PluginException {
return processObjects(plugin, beforeLogic, perObjectLogic, null, index, model, storage, liteList);
}
public static <T extends IsRODAObject> Report processObjects(Plugin<T> plugin,
RODAObjectProcessingLogic<T> perObjectLogic, RODAProcessingLogic<T> afterLogic, IndexService index,
ModelService model, StorageService storage, List<LiteOptionalWithCause> liteList) throws PluginException {
return processObjects(plugin, null, perObjectLogic, afterLogic, index, model, storage, liteList);
}
public static <T extends IsRODAObject> Report processObjects(Plugin<T> plugin,
RODAObjectProcessingLogic<T> perObjectLogic, IndexService index, ModelService model, StorageService storage,
List<LiteOptionalWithCause> liteList) throws PluginException {
return processObjects(plugin, null, perObjectLogic, null, index, model, storage, liteList);
}
public static Report processVoids(Plugin<Void> plugin, RODAProcessingLogic<Void> logic, IndexService index,
ModelService model, StorageService storage) throws PluginException {
return processVoids(plugin, logic, index, model, storage, 0);
}
public static Report processVoids(Plugin<Void> plugin, RODAProcessingLogic<Void> logic, IndexService index,
ModelService model, StorageService storage, int setSourceObjectsCount) throws PluginException {
Report report = PluginHelper.initPluginReport(plugin);
try {
SimpleJobPluginInfo jobPluginInfo = PluginHelper.getInitialJobInformation(plugin, 0);
jobPluginInfo.setSourceObjectsCount(setSourceObjectsCount);
PluginHelper.updateJobInformation(plugin, jobPluginInfo);
Job job = PluginHelper.getJob(plugin, model);
try {
logic.process(index, model, storage, report, job, jobPluginInfo, plugin);
} catch (Exception e) {
LOGGER.error("Unexpected exception during 'logic' execution", e);
}
jobPluginInfo.finalizeInfo();
PluginHelper.updateJobInformation(plugin, jobPluginInfo);
} catch (JobException | AuthorizationDeniedException | RequestNotValidException | GenericException
| NotFoundException e) {
throw new PluginException("A job exception has occurred", e);
}
return report;
}
/***************** Job report related *****************/
/******************************************************/
public static <T extends IsRODAObject> Report initPluginReport(Plugin<T> plugin) {
return initPluginReportItem(plugin, "", "");
}
public static <T extends IsRODAObject> Report initPluginReportItem(Plugin<T> plugin,
TransferredResource transferredResource) {
return initPluginReportItem(plugin, "", transferredResource.getUUID())
.setSourceObjectClass(TransferredResource.class.getName()).setOutcomeObjectClass(AIP.class.getName())
.setOutcomeObjectState(AIPState.INGEST_PROCESSING).setSourceObjectOriginalName(transferredResource.getName());
}
public static <T extends IsRODAObject> Report initPluginReportItem(Plugin<T> plugin, String outcomeObjectId,
AIPState initialOutcomeObjectState) {
return initPluginReportItem(plugin, outcomeObjectId, "").setOutcomeObjectState(initialOutcomeObjectState);
}
public static <T extends IsRODAObject> Report initPluginReportItem(Plugin<T> plugin, String objectId,
Class<?> clazz) {
return initPluginReportItem(plugin, objectId, objectId).setSourceObjectClass(clazz.getName())
.setOutcomeObjectClass(clazz.getName());
}
public static <T extends IsRODAObject> Report initPluginReportItem(Plugin<T> plugin, String objectId, Class<?> clazz,
AIPState initialOutcomeObjectState) {
return initPluginReportItem(plugin, objectId, objectId).setSourceObjectClass(clazz.getName())
.setOutcomeObjectClass(clazz.getName()).setOutcomeObjectState(initialOutcomeObjectState);
}
public static <T extends IsRODAObject> Report initPluginReportItem(Plugin<T> plugin, String sourceObjectId,
String outcomeObjectId, Class<?> clazz, AIPState initialOutcomeObjectState) {
return initPluginReportItem(plugin, outcomeObjectId, sourceObjectId).setSourceObjectClass(clazz.getName())
.setOutcomeObjectClass(clazz.getName()).setOutcomeObjectState(initialOutcomeObjectState);
}
private static <T extends IsRODAObject> Report initPluginReportItem(Plugin<T> plugin, String outcomeObjectId,
String sourceObjectId) {
String jobId = getJobId(plugin);
Report reportItem = new Report();
reportItem.injectLineSeparator(System.lineSeparator());
String jobReportPartialId = outcomeObjectId;
// FIXME 20160516 hsilva: this has problems when doing one to many SIP > AIP
// operation
if (StringUtils.isBlank(jobReportPartialId)) {
jobReportPartialId = sourceObjectId;
}
reportItem.setId(IdUtils.getJobReportId(jobId, jobReportPartialId));
reportItem.setJobId(jobId);
reportItem.setSourceObjectId(sourceObjectId);
reportItem.setOutcomeObjectId(outcomeObjectId);
reportItem.setTitle(plugin.getName());
reportItem.setPlugin(plugin.getClass().getName());
reportItem.setPluginName(plugin.getName());
reportItem.setPluginVersion(plugin.getVersion());
reportItem.setTotalSteps(getTotalStepsFromParameters(plugin));
return reportItem;
}
public static <T extends IsRODAObject> void createJobReport(Plugin<T> plugin, ModelService model, Report reportItem) {
String jobId = getJobId(plugin);
Report report = new Report(reportItem);
report.injectLineSeparator(System.lineSeparator());
String reportPartialId = reportItem.getOutcomeObjectId();
// FIXME 20160516 hsilva: this has problems when doing one to many SIP > AIP
// operation
if (StringUtils.isBlank(reportPartialId)) {
reportPartialId = reportItem.getSourceObjectId();
}
reportItem.setId(IdUtils.getJobReportId(jobId, reportPartialId));
report.setId(reportItem.getId());
report.setJobId(jobId);
if (reportItem.getTotalSteps() != 0) {
report.setTotalSteps(reportItem.getTotalSteps());
} else {
report.setTotalSteps(getTotalStepsFromParameters(plugin));
}
report.addReport(reportItem);
try {
Job job = model.retrieveJob(jobId);
model.createOrUpdateJobReport(report, job);
} catch (GenericException | RequestNotValidException | NotFoundException | AuthorizationDeniedException e) {
LOGGER.error("Error creating Job Report", e);
}
}
public static <T extends IsRODAObject> void updateJobReportState(Plugin<T> plugin, ModelService model, String aipId,
AIPState newState) {
try {
String jobId = getJobId(plugin);
Report jobReport = model.retrieveJobReport(jobId, aipId, true);
jobReport.setOutcomeObjectState(newState);
Job job = model.retrieveJob(jobId);
model.createOrUpdateJobReport(jobReport, job);
} catch (GenericException | RequestNotValidException | NotFoundException | AuthorizationDeniedException e) {
LOGGER.error("Error while updating Job Report", e);
}
}
public static <T extends IsRODAObject> void updatePartialJobReport(Plugin<T> plugin, ModelService model,
Report reportItem, boolean replaceLastReportItemIfTheSame, Job cachedJob) {
String jobId = getJobId(plugin);
boolean retrieved = true;
try {
Report jobReport;
try {
jobReport = model.retrieveJobReport(jobId, reportItem.getOutcomeObjectId(), true);
} catch (NotFoundException e) {
jobReport = initPluginReportItem(plugin, reportItem.getOutcomeObjectId(), reportItem.getSourceObjectId())
.setSourceObjectClass(reportItem.getSourceObjectClass())
.setOutcomeObjectClass(reportItem.getOutcomeObjectClass());
jobReport.setId(reportItem.getId());
jobReport.setDateCreated(reportItem.getDateCreated());
jobReport.addReport(reportItem);
retrieved = false;
}
if (retrieved) {
if (!replaceLastReportItemIfTheSame) {
jobReport.addReport(reportItem);
} else {
List<Report> reportItems = jobReport.getReports();
Report report = reportItems.get(reportItems.size() - 1);
if (report.getPlugin().equalsIgnoreCase(reportItem.getPlugin())) {
reportItems.remove(reportItems.size() - 1);
jobReport.setStepsCompleted(jobReport.getStepsCompleted() - 1);
jobReport.addReport(reportItem);
}
}
}
model.createOrUpdateJobReport(jobReport, cachedJob);
} catch (GenericException | RequestNotValidException | AuthorizationDeniedException e) {
LOGGER.error("Error while updating Job Report", e);
}
}
private static void updateJobReport(ModelService model, Report report) {
try {
Job job = model.retrieveJob(report.getJobId());
model.createOrUpdateJobReport(report, job);
} catch (GenericException | RequestNotValidException | NotFoundException | AuthorizationDeniedException e) {
LOGGER.error("Error while updating Job Report", e);
}
}
/***************** Job related *****************/
/***********************************************/
public static <T extends IsRODAObject> String getJobId(Plugin<T> plugin) {
return plugin.getParameterValues().get(RodaConstants.PLUGIN_PARAMS_JOB_ID);
}
/**
* 20160329 hsilva: use this method only to get job information that most
* certainly won't change in time (e.g. username, etc.)
*/
public static <T extends IsRODAObject> Job getJob(Plugin<T> plugin, IndexService index)
throws NotFoundException, GenericException {
String jobId = getJobId(plugin);
if (jobId != null) {
return index.retrieve(Job.class, jobId, new ArrayList<>());
} else {
throw new NotFoundException("Job not found");
}
}
public static <T extends IsRODAObject> Job getJob(Plugin<T> plugin, ModelService model)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
String jobId = getJobId(plugin);
return getJob(jobId, model);
}
public static Job getJob(String jobId, ModelService model)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
if (jobId != null) {
return model.retrieveJob(jobId);
} else {
throw new NotFoundException("Job not found");
}
}
public static String getJobUsername(String jobId, IndexService index)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
if (jobId != null) {
Job job = index.retrieve(Job.class, jobId, Arrays.asList(RodaConstants.JOB_USERNAME));
return job.getUsername();
} else {
throw new NotFoundException("Job not found");
}
}
public static <T extends IsRODAObject> String getJobUsername(Plugin<T> plugin, IndexService index)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
String jobId = getJobId(plugin);
return getJobUsername(jobId, index);
}
public static <T extends IsRODAObject> String getJobUsername(Plugin<T> plugin, ModelService model)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
String jobId = getJobId(plugin);
if (jobId != null) {
Job job = model.retrieveJob(jobId);
return job.getUsername();
} else {
throw new NotFoundException("Job not found");
}
}
public static <T extends IsRODAObject> Path getJobWorkingDirectory(Plugin<T> plugin) {
return RodaCoreFactory.getWorkingDirectory().resolve(getJobId(plugin));
}
/**
* Updates the job status for a particular plugin instance
*/
public static <T extends IsRODAObject> void updateJobInformation(Plugin<T> plugin, JobPluginInfo jobPluginInfo)
throws JobException {
Map<String, String> parameterValues = plugin.getParameterValues();
if (!parameterValues.containsKey(RodaConstants.PLUGIN_PARAMS_REPORTING_CLASS)
|| (parameterValues.containsKey(RodaConstants.PLUGIN_PARAMS_REPORTING_CLASS)
&& parameterValues.get(RodaConstants.PLUGIN_PARAMS_REPORTING_CLASS).equals(plugin.getClass().getName()))) {
RodaCoreFactory.getPluginOrchestrator().updateJobInformation(plugin, jobPluginInfo);
}
}
public static <T extends IsRODAObject> SimpleJobPluginInfo getInitialJobInformation(Plugin<T> plugin,
int sourceObjectsBeingProcess) throws JobException {
SimpleJobPluginInfo jobPluginInfo = plugin.getJobPluginInfo(SimpleJobPluginInfo.class);
if (jobPluginInfo != null) {
jobPluginInfo.setSourceObjectsBeingProcessed(sourceObjectsBeingProcess).setSourceObjectsWaitingToBeProcessed(0);
return jobPluginInfo;
} else {
return new SimpleJobPluginInfo();
}
}
public static <T extends IsRODAObject, T1 extends JobPluginInfo> T1 getInitialJobInformation(Plugin<T> plugin,
Class<T1> jobPluginInfoClass) throws JobException {
T1 jobPluginInfo = plugin.getJobPluginInfo(jobPluginInfoClass);
if (jobPluginInfo != null) {
jobPluginInfo.setSourceObjectsBeingProcessed(jobPluginInfo.getSourceObjectsCount())
.setSourceObjectsWaitingToBeProcessed(0);
} else {
throw new JobException("Cannot obtain job plugin info (that supposedly was set by plugin orchestrator)");
}
return jobPluginInfo;
}
/***************** Plugin parameters related *****************/
/*************************************************************/
public static List<Class<? extends IsRODAObject>> getReindexObjectClasses() {
List<Class<? extends IsRODAObject>> list = new ArrayList<>();
list.add(TransferredResource.class);
list.add(AIP.class);
list.add(RODAMember.class);
list.add(Format.class);
list.add(Notification.class);
list.add(Risk.class);
list.add(LogEntry.class);
list.add(RiskIncidence.class);
list.add(Job.class);
list.add(IndexedPreservationAgent.class);
list.add(IndexedPreservationEvent.class);
list.add(DIP.class);
return list;
}
public static <T extends IsRODAObject> boolean getBooleanFromParameters(Plugin<T> plugin,
PluginParameter pluginParameter) {
return verifyIfStepShouldBePerformed(plugin, pluginParameter);
}
public static <T extends IsRODAObject> String getStringFromParameters(Plugin<T> plugin,
PluginParameter pluginParameter) {
return plugin.getParameterValues().getOrDefault(pluginParameter.getId(), pluginParameter.getDefaultValue());
}
// FIXME 20161128 hsilva: rename this to search scope
public static <T extends IsRODAObject> String getParentIdFromParameters(Plugin<T> plugin) {
return plugin.getParameterValues().get(RodaConstants.PLUGIN_PARAMS_PARENT_ID);
}
public static <T extends IsRODAObject> Optional<String> getSearchScopeFromParameters(Plugin<T> plugin,
ModelService model) {
Optional<String> ret = Optional.empty();
try {
String searchScopeFromParameters = plugin.getParameterValues().get(RodaConstants.PLUGIN_PARAMS_PARENT_ID);
if (StringUtils.isNotBlank(searchScopeFromParameters)) {
AIP aip = model.retrieveAIP(searchScopeFromParameters);
ret = Optional.ofNullable(aip.getId());
}
} catch (RequestNotValidException | NotFoundException | GenericException | AuthorizationDeniedException e) {
// do nothing
}
return ret;
}
// FIXME 20161128 hsilva: rename this to force search scope
public static <T extends IsRODAObject> boolean getForceParentIdFromParameters(Plugin<T> plugin) {
return new Boolean(plugin.getParameterValues().get(RodaConstants.PLUGIN_PARAMS_FORCE_PARENT_ID));
}
/*********************************/
public static Optional<String> getComputedParent(ModelService model, IndexService index, List<String> ancestors,
Optional<String> computedSearchScope, boolean forceSearchScope, String jobId)
throws GenericException, RequestNotValidException, AuthorizationDeniedException, NotFoundException,
AlreadyExistsException, ValidationException {
if (ancestors.isEmpty()) {
return computedSearchScope;
}
return resolveParent(model, index, ancestors, computedSearchScope, forceSearchScope, jobId);
}
private static Optional<String> resolveParent(ModelService model, IndexService index, List<String> ancestorsFromSIP,
Optional<String> computedSearchScope, boolean forceParent, String jobId) {
Optional<String> parent = computedSearchScope;
if (forceParent) {
parent = computedSearchScope;
} else {
List<String> ancestors = new ArrayList<>(ancestorsFromSIP);
// Reverse list so that the top ancestors come first
Collections.reverse(ancestors);
try {
for (String ancestor : ancestors) {
Optional<String> computedAncestorId = getAncestorById(ancestor, parent, index, RodaConstants.INGEST_SIP_IDS);
if (!computedAncestorId.isPresent()) {
computedAncestorId = getAncestorById(ancestor, parent, index, RodaConstants.INDEX_UUID);
}
if (computedAncestorId.isPresent()) {
parent = computedAncestorId;
} else {
parent = createGhost(ancestor, parent, model, index, jobId);
}
}
} catch (NotFoundException | GenericException | RequestNotValidException | AlreadyExistsException
| AuthorizationDeniedException e) {
parent = computedSearchScope;
}
}
return parent;
}
private static Optional<String> createGhost(String ancestor, Optional<String> parent, ModelService model,
IndexService index, String jobId) throws NotFoundException, GenericException, RequestNotValidException,
AlreadyExistsException, AuthorizationDeniedException {
String username = getJobUsername(jobId, index);
Permissions permissions = new Permissions();
permissions.setUserPermissions(username,
new HashSet<>(Arrays.asList(Permissions.PermissionType.CREATE, Permissions.PermissionType.READ,
Permissions.PermissionType.UPDATE, Permissions.PermissionType.DELETE, Permissions.PermissionType.GRANT)));
boolean isGhost = true;
AIP ghostAIP = model.createAIP(parent.orElse(null), "", permissions, Arrays.asList(ancestor), jobId, true, username,
isGhost);
return Optional.ofNullable(ghostAIP.getId());
}
private static Optional<String> getAncestorById(String ancestor, Optional<String> computedSearchScope,
IndexService index, String aipField) {
if (ancestor.equalsIgnoreCase(computedSearchScope.orElse(null))) {
return computedSearchScope;
}
Optional<String> ancestorBySIPId = Optional.empty();
Filter ancestorFilter = new Filter(new SimpleFilterParameter(aipField, ancestor));
if (computedSearchScope.isPresent()) {
try {
IndexedAIP computedParent = index.retrieve(IndexedAIP.class, computedSearchScope.get(),
Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.AIP_ANCESTORS));
ancestorFilter.add(new SimpleFilterParameter(RodaConstants.AIP_ANCESTORS, computedParent.getId()));
} catch (NotFoundException | GenericException e) {
// Do nothing
}
}
try {
// TODO 2016-11-24 sleroux: add user permission
IndexResult<IndexedAIP> result = index.find(IndexedAIP.class, ancestorFilter, Sorter.NONE, new Sublist(0, 1),
Arrays.asList(RodaConstants.INDEX_UUID));
if (result.getTotalCount() >= 1) {
IndexedAIP indexedAIP = result.getResults().get(0);
ancestorBySIPId = Optional.ofNullable(indexedAIP.getId());
}
} catch (GenericException | RequestNotValidException e) {
// Do nothing
LOGGER.error("Error getting ancestor", e);
}
return ancestorBySIPId;
}
/*******************************************************/
public static <T extends IsRODAObject> int getTotalStepsFromParameters(Plugin<T> plugin) {
int totalSteps = 1;
String totalStepsString = plugin.getParameterValues().get(RodaConstants.PLUGIN_PARAMS_TOTAL_STEPS);
if (totalStepsString != null) {
try {
totalSteps = Integer.parseInt(totalStepsString);
} catch (NumberFormatException e) {
// return default value
}
}
return totalSteps;
}
public static <T extends IsRODAObject> boolean verifyIfStepShouldBePerformed(Plugin<T> plugin,
PluginParameter pluginParameter) {
String paramValue = getStringFromParameters(plugin, pluginParameter);
boolean perform = Boolean.parseBoolean(paramValue);
if (perform) {
String parameterClass = RodaConstants.PLUGIN_PARAMETER_TO_CLASS.get(pluginParameter.getId());
if (parameterClass != null && RodaCoreFactory.getPluginManager().getPlugin(parameterClass) == null) {
perform = false;
}
}
return perform;
}
/***************** Plugin related *****************/
/**************************************************/
public static <T extends IsRODAObject> String getPluginAgentId(Plugin<T> plugin) {
return IdUtils.getPluginAgentId(plugin.getClass().getName(), plugin.getVersion());
}
public static void createSubmission(ModelService model, boolean createSubmission, Path submissionPath, String aipId)
throws AlreadyExistsException, GenericException, RequestNotValidException, NotFoundException,
AuthorizationDeniedException {
if (createSubmission) {
if (FSUtils.isDirectory(submissionPath)) {
StorageService submissionStorage = new FileStorageService(submissionPath);
StoragePath submissionStoragePath = DefaultStoragePath.empty();
model.createSubmission(submissionStorage, submissionStoragePath, aipId);
} else {
model.createSubmission(submissionPath, aipId);
}
}
}
public static Risk createRiskIfNotExists(ModelService model, String riskId, ClassLoader pluginClassLoader)
throws RequestNotValidException, GenericException, AuthorizationDeniedException, AlreadyExistsException,
NotFoundException {
try {
return model.retrieveRisk(riskId);
} catch (NotFoundException e) {
return createDefaultRisk(model, riskId, pluginClassLoader);
}
}
private static Risk createDefaultRisk(ModelService model, String riskId, ClassLoader pluginClassLoader)
throws AlreadyExistsException, GenericException, RequestNotValidException, NotFoundException,
AuthorizationDeniedException {
String defaultFile = RodaConstants.CORE_DATA_FOLDER + '/' + RodaConstants.CORE_STORAGE_FOLDER + '/'
+ RodaConstants.CORE_RISK_FOLDER + '/' + riskId + ".json";
InputStream inputStream = RodaCoreFactory.getDefaultFileAsStream(defaultFile, pluginClassLoader);
if (inputStream == null) {
throw new GenericException("Could not create a default risk because resource was not found: " + defaultFile);
}
Risk risk = null;
try {
risk = JsonUtils.getObjectFromJson(inputStream, Risk.class);
risk.setId(riskId);
model.createRisk(risk, false);
} catch (GenericException e) {
LOGGER.error("Could not create a default risk", e);
throw e;
} finally {
IOUtils.closeQuietly(inputStream);
}
return risk;
}
/***************** Preservation events related *****************/
/***************************************************************/
/**
* For SIP > AIP
*/
public static <T extends IsRODAObject> PreservationMetadata createPluginEvent(Plugin<T> plugin, String aipId,
ModelService model, IndexService index, TransferredResource source, PluginState outcome,
String outcomeDetailExtension, boolean notify, Date eventDate) throws RequestNotValidException, NotFoundException,
GenericException, AuthorizationDeniedException, ValidationException, AlreadyExistsException {
List<LinkingIdentifier> sources = Arrays
.asList(PluginHelper.getLinkingIdentifier(source, RodaConstants.PRESERVATION_LINKING_OBJECT_SOURCE));
List<LinkingIdentifier> outcomes = Arrays
.asList(PluginHelper.getLinkingIdentifier(aipId, RodaConstants.PRESERVATION_LINKING_OBJECT_OUTCOME));
return createPluginEvent(plugin, aipId, null, null, null, model, index, sources, outcomes, outcome,
outcomeDetailExtension, notify, eventDate);
}
public static <T extends IsRODAObject> PreservationMetadata createPluginEvent(Plugin<T> plugin, String aipId,
ModelService model, IndexService index, TransferredResource source, PluginState outcome,
String outcomeDetailExtension, boolean notify) throws RequestNotValidException, NotFoundException, GenericException,
AuthorizationDeniedException, ValidationException, AlreadyExistsException {
return createPluginEvent(plugin, aipId, model, index, source, outcome, outcomeDetailExtension, notify, new Date());
}
/**
* For AIP as source only
*/
public static <T extends IsRODAObject> PreservationMetadata createPluginEvent(Plugin<T> plugin, String aipId,
ModelService model, IndexService index, PluginState outcome, String outcomeDetailExtension, boolean notify)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException,
ValidationException, AlreadyExistsException {
List<LinkingIdentifier> sources = Arrays
.asList(PluginHelper.getLinkingIdentifier(aipId, RodaConstants.PRESERVATION_LINKING_OBJECT_OUTCOME));
List<LinkingIdentifier> outcomes = null;
return createPluginEvent(plugin, aipId, null, null, null, model, index, sources, outcomes, outcome,
outcomeDetailExtension, notify, new Date());
}
// used by migration plugin
public static <T extends IsRODAObject> PreservationMetadata createPluginEvent(Plugin<T> plugin, String aipId,
ModelService model, IndexService index, PluginState outcome, String outcomeDetailExtension, boolean notify,
Date eventDate) throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException,
ValidationException, AlreadyExistsException {
List<LinkingIdentifier> sources = Arrays
.asList(PluginHelper.getLinkingIdentifier(aipId, RodaConstants.PRESERVATION_LINKING_OBJECT_SOURCE));
List<LinkingIdentifier> outcomes = Arrays
.asList(PluginHelper.getLinkingIdentifier(aipId, RodaConstants.PRESERVATION_LINKING_OBJECT_OUTCOME));
return createPluginEvent(plugin, aipId, null, null, null, model, index, sources, outcomes, outcome,
outcomeDetailExtension, notify, eventDate);
}
/**
* For AIP
*/
public static <T extends IsRODAObject> PreservationMetadata createPluginEvent(Plugin<T> plugin, String aipId,
ModelService model, IndexService index, List<LinkingIdentifier> sources, List<LinkingIdentifier> targets,
PluginState outcome, String outcomeDetailExtension, boolean notify) throws RequestNotValidException,
NotFoundException, GenericException, AuthorizationDeniedException, ValidationException, AlreadyExistsException {
return createPluginEvent(plugin, aipId, null, null, null, model, index, sources, targets, outcome,
outcomeDetailExtension, notify, new Date());
}
/**
* For REPRESENTATION
*/
public static <T extends IsRODAObject> PreservationMetadata createPluginEvent(Plugin<T> plugin, String aipId,
String representationId, ModelService model, IndexService index, List<LinkingIdentifier> sources,
List<LinkingIdentifier> targets, PluginState outcome, String outcomeDetailExtension, boolean notify)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException,
ValidationException, AlreadyExistsException {
return createPluginEvent(plugin, aipId, representationId, null, null, model, index, sources, targets, outcome,
outcomeDetailExtension, notify, new Date());
}
/**
* For FILE
*/
public static <T extends IsRODAObject> PreservationMetadata createPluginEvent(Plugin<T> plugin, String aipId,
String representationId, List<String> filePath, String fileId, ModelService model, IndexService index,
List<LinkingIdentifier> sources, List<LinkingIdentifier> outcomes, PluginState outcome,
String outcomeDetailExtension, boolean notify) throws RequestNotValidException, NotFoundException, GenericException,
AuthorizationDeniedException, ValidationException, AlreadyExistsException {
return createPluginEvent(plugin, aipId, representationId, filePath, fileId, model, index, sources, outcomes,
outcome, outcomeDetailExtension, notify, new Date());
}
private static <T extends IsRODAObject> PreservationMetadata createPluginEvent(Plugin<T> plugin, String aipId,
String representationId, List<String> filePath, String fileId, ModelService model, IndexService index,
List<LinkingIdentifier> sources, List<LinkingIdentifier> outcomes, PluginState outcome,
String outcomeDetailExtension, boolean notify, Date startDate) throws RequestNotValidException, NotFoundException,
GenericException, AuthorizationDeniedException, ValidationException, AlreadyExistsException {
List<String> agentIds = new ArrayList<>();
try {
boolean notifyAgent = true;
PreservationMetadata pm = PremisV3Utils.createPremisAgentBinary(plugin, model, notifyAgent);
agentIds.add(pm.getId());
} catch (AlreadyExistsException e) {
agentIds.add(IdUtils.getPluginAgentId(plugin.getClass().getName(), plugin.getVersion()));
} catch (RODAException e) {
LOGGER.error("Error creating PREMIS agent", e);
}
Job job;
try {
job = getJob(plugin, index);
} catch (NotFoundException e) {
job = null;
}
if (job != null) {
try {
boolean notifyAgent = true;
PreservationMetadata pm = PremisV3Utils.createPremisUserAgentBinary(job.getUsername(), model, index,
notifyAgent);
if (pm != null) {
agentIds.add(pm.getId());
}
} catch (AlreadyExistsException e) {
agentIds.add(IdUtils.getUserAgentId(job.getUsername()));
} catch (RODAException e) {
LOGGER.error("Error creating PREMIS agent", e);
}
}
String id = IdUtils.createPreservationMetadataId(PreservationMetadataType.EVENT);
String outcomeDetailNote = (outcome == PluginState.SUCCESS) ? plugin.getPreservationEventSuccessMessage()
: plugin.getPreservationEventFailureMessage();
ContentPayload premisEvent = PremisV3Utils.createPremisEventBinary(id, startDate,
plugin.getPreservationEventType().toString(), plugin.getPreservationEventDescription(), sources, outcomes,
outcome.name(), outcomeDetailNote, outcomeDetailExtension, agentIds);
model.createPreservationMetadata(PreservationMetadataType.EVENT, id, aipId, representationId, filePath, fileId,
premisEvent, notify);
PreservationMetadata pm = new PreservationMetadata();
pm.setId(id);
pm.setAipId(aipId);
pm.setRepresentationId(representationId);
pm.setFileDirectoryPath(filePath);
pm.setFileId(fileId);
pm.setType(PreservationMetadataType.EVENT);
return pm;
}
public static LinkingIdentifier getLinkingIdentifier(TransferredResource transferredResource, String role) {
LinkingIdentifier li = new LinkingIdentifier();
li.setValue(LinkingObjectUtils.getLinkingIdentifierId(transferredResource));
li.setType("URN");
li.setRoles(Arrays.asList(role));
return li;
}
public static LinkingIdentifier getLinkingIdentifier(String aipId, String role) {
return getLinkingIdentifier(RODA_TYPE.AIP, aipId, role);
}
public static LinkingIdentifier getLinkingIdentifier(String aipId, String representationId, String role) {
return getLinkingIdentifier(RODA_TYPE.REPRESENTATION, IdUtils.getRepresentationId(aipId, representationId), role);
}
public static LinkingIdentifier getLinkingIdentifier(String aipId, String representationId, List<String> filePath,
String fileId, String role) {
return getLinkingIdentifier(RODA_TYPE.FILE, IdUtils.getFileId(aipId, representationId, filePath, fileId), role);
}
private static LinkingIdentifier getLinkingIdentifier(RODA_TYPE type, String uuid, String role) {
LinkingIdentifier li = new LinkingIdentifier();
li.setValue(LinkingObjectUtils.getLinkingIdentifierId(type, uuid));
li.setType("URN");
li.setRoles(Arrays.asList(role));
return li;
}
public static <T extends IsRODAObject> void moveSIPs(Plugin<T> plugin, ModelService model, IndexService index,
List<TransferredResource> transferredResources, IngestJobPluginInfo jobPluginInfo) {
List<String> success = new ArrayList<>();
List<String> unsuccess = new ArrayList<>();
String baseFolder = RodaCoreFactory.getRodaConfiguration().getString("core.ingest.processed.base_folder",
"PROCESSED");
String successFolder = RodaCoreFactory.getRodaConfiguration()
.getString("core.ingest.processed.successfully_ingested", "SUCCESSFULLY_INGESTED");
String unsuccessFolder = RodaCoreFactory.getRodaConfiguration()
.getString("core.ingest.processed.unsuccessfully_ingested", "UNSUCCESSFULLY_INGESTED");
String successPath = Paths.get(baseFolder, successFolder).toString();
String unsuccessPath = Paths.get(baseFolder, unsuccessFolder).toString();
// determine which SIPs will be moved based on 1) if at least one AIP was
// created from each SIP; 2) if it was created, in which state it is
for (TransferredResource transferredResource : transferredResources) {
String transferredResourceId = transferredResource.getUUID();
List<String> aipIds = jobPluginInfo.getAipIds(transferredResourceId);
if (aipIds != null && !aipIds.isEmpty()) {
try {
AIPState aipState = model.retrieveAIP(aipIds.get(0)).getState();
if (AIPState.ACTIVE == aipState) {
success.add(transferredResourceId);
} else if (AIPState.UNDER_APPRAISAL != aipState) {
unsuccess.add(transferredResourceId);
}
} catch (RequestNotValidException | NotFoundException | GenericException | AuthorizationDeniedException e) {
LOGGER.error("Error retrieving AIP", e);
}
} else {
// no AIP was generated
unsuccess.add(transferredResourceId);
}
}
// move SIPs and update reports
Map<String, String> successOldToNewTransferredResourceIds = new HashMap<>();
Map<String, String> unsuccessOldToNewTransferredResourceIds = new HashMap<>();
try {
if (!success.isEmpty()) {
successOldToNewTransferredResourceIds = RodaCoreFactory.getTransferredResourcesScanner()
.moveTransferredResource(successPath, success, true);
updateReportsAfterMovingSIPs(model, jobPluginInfo, successOldToNewTransferredResourceIds);
}
} catch (AlreadyExistsException | GenericException | NotFoundException e) {
LOGGER.error("Error moving successfully ingested SIPs", e);
} catch (IsStillUpdatingException e) {
LOGGER.warn("TransferredResources are already being indexed");
}
try {
if (!unsuccess.isEmpty()) {
unsuccessOldToNewTransferredResourceIds = RodaCoreFactory.getTransferredResourcesScanner()
.moveTransferredResource(unsuccessPath, unsuccess, true);
updateReportsAfterMovingSIPs(model, jobPluginInfo, unsuccessOldToNewTransferredResourceIds);
}
} catch (AlreadyExistsException | GenericException | NotFoundException e) {
LOGGER.error("Error moving unsuccessfully ingested SIPs", e);
} catch (IsStillUpdatingException e) {
LOGGER.warn("TransferredResources are already being indexed");
}
// update Job (with all new ids)
successOldToNewTransferredResourceIds.putAll(unsuccessOldToNewTransferredResourceIds);
updateJobAfterMovingSIPs(plugin, index, successOldToNewTransferredResourceIds);
}
private static void updateReportsAfterMovingSIPs(ModelService model, IngestJobPluginInfo jobPluginInfo,
Map<String, String> oldToNewTransferredResourceIds) {
// FIXME 20161017 hsilva: old sip ids should be updated in
// IngestJobPluginInfo
for (Entry<String, String> oldToNewId : oldToNewTransferredResourceIds.entrySet()) {
String oldSIPId = oldToNewId.getKey();
String newSIPId = oldToNewId.getValue();
for (Report report : jobPluginInfo.getAllReports().getOrDefault(oldSIPId, Collections.emptyMap()).values()) {
report.setSourceObjectId(newSIPId);
if (!report.getReports().isEmpty()) {
report.getReports().get(0).setSourceObjectId(newSIPId);
}
// update in model
updateJobReport(model, report);
}
}
}
/**
* Update Job source object ids (done asynchronously)
*/
private static <T extends IsRODAObject> void updateJobAfterMovingSIPs(Plugin<T> plugin, IndexService index,
Map<String, String> oldToNewTransferredResourceIds) {
try {
Job job = getJob(plugin, index);
SelectedItems<?> sourceObjects = job.getSourceObjects();
if (sourceObjects instanceof SelectedItemsList) {
RodaCoreFactory.getPluginOrchestrator().updateJob(plugin,
new Messages.JobSourceObjectsUpdated(oldToNewTransferredResourceIds));
}
} catch (NotFoundException | GenericException e) {
LOGGER.error("Error retrieving Job", e);
}
}
public static void fixParents(IndexService index, ModelService model, Optional<String> jobId,
Optional<String> computedSearchScope)
throws GenericException, RequestNotValidException, AuthorizationDeniedException, NotFoundException {
// collect all ghost ids
Map<String, List<String>> aipIdToGhost = new HashMap<>();
Map<String, List<String>> sipIdToGhost = new HashMap<>();
Filter ghostsFilter = new Filter(new SimpleFilterParameter(RodaConstants.AIP_GHOST, Boolean.TRUE.toString()));
jobId.ifPresent(id -> ghostsFilter.add(new SimpleFilterParameter(RodaConstants.INGEST_JOB_ID, id)));
IterableIndexResult<IndexedAIP> ghosts = index.findAll(IndexedAIP.class, ghostsFilter,
Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.INGEST_SIP_IDS));
for (IndexedAIP aip : ghosts) {
if (aip.getIngestSIPIds() != null && !aip.getIngestSIPIds().isEmpty()) {
List<String> temp = new ArrayList<>();
String firstIngestSIPId = aip.getIngestSIPIds().get(0);
if (sipIdToGhost.containsKey(firstIngestSIPId)) {
temp = sipIdToGhost.get(firstIngestSIPId);
}
temp.add(aip.getId());
sipIdToGhost.put(firstIngestSIPId, temp);
} else {
List<String> temp = new ArrayList<>();
if (aipIdToGhost.containsKey(aip.getId())) {
temp = aipIdToGhost.get(aip.getId());
}
temp.add(aip.getId());
aipIdToGhost.put(aip.getId(), temp);
}
}
for (Map.Entry<String, List<String>> entry : sipIdToGhost.entrySet()) {
Filter nonGhostsFilter = new Filter(
new OneOfManyFilterParameter(RodaConstants.INGEST_SIP_IDS, Arrays.asList(entry.getKey())),
new SimpleFilterParameter(RodaConstants.AIP_GHOST, Boolean.FALSE.toString()));
computedSearchScope
.ifPresent(id -> nonGhostsFilter.add(new SimpleFilterParameter(RodaConstants.AIP_ANCESTORS, id)));
IndexResult<IndexedAIP> result = index.find(IndexedAIP.class, nonGhostsFilter, Sorter.NONE, new Sublist(0, 1),
Arrays.asList(RodaConstants.INDEX_UUID));
if (result.getTotalCount() > 1) {
LOGGER.debug("Couldn't find non-ghost AIP with ingest SIP ids {}", entry.getKey());
} else if (result.getTotalCount() == 1) {
IndexedAIP newParentIAIP = result.getResults().get(0);
for (String id : entry.getValue()) {
moveChildrenAIPsAndDelete(index, model, id, newParentIAIP.getId(), computedSearchScope);
}
} else if (result.getTotalCount() == 0) {
String ghostIdToKeep = entry.getValue().get(0);
AIP ghostToKeep = model.retrieveAIP(ghostIdToKeep);
ghostToKeep.setGhost(false);
model.updateAIP(ghostToKeep, "test");
if (entry.getValue().size() > 1) {
for (int i = 1; i < entry.getValue().size(); i++) {
updateParent(index, model, entry.getValue().get(i), ghostIdToKeep, computedSearchScope);
}
}
}
}
}
private static void updateParent(IndexService index, ModelService model, String aipId, String newParentId,
Optional<String> searchScope) throws GenericException, AuthorizationDeniedException, RequestNotValidException {
Filter parentFilter = new Filter(new SimpleFilterParameter(RodaConstants.AIP_PARENT_ID, aipId));
searchScope.ifPresent(id -> parentFilter.add(new SimpleFilterParameter(RodaConstants.AIP_ANCESTORS, id)));
index.execute(IndexedAIP.class, parentFilter, Arrays.asList(RodaConstants.INDEX_UUID), child -> {
try {
AIP aip = model.retrieveAIP(child.getId());
aip.setParentId(newParentId);
model.updateAIP(aip, "test");
} catch (NotFoundException e) {
LOGGER.debug("Can't move child. It wasn't found.", e);
}
});
try {
model.deleteAIP(aipId);
} catch (NotFoundException e) {
LOGGER.debug("Can't delete ghost or move node. It wasn't found.", e);
}
}
private static void moveChildrenAIPsAndDelete(IndexService index, ModelService model, String aipId,
String newParentId, Optional<String> searchScope)
throws GenericException, AuthorizationDeniedException, RequestNotValidException {
Filter parentFilter = new Filter(new SimpleFilterParameter(RodaConstants.AIP_PARENT_ID, aipId));
searchScope.ifPresent(id -> parentFilter.add(new SimpleFilterParameter(RodaConstants.AIP_ANCESTORS, id)));
index.execute(IndexedAIP.class, parentFilter, Arrays.asList(RodaConstants.INDEX_UUID), child -> {
try {
model.moveAIP(child.getId(), newParentId);
} catch (NotFoundException e) {
LOGGER.debug("Can't move child. It wasn't found.", e);
}
});
try {
model.deleteAIP(aipId);
} catch (NotFoundException e) {
LOGGER.debug("Can't delete ghost or move node. It wasn't found.", e);
}
}
public static void createAndExecuteJob(Job job) throws GenericException, JobAlreadyStartedException,
RequestNotValidException, NotFoundException, AuthorizationDeniedException {
RodaCoreFactory.getModelService().createJob(job);
RodaCoreFactory.getPluginOrchestrator().executeJob(job, true);
RodaCoreFactory.getIndexService().commit(Job.class);
}
public static String getReindexPluginName(Class<?> reindexClass) throws NotFoundException {
if (reindexClass.equals(AIP.class)) {
return ReindexAIPPlugin.class.getName();
} else if (reindexClass.equals(Format.class)) {
return ReindexFormatPlugin.class.getName();
} else if (reindexClass.equals(Risk.class)) {
return ReindexRiskPlugin.class.getName();
} else if (reindexClass.equals(RiskIncidence.class)) {
return ReindexIncidencePlugin.class.getName();
} else if (reindexClass.equals(Job.class)) {
return ReindexJobPlugin.class.getName();
} else if (reindexClass.equals(Notification.class)) {
return ReindexNotificationPlugin.class.getName();
} else if (reindexClass.equals(TransferredResource.class)) {
return ReindexTransferredResourcePlugin.class.getName();
} else if (reindexClass.equals(RODAMember.class)) {
return ReindexRodaMemberPlugin.class.getName();
} else if (reindexClass.equals(LogEntry.class)) {
return ReindexActionLogPlugin.class.getName();
} else if (reindexClass.equals(IndexedPreservationAgent.class)) {
return ReindexPreservationAgentPlugin.class.getName();
} else if (reindexClass.equals(IndexedPreservationEvent.class)) {
return ReindexPreservationRepositoryEventPlugin.class.getName();
} else if (reindexClass.equals(DIP.class)) {
return ReindexDIPPlugin.class.getName();
} else {
throw new NotFoundException("No reindex plugin available");
}
}
public static <T extends IsRODAObject> List<T> transformLitesIntoObjects(ModelService model, Plugin<T> plugin,
Report report, JobPluginInfo pluginInfo, List<LiteOptionalWithCause> lites, Job job) {
List<T> finalObjects = new ArrayList<>();
for (LiteOptionalWithCause lite : lites) {
String failureMessage = "";
Optional<LiteRODAObject> optionalLite = lite.getLite();
if (optionalLite.isPresent() && StringUtils.isNotBlank(optionalLite.get().getInfo())) {
boolean objectMatchPluginKnownObjectsClass = false;
String liteString = optionalLite.get().getInfo();
for (Class<T> pluginClass : plugin.getObjectClasses()) {
if (liteString.startsWith(pluginClass.getName())) {
objectMatchPluginKnownObjectsClass = true;
break;
}
}
if (objectMatchPluginKnownObjectsClass) {
OptionalWithCause<T> retrievedObject = (OptionalWithCause<T>) model
.retrieveObjectFromLite(optionalLite.get());
if (retrievedObject.isPresent()) {
finalObjects.add(retrievedObject.get());
} else {
RODAException exception = retrievedObject.getCause();
if (exception != null) {
failureMessage = "RODA object conversion from lite throwed an error: [" + exception.getClass().getName()
+ "] " + exception.getMessage();
} else {
failureMessage = "RODA object conversion from lite throwed an error.";
}
}
} else {
failureMessage = "RODA object conversion from lite has failed because lite object class does not match any of the plugin known object classes "
+ "(which might be caused by blank lite)";
}
} else {
failureMessage = "Lite object has an error: [" + lite.getExceptionClass() + "] " + lite.getExceptionMessage();
}
if (StringUtils.isNotBlank(failureMessage)) {
if (pluginInfo != null) {
pluginInfo.incrementObjectsProcessedWithFailure();
}
if (report != null) {
String id = lite.toString();
if (optionalLite.isPresent()) {
String[] split = optionalLite.get().getInfo().split(LiteRODAObjectFactory.SEPARATOR_REGEX);
id = split[1];
}
Report reportItem = PluginHelper.initPluginReportItem(plugin, id, LiteRODAObject.class);
reportItem.setPluginState(PluginState.FAILURE).setPluginDetails(failureMessage);
report.addReport(reportItem);
PluginHelper.updatePartialJobReport(plugin, model, reportItem, true, job);
}
}
}
return finalObjects;
}
public static String createOutcomeTextForAIP(IndexedAIP item, String actionMessage) {
SimpleDateFormat format = new SimpleDateFormat(RodaConstants.SIMPLE_DATE_FORMATTER);
StringBuilder outcomeText = new StringBuilder("Archival Information Package [id: ").append(item.getId());
if (StringUtils.isNotBlank(item.getTitle())) {
outcomeText.append("; title: ").append(item.getTitle());
}
if (StringUtils.isNotBlank(item.getLevel())) {
outcomeText.append("; level: ").append(item.getLevel());
}
if (item.getDateInitial() != null) {
outcomeText.append("; initial date: ").append(format.format(item.getDateInitial()));
}
if (item.getDateFinal() != null) {
outcomeText.append("; end date: ").append(format.format(item.getDateFinal()));
}
outcomeText.append("] ").append(actionMessage);
return outcomeText.toString();
}
}