/** * 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.risks; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; 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.InvalidParameterException; import org.roda.core.data.exceptions.JobException; 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.LiteOptionalWithCause; import org.roda.core.data.v2.common.Pair; import org.roda.core.data.v2.ip.AIP; 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.PluginParameter; import org.roda.core.data.v2.jobs.PluginParameter.PluginParameterType; 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.risks.Risk; import org.roda.core.data.v2.risks.Risk.SEVERITY_LEVEL; import org.roda.core.data.v2.risks.RiskIncidence; import org.roda.core.data.v2.risks.RiskIncidence.INCIDENCE_STATUS; 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.AbstractPlugin; 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; /*** * https://docs.google.com/spreadsheets/d/ * 1Ncu0My6tf19umSClIA6iXeYlJ4_FP6MygRwFCe0EzyM * * @author Hélder Silva <hsilva@keep.pt> */ public class RiskAssociationPlugin<T extends IsRODAObject> extends AbstractPlugin<T> { private static final Logger LOGGER = LoggerFactory.getLogger(RiskAssociationPlugin.class); private String riskIds = null; private String incidenceDescription = ""; private String severity = ""; private static Map<String, PluginParameter> pluginParameters = new HashMap<>(); static { pluginParameters.put(RodaConstants.PLUGIN_PARAMS_RISK_ID, new PluginParameter(RodaConstants.PLUGIN_PARAMS_RISK_ID, "Risks", PluginParameterType.RISK_ID, "", false, false, "Add the risks that will be associated with the objects above.")); pluginParameters.put(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_DESCRIPTION, new PluginParameter(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_DESCRIPTION, "Incidence description", PluginParameterType.STRING, "", false, false, "Associate a description to the incidence(s) created")); pluginParameters.put(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_SEVERITY, new PluginParameter(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_SEVERITY, "Incidence severity", PluginParameterType.SEVERITY, "", false, false, "Associate a severity to the incidence")); } @Override public String getName() { return "Risk association"; } @Override public String getDescription() { return "Associates selected items to existing risks in the Risk registry (as risk incidences).\nThis task is convenient when the preservation " + "expert wants to associate a set of items (e.g. AIPs, representations or files) to a risk to be mitigated in the near future.\nAs an example, " + "if the designated community of the repository provides feedback that a given format under a certain collection is not being displayed properly " + "on the graphical user interface of the repository, then the preservation expert may want to mark these files to be targeted by a preservation" + " action (e.g. generate new representations for access purposes)."; } @Override public void setParameterValues(Map<String, String> parameters) throws InvalidParameterException { super.setParameterValues(parameters); if (parameters.containsKey(RodaConstants.PLUGIN_PARAMS_RISK_ID)) { riskIds = parameters.get(RodaConstants.PLUGIN_PARAMS_RISK_ID); } if (parameters.containsKey(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_DESCRIPTION)) { incidenceDescription = parameters.get(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_DESCRIPTION); } if (parameters.containsKey(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_SEVERITY)) { severity = parameters.get(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_SEVERITY); } } @Override public List<PluginParameter> getParameters() { ArrayList<PluginParameter> parameters = new ArrayList<>(); parameters.add(pluginParameters.get(RodaConstants.PLUGIN_PARAMS_RISK_ID)); parameters.add(pluginParameters.get(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_DESCRIPTION)); parameters.add(pluginParameters.get(RodaConstants.PLUGIN_PARAMS_RISK_INCIDENCE_SEVERITY)); return parameters; } @Override public void init() throws PluginException { // do nothing } @Override public void shutdown() { // do nothing } @Override public Report beforeAllExecute(IndexService index, ModelService model, StorageService storage) throws PluginException { // do nothing return null; } @Override public Report execute(IndexService index, ModelService model, StorageService storage, List<LiteOptionalWithCause> liteList) throws PluginException { LOGGER.debug("Creating risk incidences"); Report pluginReport = PluginHelper.initPluginReport(this); try { SimpleJobPluginInfo jobPluginInfo = PluginHelper.getInitialJobInformation(this, liteList.size()); PluginHelper.updateJobInformation(this, jobPluginInfo); Job job = PluginHelper.getJob(this, model); List<T> list = PluginHelper.transformLitesIntoObjects(model, this, pluginReport, jobPluginInfo, liteList, job); if (!list.isEmpty() && riskIds != null) { List<String> risks = Arrays.asList(riskIds.split(",")); Pair<SimpleJobPluginInfo, Report> jobInfo = new Pair<>(); if (list.get(0) instanceof AIP) { jobInfo = addIncidenceToAIPList(model, index, (List<AIP>) list, risks, jobPluginInfo, pluginReport, job); } else if (list.get(0) instanceof Representation) { jobInfo = addIncidenceToRepresentationList(model, index, (List<Representation>) list, risks, jobPluginInfo, pluginReport, job); } else if (list.get(0) instanceof File) { jobInfo = addIncidenceToFileList(model, index, (List<File>) list, risks, jobPluginInfo, pluginReport, job); } jobPluginInfo = jobInfo.getFirst(); pluginReport = jobInfo.getSecond(); } jobPluginInfo.finalizeInfo(); PluginHelper.updateJobInformation(this, jobPluginInfo); } catch (JobException | AuthorizationDeniedException | NotFoundException | GenericException | RequestNotValidException e) { throw new PluginException("A job exception has occurred", e); } return pluginReport; } @Override public Report afterAllExecute(IndexService index, ModelService model, StorageService storage) throws PluginException { try { index.commit(RiskIncidence.class); } catch (GenericException e) { LOGGER.error("Error commiting risk incidences to index"); } return new Report(); } private Pair<SimpleJobPluginInfo, Report> addIncidenceToAIPList(ModelService model, IndexService index, List<AIP> list, List<String> risks, SimpleJobPluginInfo jobPluginInfo, Report pluginReport, Job job) throws JobException { for (AIP aip : list) { PluginState state = PluginState.SUCCESS; for (String riskId : risks) { try { RiskIncidence incidence = new RiskIncidence(); incidence.setDetectedOn(new Date()); incidence.setDetectedBy(job.getUsername()); incidence.setRiskId(riskId); incidence.setAipId(aip.getId()); incidence.setObjectClass(AIP.class.getSimpleName()); incidence.setStatus(INCIDENCE_STATUS.UNMITIGATED); incidence.setSeverity(Risk.SEVERITY_LEVEL.valueOf(severity)); incidence.setDescription(incidenceDescription); model.createRiskIncidence(incidence, false); } catch (GenericException e) { state = PluginState.FAILURE; } } jobPluginInfo.incrementObjectsProcessed(state); Report reportItem = PluginHelper.initPluginReportItem(this, aip.getId(), AIP.class); reportItem.setPluginState(state).setPluginDetails("Risk job plugin ran on an AIP"); pluginReport.addReport(reportItem); PluginHelper.updatePartialJobReport(this, model, reportItem, true, job); try { PluginHelper.createPluginEvent(this, aip.getId(), model, index, state, "", true); } catch (RequestNotValidException | NotFoundException | GenericException | AuthorizationDeniedException | ValidationException | AlreadyExistsException e) { LOGGER.error("Could not create a risk job plugin event"); } } return Pair.of(jobPluginInfo, pluginReport); } private Pair<SimpleJobPluginInfo, Report> addIncidenceToRepresentationList(ModelService model, IndexService index, List<Representation> list, List<String> risks, SimpleJobPluginInfo jobPluginInfo, Report pluginReport, Job job) throws JobException { for (Representation representation : list) { PluginState state = PluginState.SUCCESS; for (String riskId : risks) { try { RiskIncidence incidence = new RiskIncidence(); incidence.setDetectedOn(new Date()); incidence.setDetectedBy(job.getUsername()); incidence.setRiskId(riskId); incidence.setAipId(representation.getAipId()); incidence.setRepresentationId(representation.getId()); incidence.setObjectClass(Representation.class.getSimpleName()); incidence.setStatus(INCIDENCE_STATUS.UNMITIGATED); incidence.setSeverity(SEVERITY_LEVEL.valueOf(severity)); model.createRiskIncidence(incidence, false); } catch (GenericException e) { state = PluginState.FAILURE; } } jobPluginInfo.incrementObjectsProcessed(state); Report reportItem = PluginHelper.initPluginReportItem(this, IdUtils.getRepresentationId(representation), Representation.class); reportItem.setPluginState(state).setPluginDetails("Risk job plugin ran on a representation"); pluginReport.addReport(reportItem); PluginHelper.updatePartialJobReport(this, model, reportItem, true, job); try { PluginHelper.createPluginEvent(this, representation.getAipId(), representation.getId(), model, index, null, null, state, "", true); } catch (RequestNotValidException | NotFoundException | GenericException | AuthorizationDeniedException | ValidationException | AlreadyExistsException e) { LOGGER.error("Could not create a risk job plugin event"); } } return Pair.of(jobPluginInfo, pluginReport); } private Pair<SimpleJobPluginInfo, Report> addIncidenceToFileList(ModelService model, IndexService index, List<File> list, List<String> risks, SimpleJobPluginInfo jobPluginInfo, Report pluginReport, Job job) throws JobException { for (File file : list) { PluginState state = PluginState.SUCCESS; for (String riskId : risks) { try { RiskIncidence incidence = new RiskIncidence(); incidence.setDetectedOn(new Date()); incidence.setDetectedBy(job.getUsername()); incidence.setRiskId(riskId); incidence.setAipId(file.getAipId()); incidence.setRepresentationId(file.getRepresentationId()); incidence.setFilePath(file.getPath()); incidence.setFileId(file.getId()); incidence.setObjectClass(File.class.getSimpleName()); incidence.setStatus(INCIDENCE_STATUS.UNMITIGATED); incidence.setSeverity(SEVERITY_LEVEL.valueOf(severity)); model.createRiskIncidence(incidence, false); } catch (GenericException e) { state = PluginState.FAILURE; } } jobPluginInfo.incrementObjectsProcessed(state); Report reportItem = PluginHelper.initPluginReportItem(this, IdUtils.getFileId(file), File.class); reportItem.setPluginState(state).setPluginDetails("Risk job plugin ran on a file"); pluginReport.addReport(reportItem); PluginHelper.updatePartialJobReport(this, model, reportItem, true, job); try { PluginHelper.createPluginEvent(this, file.getAipId(), file.getRepresentationId(), file.getPath(), file.getId(), model, index, null, null, state, "", true); } catch (RequestNotValidException | NotFoundException | GenericException | AuthorizationDeniedException | ValidationException | AlreadyExistsException e) { LOGGER.error("Could not create a risk job plugin event"); } } return Pair.of(jobPluginInfo, pluginReport); } @Override public PluginType getType() { return PluginType.MISC; } @Override public boolean areParameterValuesValid() { return true; } @Override public Plugin<T> cloneMe() { return new RiskAssociationPlugin<>(); } @Override public String getVersionImpl() { return "1.0"; } @Override public PreservationEventType getPreservationEventType() { return PreservationEventType.RISK_MANAGEMENT; } @Override public String getPreservationEventDescription() { return getDescription(); } @Override public String getPreservationEventSuccessMessage() { return "Risk was successfully associated with objects"; } @Override public String getPreservationEventFailureMessage() { return "Risk was not successfully associated with objects"; } @Override public List<String> getCategories() { return Arrays.asList(RodaConstants.PLUGIN_CATEGORY_RISK_MANAGEMENT); } @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; } }