/**
* 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.common;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.common.SolrInputDocument;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlValidationError;
import org.roda.core.RodaCoreFactory;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.common.RodaConstants.PreservationAgentType;
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.utils.URNUtils;
import org.roda.core.data.v2.ip.File;
import org.roda.core.data.v2.ip.metadata.Fixity;
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.user.RODAMember;
import org.roda.core.data.v2.user.User;
import org.roda.core.data.v2.validation.ValidationException;
import org.roda.core.index.IndexService;
import org.roda.core.model.ModelService;
import org.roda.core.model.utils.ModelUtils;
import org.roda.core.plugins.Plugin;
import org.roda.core.plugins.plugins.characterization.PremisSkeletonPluginUtils;
import org.roda.core.storage.Binary;
import org.roda.core.storage.ContentPayload;
import org.roda.core.util.FileUtility;
import org.roda.core.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.util.DateParser;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import gov.loc.premis.v3.AgentComplexType;
import gov.loc.premis.v3.AgentDocument;
import gov.loc.premis.v3.AgentIdentifierComplexType;
import gov.loc.premis.v3.ContentLocationComplexType;
import gov.loc.premis.v3.CreatingApplicationComplexType;
import gov.loc.premis.v3.EventComplexType;
import gov.loc.premis.v3.EventDetailInformationComplexType;
import gov.loc.premis.v3.EventDocument;
import gov.loc.premis.v3.EventIdentifierComplexType;
import gov.loc.premis.v3.EventOutcomeDetailComplexType;
import gov.loc.premis.v3.EventOutcomeInformationComplexType;
import gov.loc.premis.v3.FixityComplexType;
import gov.loc.premis.v3.FormatComplexType;
import gov.loc.premis.v3.FormatDesignationComplexType;
import gov.loc.premis.v3.FormatRegistryComplexType;
import gov.loc.premis.v3.LinkingAgentIdentifierComplexType;
import gov.loc.premis.v3.LinkingObjectIdentifierComplexType;
import gov.loc.premis.v3.ObjectCharacteristicsComplexType;
import gov.loc.premis.v3.ObjectComplexType;
import gov.loc.premis.v3.ObjectDocument;
import gov.loc.premis.v3.ObjectIdentifierComplexType;
import gov.loc.premis.v3.RelatedObjectIdentifierComplexType;
import gov.loc.premis.v3.RelationshipComplexType;
import gov.loc.premis.v3.Representation;
import gov.loc.premis.v3.StorageComplexType;
import gov.loc.premis.v3.StringPlusAuthority;
public final class PremisV3Utils {
private static final Logger LOGGER = LoggerFactory.getLogger(PremisV3Utils.class);
private static final String FIXITY_ORIGINATOR = "RODA";
private static final String W3C_XML_SCHEMA_NS_URI = "http://www.w3.org/2001/XMLSchema";
/** Private empty constructor */
private PremisV3Utils() {
// do nothing
}
public static List<Fixity> calculateFixities(Binary binary, Collection<String> algorithms, String originator)
throws IOException, NoSuchAlgorithmException {
List<Fixity> ret = new ArrayList<>();
InputStream stream = binary.getContent().createInputStream();
Map<String, String> checksums = FileUtility.checksums(stream, algorithms);
for (Entry<String, String> entry : checksums.entrySet()) {
String algorithm = entry.getKey();
String checksum = entry.getValue();
ret.add(new Fixity(algorithm, checksum, originator));
}
IOUtils.closeQuietly(stream);
return ret;
}
public static boolean isPremisV2(Binary binary) throws IOException, SAXException {
boolean premisV2 = true;
InputStream inputStream = binary.getContent().createInputStream();
InputStream schemaStream = RodaCoreFactory.getConfigurationFileAsStream("schemas/premis-v2-0.xsd");
Source xmlFile = new StreamSource(inputStream);
SchemaFactory schemaFactory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new StreamSource(schemaStream));
Validator validator = schema.newValidator();
RodaErrorHandler errorHandler = new RodaErrorHandler();
validator.setErrorHandler(errorHandler);
try {
validator.validate(xmlFile);
List<SAXParseException> errors = errorHandler.getErrors();
if (!errors.isEmpty()) {
premisV2 = false;
}
} catch (SAXException e) {
premisV2 = false;
}
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(schemaStream);
return premisV2;
}
private static class RodaErrorHandler extends DefaultHandler {
List<SAXParseException> errors;
public RodaErrorHandler() {
errors = new ArrayList<>();
}
@Override
public void warning(SAXParseException e) throws SAXException {
errors.add(e);
}
@Override
public void error(SAXParseException e) throws SAXException {
errors.add(e);
}
@Override
public void fatalError(SAXParseException e) throws SAXException {
errors.add(e);
}
public List<SAXParseException> getErrors() {
return errors;
}
}
public static void updateFileFormat(gov.loc.premis.v3.File file, String formatDesignationName,
String formatDesignationVersion, String pronom, String mimeType) {
if (StringUtils.isNotBlank(formatDesignationName)) {
FormatDesignationComplexType fdct = getFormatDesignation(file);
fdct.setFormatName(getStringPlusAuthority(formatDesignationName));
}
if (StringUtils.isNotBlank(formatDesignationVersion)) {
FormatDesignationComplexType fdct = getFormatDesignation(file);
fdct.setFormatVersion(formatDesignationVersion);
}
if (StringUtils.isNotBlank(pronom)) {
FormatRegistryComplexType frct = getFormatRegistry(file, RodaConstants.PRESERVATION_REGISTRY_PRONOM);
frct.setFormatRegistryKey(getStringPlusAuthority(pronom));
}
if (StringUtils.isNotBlank(mimeType)) {
FormatRegistryComplexType frct = getFormatRegistry(file, RodaConstants.PRESERVATION_REGISTRY_MIME);
frct.setFormatRegistryKey(getStringPlusAuthority(mimeType));
}
}
public static void updateCreatingApplication(gov.loc.premis.v3.File file, String creatingApplicationName,
String creatingApplicationVersion, String dateCreatedByApplication) {
if (StringUtils.isNotBlank(creatingApplicationName)) {
CreatingApplicationComplexType cact = getCreatingApplication(file);
cact.setCreatingApplicationName(getStringPlusAuthority(creatingApplicationName));
}
if (StringUtils.isNotBlank(creatingApplicationVersion)) {
CreatingApplicationComplexType cact = getCreatingApplication(file);
cact.setCreatingApplicationVersion(creatingApplicationVersion);
}
if (StringUtils.isNotBlank(dateCreatedByApplication)) {
CreatingApplicationComplexType cact = getCreatingApplication(file);
cact.setDateCreatedByApplication(dateCreatedByApplication);
}
}
private static CreatingApplicationComplexType getCreatingApplication(gov.loc.premis.v3.File f) {
ObjectCharacteristicsComplexType occt;
CreatingApplicationComplexType cact;
if (f.getObjectCharacteristicsArray() != null && f.getObjectCharacteristicsArray().length > 0) {
occt = f.getObjectCharacteristicsArray(0);
} else {
occt = f.addNewObjectCharacteristics();
}
if (occt.getCreatingApplicationArray() != null && occt.getCreatingApplicationArray().length > 0) {
cact = occt.getCreatingApplicationArray(0);
} else {
cact = occt.addNewCreatingApplication();
}
return cact;
}
public static FormatRegistryComplexType getFormatRegistry(gov.loc.premis.v3.File f, String registryName) {
ObjectCharacteristicsComplexType occt;
FormatRegistryComplexType frct = null;
if (f.getObjectCharacteristicsArray() != null && f.getObjectCharacteristicsArray().length > 0) {
occt = f.getObjectCharacteristicsArray(0);
} else {
occt = f.addNewObjectCharacteristics();
}
if (occt.getFormatArray() != null && occt.getFormatArray().length > 0) {
for (FormatComplexType fct : occt.getFormatArray()) {
if (fct.getFormatRegistry() != null
&& fct.getFormatRegistry().getFormatRegistryName().getStringValue().equalsIgnoreCase(registryName)) {
frct = fct.getFormatRegistry();
break;
}
}
if (frct == null) {
FormatComplexType fct = occt.addNewFormat();
frct = fct.addNewFormatRegistry();
frct.setFormatRegistryName(getStringPlusAuthority(registryName));
}
} else {
FormatComplexType fct = occt.addNewFormat();
frct = fct.addNewFormatRegistry();
frct.setFormatRegistryName(getStringPlusAuthority(registryName));
}
return frct;
}
private static FormatDesignationComplexType getFormatDesignation(gov.loc.premis.v3.File f) {
ObjectCharacteristicsComplexType occt;
FormatComplexType fct;
FormatDesignationComplexType fdct;
if (f.getObjectCharacteristicsArray() != null && f.getObjectCharacteristicsArray().length > 0) {
occt = f.getObjectCharacteristicsArray(0);
} else {
occt = f.addNewObjectCharacteristics();
}
if (occt.getFormatArray() != null && occt.getFormatArray().length > 0) {
fct = occt.getFormatArray(0);
} else {
fct = occt.addNewFormat();
}
if (fct.getFormatDesignation() != null) {
fdct = fct.getFormatDesignation();
} else {
fdct = fct.addNewFormatDesignation();
}
return fdct;
}
public static ContentPayload createPremisEventBinary(String eventID, Date date, String type, String details,
List<LinkingIdentifier> sources, List<LinkingIdentifier> outcomes, String outcome, String detailNote,
String detailExtension, List<String> agentIds) throws GenericException, ValidationException {
EventDocument event = EventDocument.Factory.newInstance();
EventComplexType ect = event.addNewEvent();
EventIdentifierComplexType eict = ect.addNewEventIdentifier();
eict.setEventIdentifierValue(eventID);
eict.setEventIdentifierType(getStringPlusAuthority(RodaConstants.PREMIS_IDENTIFIER_TYPE_URN));
ect.setEventDateTime(DateParser.getIsoDate(date));
ect.setEventType(getStringPlusAuthority(type));
EventDetailInformationComplexType edict = ect.addNewEventDetailInformation();
edict.setEventDetail(details);
if (sources != null) {
for (LinkingIdentifier identifier : sources) {
LinkingObjectIdentifierComplexType loict = ect.addNewLinkingObjectIdentifier();
loict.setLinkingObjectIdentifierValue(identifier.getValue());
loict.setLinkingObjectIdentifierType(getStringPlusAuthority(identifier.getType()));
if (identifier.getRoles() != null) {
loict.setLinkingObjectRoleArray(getStringPlusAuthorityArray(identifier.getRoles()));
}
}
}
if (outcomes != null) {
for (LinkingIdentifier identifier : outcomes) {
LinkingObjectIdentifierComplexType loict = ect.addNewLinkingObjectIdentifier();
loict.setLinkingObjectIdentifierValue(identifier.getValue());
loict.setLinkingObjectIdentifierType(getStringPlusAuthority(identifier.getType()));
if (identifier.getRoles() != null) {
loict.setLinkingObjectRoleArray(getStringPlusAuthorityArray(identifier.getRoles()));
}
}
}
if (agentIds != null) {
for (String agentId : agentIds) {
LinkingAgentIdentifierComplexType agentIdentifier = ect.addNewLinkingAgentIdentifier();
agentIdentifier.setLinkingAgentIdentifierType(getStringPlusAuthority(RodaConstants.PREMIS_IDENTIFIER_TYPE_URN));
agentIdentifier.setLinkingAgentIdentifierValue(agentId);
}
}
EventOutcomeInformationComplexType outcomeInformation = ect.addNewEventOutcomeInformation();
outcomeInformation.setEventOutcome(getStringPlusAuthority(outcome));
StringBuilder outcomeDetailNote = new StringBuilder(detailNote);
if (StringUtils.isNotBlank(detailExtension)) {
outcomeDetailNote.append("\n").append(detailExtension);
}
EventOutcomeDetailComplexType eodct = outcomeInformation.addNewEventOutcomeDetail();
eodct.setEventOutcomeDetailNote(outcomeDetailNote.toString());
return MetadataUtils.saveToContentPayload(event, true);
}
public static ContentPayload createPremisAgentBinary(String id, String name, PreservationAgentType type,
String extension, String note, String version) throws GenericException, ValidationException {
AgentDocument agent = AgentDocument.Factory.newInstance();
AgentComplexType act = agent.addNewAgent();
AgentIdentifierComplexType agentIdentifier = act.addNewAgentIdentifier();
agentIdentifier.setAgentIdentifierType(getStringPlusAuthority(RodaConstants.PREMIS_IDENTIFIER_TYPE_URN));
agentIdentifier.setAgentIdentifierValue(id);
act.setAgentType(getStringPlusAuthority(type.toString()));
if (StringUtils.isNotBlank(name)) {
act.addNewAgentName().setStringValue(name);
}
if (StringUtils.isNotBlank(note)) {
act.addAgentNote(note);
}
if (StringUtils.isNotBlank(version)) {
act.setAgentVersion(version);
}
if (StringUtils.isNotBlank(extension)) {
try {
act.addNewAgentExtension().set(XmlObject.Factory.parse(extension));
} catch (XmlException e) {
throw new ValidationException(e.getMessage());
}
}
return MetadataUtils.saveToContentPayload(agent, true);
}
public static Representation createBaseRepresentation(String aipId, String representationId)
throws GenericException, ValidationException {
Representation representation = Representation.Factory.newInstance();
ObjectIdentifierComplexType oict = representation.addNewObjectIdentifier();
oict.setObjectIdentifierType(getStringPlusAuthority(RodaConstants.PREMIS_IDENTIFIER_TYPE_URN));
String identifier = IdUtils.getPreservationId(PreservationMetadataType.REPRESENTATION, aipId, representationId,
null, null);
oict.setObjectIdentifierValue(identifier);
representation.addNewPreservationLevel().setPreservationLevelValue(getStringPlusAuthority(""));
return representation;
}
public static ContentPayload createBaseFile(File originalFile, ModelService model,
Collection<String> fixityAlgorithms) throws GenericException, RequestNotValidException, NotFoundException,
AuthorizationDeniedException, ValidationException, XmlException {
ObjectDocument document = ObjectDocument.Factory.newInstance();
gov.loc.premis.v3.File file = gov.loc.premis.v3.File.Factory.newInstance();
file.addNewPreservationLevel()
.setPreservationLevelValue(getStringPlusAuthority(RodaConstants.PRESERVATION_LEVEL_FULL));
ObjectIdentifierComplexType oict = file.addNewObjectIdentifier();
oict.setObjectIdentifierValue(
URNUtils.createRodaPreservationURN(PreservationMetadataType.FILE, originalFile.getId()));
oict.setObjectIdentifierType(getStringPlusAuthority(RodaConstants.PREMIS_IDENTIFIER_TYPE_URN));
ObjectCharacteristicsComplexType occt = file.addNewObjectCharacteristics();
// TODO
// occt.setCompositionLevel(CompositionLevelComplexType.Factory.parse("0"));
FormatComplexType fct = occt.addNewFormat();
FormatDesignationComplexType fdct = fct.addNewFormatDesignation();
fdct.setFormatName(getStringPlusAuthority(""));
fdct.setFormatVersion("");
Binary binary = model.getStorage().getBinary(ModelUtils.getFileStoragePath(originalFile));
if (binary.getContentDigest() != null && !binary.getContentDigest().isEmpty()) {
// use binary content digest information
for (Entry<String, String> entry : binary.getContentDigest().entrySet()) {
FixityComplexType premisFixity = occt.addNewFixity();
premisFixity.setMessageDigest(entry.getKey());
premisFixity.setMessageDigestAlgorithm(getStringPlusAuthority(entry.getValue()));
premisFixity.setMessageDigestOriginator(getStringPlusAuthority(FIXITY_ORIGINATOR));
}
} else {
// if binary does not contain digest, create a new one
try {
List<Fixity> fixities = calculateFixities(binary, fixityAlgorithms, FIXITY_ORIGINATOR);
for (Fixity fixity : fixities) {
FixityComplexType premisFixity = occt.addNewFixity();
premisFixity.setMessageDigest(fixity.getMessageDigest());
premisFixity.setMessageDigestAlgorithm(getStringPlusAuthority(fixity.getMessageDigestAlgorithm()));
premisFixity.setMessageDigestOriginator(getStringPlusAuthority(fixity.getMessageDigestOriginator()));
}
} catch (IOException | NoSuchAlgorithmException e) {
LOGGER.warn("Could not calculate fixity for file " + originalFile);
}
}
occt.setSize(binary.getSizeInBytes());
// occt.addNewObjectCharacteristicsExtension().set("");
file.addNewOriginalName().setStringValue(originalFile.getId());
StorageComplexType sct = file.addNewStorage();
ContentLocationComplexType clct = sct.addNewContentLocation();
clct.setContentLocationType(getStringPlusAuthority(""));
clct.setContentLocationValue("");
document.setObject(file);
return MetadataUtils.saveToContentPayload(document, true);
}
public static List<Fixity> extractFixities(Binary premisFile) throws GenericException, XmlException, IOException {
List<Fixity> fixities = new ArrayList<>();
InputStream inputStream = premisFile.getContent().createInputStream();
gov.loc.premis.v3.File f = binaryToFile(inputStream);
if (f.getObjectCharacteristicsArray() != null && f.getObjectCharacteristicsArray().length > 0) {
ObjectCharacteristicsComplexType occt = f.getObjectCharacteristicsArray(0);
if (occt.getFixityArray() != null && occt.getFixityArray().length > 0) {
for (FixityComplexType fct : occt.getFixityArray()) {
Fixity fix = new Fixity();
fix.setMessageDigest(fct.getMessageDigest());
fix.setMessageDigestAlgorithm(fct.getMessageDigestAlgorithm().getStringValue());
fix.setMessageDigestOriginator(fct.getMessageDigestOriginator().getStringValue());
fixities.add(fix);
}
}
}
IOUtils.closeQuietly(inputStream);
return fixities;
}
public static String extractFixity(Binary premisFile, String fixityType)
throws IOException, GenericException, XmlException {
String fixityValue = null;
InputStream inputStream = premisFile.getContent().createInputStream();
gov.loc.premis.v3.File f = binaryToFile(inputStream);
if (f.getObjectCharacteristicsArray() != null && f.getObjectCharacteristicsArray().length > 0) {
ObjectCharacteristicsComplexType occt = f.getObjectCharacteristicsArray(0);
if (occt.getFixityArray() != null && occt.getFixityArray().length > 0) {
for (FixityComplexType fct : occt.getFixityArray()) {
if (fct.getMessageDigestAlgorithm().getStringValue().equalsIgnoreCase(fixityType)) {
fixityValue = fct.getMessageDigest();
break;
}
}
}
}
IOUtils.closeQuietly(inputStream);
return fixityValue;
}
public static gov.loc.premis.v3.Representation binaryToRepresentation(InputStream binaryInputStream)
throws XmlException, IOException, GenericException {
ObjectDocument objectDocument = ObjectDocument.Factory.parse(binaryInputStream);
ObjectComplexType object = objectDocument.getObject();
if (object instanceof Representation) {
return (Representation) object;
} else {
throw new GenericException("Trying to load a representation but was a " + object.getClass().getSimpleName());
}
}
public static gov.loc.premis.v3.File binaryToFile(InputStream binaryInputStream)
throws XmlException, IOException, GenericException {
ObjectDocument objectDocument = ObjectDocument.Factory.parse(binaryInputStream);
ObjectComplexType object = objectDocument.getObject();
if (object instanceof gov.loc.premis.v3.File) {
return (gov.loc.premis.v3.File) object;
} else {
throw new GenericException("Trying to load a file but was a " + object.getClass().getSimpleName());
}
}
public static EventComplexType binaryToEvent(InputStream binaryInputStream) throws XmlException, IOException {
return EventDocument.Factory.parse(binaryInputStream).getEvent();
}
public static AgentComplexType binaryToAgent(InputStream binaryInputStream) throws XmlException, IOException {
return AgentDocument.Factory.parse(binaryInputStream).getAgent();
}
public static gov.loc.premis.v3.Representation binaryToRepresentation(ContentPayload payload, boolean validate)
throws ValidationException, GenericException {
Representation representation;
InputStream inputStream = null;
try {
inputStream = payload.createInputStream();
representation = binaryToRepresentation(inputStream);
List<XmlValidationError> validationErrors = new ArrayList<>();
XmlOptions validationOptions = new XmlOptions();
validationOptions.setErrorListener(validationErrors);
if (validate && !representation.validate(validationOptions)) {
throw new ValidationException(MetadataUtils.xmlValidationErrorsToValidationReport(validationErrors));
}
} catch (XmlException | IOException e) {
throw new GenericException("Error loading representation premis file", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return representation;
}
public static gov.loc.premis.v3.File binaryToFile(ContentPayload payload, boolean validate)
throws ValidationException, GenericException {
gov.loc.premis.v3.File file;
List<XmlValidationError> validationErrors = new ArrayList<>();
InputStream inputStream = null;
try {
inputStream = payload.createInputStream();
file = binaryToFile(inputStream);
XmlOptions validationOptions = new XmlOptions();
validationOptions.setErrorListener(validationErrors);
if (validate && !file.validate(validationOptions)) {
throw new ValidationException(MetadataUtils.xmlValidationErrorsToValidationReport(validationErrors));
}
} catch (XmlException e) {
ValidationException exception = new ValidationException(e);
exception.setReport(MetadataUtils.xmlValidationErrorsToValidationReport(validationErrors));
throw exception;
} catch (IOException e) {
throw new GenericException("Error loading representation premis file", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return file;
}
public static ContentPayload fileToBinary(gov.loc.premis.v3.File file) throws GenericException, ValidationException {
ObjectDocument d = ObjectDocument.Factory.newInstance();
d.setObject(file);
return MetadataUtils.saveToContentPayload(d, true);
}
public static ContentPayload representationToBinary(Representation representation)
throws GenericException, ValidationException {
ObjectDocument d = ObjectDocument.Factory.newInstance();
d.setObject(representation);
return MetadataUtils.saveToContentPayload(d, true);
}
public static EventComplexType binaryToEvent(ContentPayload payload, boolean validate)
throws ValidationException, GenericException {
EventComplexType event;
InputStream inputStream = null;
try {
inputStream = payload.createInputStream();
event = binaryToEvent(inputStream);
List<XmlValidationError> validationErrors = new ArrayList<>();
XmlOptions validationOptions = new XmlOptions();
validationOptions.setErrorListener(validationErrors);
if (validate && !event.validate(validationOptions)) {
throw new ValidationException(MetadataUtils.xmlValidationErrorsToValidationReport(validationErrors));
}
} catch (XmlException | IOException e) {
throw new GenericException("Error loading representation premis file", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return event;
}
public static AgentComplexType binaryToAgent(ContentPayload payload, boolean validate)
throws ValidationException, GenericException {
AgentComplexType agent;
InputStream inputStream = null;
try {
inputStream = payload.createInputStream();
agent = binaryToAgent(inputStream);
List<XmlValidationError> validationErrors = new ArrayList<>();
XmlOptions validationOptions = new XmlOptions();
validationOptions.setErrorListener(validationErrors);
if (validate && !agent.validate(validationOptions)) {
throw new ValidationException(MetadataUtils.xmlValidationErrorsToValidationReport(validationErrors));
}
} catch (XmlException | IOException e) {
throw new GenericException("Error loading representation premis file", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return agent;
}
public static SolrInputDocument getSolrDocument(Binary premisBinary) throws GenericException {
SolrInputDocument doc = new SolrInputDocument();
InputStream inputStream = null;
try {
inputStream = premisBinary.getContent().createInputStream();
gov.loc.premis.v3.File premisFile = binaryToFile(inputStream);
if (premisFile.getOriginalName() != null) {
doc.setField(RodaConstants.FILE_ORIGINALNAME, premisFile.getOriginalName().getStringValue());
// TODO extension
}
if (premisFile.getObjectCharacteristicsArray() != null && premisFile.getObjectCharacteristicsArray().length > 0) {
ObjectCharacteristicsComplexType occt = premisFile.getObjectCharacteristicsArray(0);
doc.setField(RodaConstants.FILE_SIZE, occt.getSize());
if (occt.getFixityArray() != null && occt.getFixityArray().length > 0) {
List<String> hashes = new ArrayList<>();
for (FixityComplexType fct : occt.getFixityArray()) {
StringBuilder fixityPrint = new StringBuilder();
fixityPrint.append(fct.getMessageDigest());
fixityPrint.append(" (");
fixityPrint.append(fct.getMessageDigestAlgorithm().getStringValue());
if (StringUtils.isNotBlank(fct.getMessageDigestOriginator().getStringValue())) {
fixityPrint.append(", "); //
fixityPrint.append(fct.getMessageDigestOriginator().getStringValue());
}
fixityPrint.append(")");
hashes.add(fixityPrint.toString());
}
doc.addField(RodaConstants.FILE_HASH, hashes);
}
if (occt.getFormatArray() != null && occt.getFormatArray().length > 0) {
FormatComplexType fct = occt.getFormatArray(0);
if (fct.getFormatDesignation() != null) {
if (StringUtils.isNotBlank(fct.getFormatDesignation().getFormatName().getStringValue())) {
doc.addField(RodaConstants.FILE_FILEFORMAT, fct.getFormatDesignation().getFormatName().getStringValue());
}
if (StringUtils.isNotBlank(fct.getFormatDesignation().getFormatVersion())) {
doc.addField(RodaConstants.FILE_FORMAT_VERSION, fct.getFormatDesignation().getFormatVersion());
}
}
FormatRegistryComplexType pronomRegistry = getFormatRegistry(premisFile,
RodaConstants.PRESERVATION_REGISTRY_PRONOM);
if (pronomRegistry != null && pronomRegistry.getFormatRegistryKey() != null) {
doc.addField(RodaConstants.FILE_PRONOM, pronomRegistry.getFormatRegistryKey().getStringValue());
}
FormatRegistryComplexType mimeRegistry = getFormatRegistry(premisFile,
RodaConstants.PRESERVATION_REGISTRY_MIME);
if (mimeRegistry != null && mimeRegistry.getFormatRegistryKey() != null) {
doc.addField(RodaConstants.FILE_FORMAT_MIMETYPE, mimeRegistry.getFormatRegistryKey().getStringValue());
}
// TODO extension
}
if (occt.getCreatingApplicationArray() != null && occt.getCreatingApplicationArray().length > 0) {
CreatingApplicationComplexType cact = occt.getCreatingApplicationArray(0);
if (cact.getCreatingApplicationName() != null) {
doc.addField(RodaConstants.FILE_CREATING_APPLICATION_NAME,
cact.getCreatingApplicationName().getStringValue());
}
doc.addField(RodaConstants.FILE_CREATING_APPLICATION_VERSION, cact.getCreatingApplicationVersion());
doc.addField(RodaConstants.FILE_DATE_CREATED_BY_APPLICATION, cact.getDateCreatedByApplication());
}
}
} catch (XmlException | IOException e) {
LOGGER.error("Error updating Solr document", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return doc;
}
public static PreservationMetadata createPremisAgentBinary(Plugin<?> plugin, ModelService model, boolean notify)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException,
ValidationException, AlreadyExistsException {
String id = IdUtils.getPluginAgentId(plugin.getClass().getName(), plugin.getVersion());
String extension = "";
ContentPayload agentPayload = PremisV3Utils.createPremisAgentBinary(id, plugin.getName(), plugin.getAgentType(),
extension, plugin.getDescription(), plugin.getVersion());
return model.createPreservationMetadata(PreservationMetadataType.AGENT, id, agentPayload, notify);
}
public static void linkFileToRepresentation(String fileId, String relationshipType, String relationshipSubType,
Representation r) throws GenericException, RequestNotValidException, NotFoundException,
AuthorizationDeniedException, XmlException, IOException, ValidationException {
RelationshipComplexType relationship = r.addNewRelationship();
relationship.setRelationshipType(getStringPlusAuthority(relationshipType));
relationship.setRelationshipSubType(getStringPlusAuthority(relationshipSubType));
RelatedObjectIdentifierComplexType roict = relationship.addNewRelatedObjectIdentifier();
roict.setRelatedObjectIdentifierType(getStringPlusAuthority(RodaConstants.PREMIS_IDENTIFIER_TYPE_URN));
roict.setRelatedObjectIdentifierValue(fileId);
}
public static List<LinkingIdentifier> extractAgentsFromEvent(Binary b) throws ValidationException, GenericException {
List<LinkingIdentifier> identifiers = new ArrayList<>();
EventComplexType event = PremisV3Utils.binaryToEvent(b.getContent(), true);
if (event.getLinkingAgentIdentifierArray() != null && event.getLinkingAgentIdentifierArray().length > 0) {
for (LinkingAgentIdentifierComplexType laict : event.getLinkingAgentIdentifierArray()) {
LinkingIdentifier li = new LinkingIdentifier();
li.setType(laict.getLinkingAgentIdentifierType().getStringValue());
li.setValue(laict.getLinkingAgentIdentifierValue());
li.setRoles(stringplusArrayToStringList(laict.getLinkingAgentRoleArray()));
identifiers.add(li);
}
}
return identifiers;
}
public static List<LinkingIdentifier> extractObjectFromEvent(Binary binary)
throws ValidationException, GenericException {
List<LinkingIdentifier> identifiers = new ArrayList<>();
EventComplexType event = PremisV3Utils.binaryToEvent(binary.getContent(), true);
if (event.getLinkingObjectIdentifierArray() != null && event.getLinkingObjectIdentifierArray().length > 0) {
for (LinkingObjectIdentifierComplexType loict : event.getLinkingObjectIdentifierArray()) {
LinkingIdentifier li = new LinkingIdentifier();
li.setType(loict.getLinkingObjectIdentifierType().getStringValue());
li.setValue(loict.getLinkingObjectIdentifierValue());
li.setRoles(stringplusArrayToStringList(loict.getLinkingObjectRoleArray()));
identifiers.add(li);
}
}
return identifiers;
}
public static StringPlusAuthority getStringPlusAuthority(String value) {
return getStringPlusAuthority(value, "");
}
private static StringPlusAuthority getStringPlusAuthority(String value, String authority) {
StringPlusAuthority spa = StringPlusAuthority.Factory.newInstance();
spa.setStringValue(value);
if (StringUtils.isNotBlank(authority)) {
spa.setAuthority(authority);
}
return spa;
}
public static StringPlusAuthority[] getStringPlusAuthorityArray(List<String> values) {
List<StringPlusAuthority> l = new ArrayList<>();
if (values != null && !values.isEmpty()) {
for (String value : values) {
l.add(getStringPlusAuthority(value));
}
}
return l.toArray(new StringPlusAuthority[l.size()]);
}
private static List<String> stringplusArrayToStringList(StringPlusAuthority[] source) {
List<String> dst = new ArrayList<>();
if (source != null && source.length > 0) {
for (StringPlusAuthority spa : source) {
dst.add(spa.getStringValue());
}
}
return dst;
}
public static PreservationMetadata createPremisUserAgentBinary(String username, ModelService model,
IndexService index, boolean notify) throws GenericException, ValidationException, NotFoundException,
RequestNotValidException, AuthorizationDeniedException, AlreadyExistsException {
PreservationMetadata pm = null;
if (StringUtils.isNotBlank(username)) {
String id = IdUtils.getUserAgentId(username);
String fullName = "";
String extension = "";
String note = "";
String version = "";
try {
RODAMember member = index.retrieve(RODAMember.class, username,
Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.MEMBERS_FULLNAME, RodaConstants.MEMBERS_EMAIL));
fullName = member.getFullName();
if (member instanceof User) {
User user = (User) member;
note = user.getEmail();
}
} catch (NotFoundException e) {
LOGGER.warn("Could not find user and add its details to the PREMIS agent", e);
}
ContentPayload agentPayload = PremisV3Utils.createPremisAgentBinary(id, fullName, PreservationAgentType.PERSON,
extension, note, version);
pm = model.createPreservationMetadata(PreservationMetadataType.AGENT, id, agentPayload, notify);
}
return pm;
}
public static void updateFormatPreservationMetadata(ModelService model, String aipId, String representationId,
List<String> fileDirectoryPath, String fileId, String format, String version, String pronom, String mime,
boolean notify) {
Binary premisBin;
try {
try {
premisBin = model.retrievePreservationFile(aipId, representationId, fileDirectoryPath, fileId);
} catch (NotFoundException e) {
LOGGER.debug("PREMIS object skeleton does not exist yet. Creating PREMIS object!");
List<String> algorithms = RodaCoreFactory.getFixityAlgorithms();
if (fileId == null) {
PremisSkeletonPluginUtils.createPremisSkeletonOnRepresentation(model, aipId, representationId, algorithms);
} else {
File file = model.retrieveFile(aipId, representationId, fileDirectoryPath, fileId);
PremisSkeletonPluginUtils.createPremisSkeletonOnFile(model, file, algorithms);
}
premisBin = model.retrievePreservationFile(aipId, representationId, fileDirectoryPath, fileId);
LOGGER.debug("PREMIS object skeleton created");
}
gov.loc.premis.v3.File premisFile = binaryToFile(premisBin.getContent(), false);
PremisV3Utils.updateFileFormat(premisFile, format, version, pronom, mime);
PreservationMetadataType type = PreservationMetadataType.FILE;
String id = IdUtils.getPreservationFileId(fileId);
ContentPayload premisFilePayload = fileToBinary(premisFile);
model.updatePreservationMetadata(id, type, aipId, representationId, fileDirectoryPath, fileId, premisFilePayload,
notify);
} catch (RODAException | XmlException | IOException e) {
LOGGER.error("PREMIS will not be updated due to an error", e);
}
}
public static void updateCreatingApplicationPreservationMetadata(ModelService model, String aipId,
String representationId, List<String> fileDirectoryPath, String fileId, String creatingApplicationName,
String creatingApplicationVersion, String dateCreatedByApplication, boolean notify) {
Binary premisBin;
try {
try {
premisBin = model.retrievePreservationFile(aipId, representationId, fileDirectoryPath, fileId);
} catch (NotFoundException e) {
LOGGER.debug("PREMIS object skeleton does not exist yet. Creating PREMIS object!");
List<String> algorithms = RodaCoreFactory.getFixityAlgorithms();
PremisSkeletonPluginUtils.createPremisSkeletonOnRepresentation(model, aipId, representationId, algorithms);
premisBin = model.retrievePreservationFile(aipId, representationId, fileDirectoryPath, fileId);
LOGGER.debug("PREMIS object skeleton created");
}
gov.loc.premis.v3.File premisFile = binaryToFile(premisBin.getContent(), false);
PremisV3Utils.updateCreatingApplication(premisFile, creatingApplicationName, creatingApplicationVersion,
dateCreatedByApplication);
PreservationMetadataType type = PreservationMetadataType.FILE;
String id = IdUtils.getPreservationFileId(fileId);
ContentPayload premisFilePayload = fileToBinary(premisFile);
model.updatePreservationMetadata(id, type, aipId, representationId, fileDirectoryPath, fileId, premisFilePayload,
notify);
} catch (RODAException | XmlException | IOException e) {
LOGGER.error("PREMIS will not be updated due to an error", e);
}
}
}