/**
* 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.model;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.roda.core.RodaCoreFactory;
import org.roda.core.common.PremisV3Utils;
import org.roda.core.common.UserUtility;
import org.roda.core.common.dips.DIPUtils;
import org.roda.core.common.iterables.CloseableIterable;
import org.roda.core.common.iterables.CloseableIterables;
import org.roda.core.common.monitor.TransferredResourcesScanner;
import org.roda.core.common.notifications.NotificationProcessor;
import org.roda.core.common.validation.ValidationUtils;
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.AuthenticationDeniedException;
import org.roda.core.data.exceptions.AuthorizationDeniedException;
import org.roda.core.data.exceptions.EmailAlreadyExistsException;
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.exceptions.IllegalOperationException;
import org.roda.core.data.exceptions.InvalidTokenException;
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.exceptions.UserAlreadyExistsException;
import org.roda.core.data.utils.JsonUtils;
import org.roda.core.data.utils.URNUtils;
import org.roda.core.data.v2.IsModelObject;
import org.roda.core.data.v2.IsRODAObject;
import org.roda.core.data.v2.LiteRODAObject;
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.common.Pair;
import org.roda.core.data.v2.formats.Format;
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.DIPFile;
import org.roda.core.data.v2.ip.File;
import org.roda.core.data.v2.ip.Permissions;
import org.roda.core.data.v2.ip.Permissions.PermissionType;
import org.roda.core.data.v2.ip.Representation;
import org.roda.core.data.v2.ip.StoragePath;
import org.roda.core.data.v2.ip.TransferredResource;
import org.roda.core.data.v2.ip.metadata.DescriptiveMetadata;
import org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent;
import org.roda.core.data.v2.ip.metadata.LinkingIdentifier;
import org.roda.core.data.v2.ip.metadata.OtherMetadata;
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.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.Group;
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.data.v2.validation.ValidationReport;
import org.roda.core.model.iterables.LogEntryFileSystemIterable;
import org.roda.core.model.iterables.LogEntryStorageIterable;
import org.roda.core.model.utils.ModelUtils;
import org.roda.core.model.utils.ResourceListUtils;
import org.roda.core.model.utils.ResourceParseUtils;
import org.roda.core.storage.Binary;
import org.roda.core.storage.BinaryVersion;
import org.roda.core.storage.ContentPayload;
import org.roda.core.storage.DefaultBinary;
import org.roda.core.storage.DefaultStoragePath;
import org.roda.core.storage.Directory;
import org.roda.core.storage.EmptyClosableIterable;
import org.roda.core.storage.Entity;
import org.roda.core.storage.Resource;
import org.roda.core.storage.StorageService;
import org.roda.core.storage.StringContentPayload;
import org.roda.core.storage.fs.FSPathContentPayload;
import org.roda.core.storage.fs.FSUtils;
import org.roda.core.util.HTTPUtility;
import org.roda.core.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class that "relates" Model & Storage
*
*
* @author Luis Faria <lfaria@keep.pt>
* @author Hélder Silva <hsilva@keep.pt>
*/
public class ModelService extends ModelObservable {
private static final Logger LOGGER = LoggerFactory.getLogger(ModelService.class);
private static final DateTimeFormatter LOG_NAME_DATE_FORMAT = DateTimeFormat.forPattern("yyyy-MM-dd");
private static final boolean FAIL_IF_NO_DESCRIPTIVE_METADATA_SCHEMA = false;
private final StorageService storage;
private Object logFileLock = new Object();
public ModelService(StorageService storage) {
super();
this.storage = storage;
ensureAllContainersExist();
ensureAllDiretoriesExist();
}
private void ensureAllContainersExist() {
try {
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_AIP);
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_PRESERVATION);
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_ACTIONLOG);
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_JOB);
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_JOB_REPORT);
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_RISK);
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_RISK_INCIDENCE);
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_DIP);
} catch (RequestNotValidException | GenericException | AuthorizationDeniedException e) {
LOGGER.error("Error while ensuring that all containers exist", e);
}
}
private void createContainerIfNotExists(String containerName)
throws RequestNotValidException, GenericException, AuthorizationDeniedException {
try {
storage.createContainer(DefaultStoragePath.parse(containerName));
} catch (AlreadyExistsException e) {
// do nothing
}
}
private void ensureAllDiretoriesExist() {
try {
createDirectoryIfNotExists(
DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_PRESERVATION, RodaConstants.STORAGE_DIRECTORY_AGENTS));
} catch (RequestNotValidException | GenericException | AuthorizationDeniedException e) {
LOGGER.error("Error initializing directories", e);
}
}
private void createDirectoryIfNotExists(StoragePath directoryPath)
throws GenericException, AuthorizationDeniedException {
try {
storage.createDirectory(directoryPath);
} catch (AlreadyExistsException e) {
// do nothing
}
}
public StorageService getStorage() {
return storage;
}
/***************** AIP related *****************/
/***********************************************/
private void createAIPMetadata(AIP aip) throws RequestNotValidException, GenericException, AlreadyExistsException,
AuthorizationDeniedException, NotFoundException {
createAIPMetadata(aip, ModelUtils.getAIPStoragePath(aip.getId()));
}
private void createAIPMetadata(AIP aip, StoragePath storagePath) throws RequestNotValidException, GenericException,
AlreadyExistsException, AuthorizationDeniedException, NotFoundException {
String json = JsonUtils.getJsonFromObject(aip);
DefaultStoragePath metadataStoragePath = DefaultStoragePath.parse(storagePath,
RodaConstants.STORAGE_AIP_METADATA_FILENAME);
boolean asReference = false;
storage.createBinary(metadataStoragePath, new StringContentPayload(json), asReference);
}
private void updateAIPMetadata(AIP aip)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
updateAIPMetadata(aip, ModelUtils.getAIPStoragePath(aip.getId()));
}
private void updateAIPMetadata(AIP aip, StoragePath storagePath)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
String json = JsonUtils.getJsonFromObject(aip);
DefaultStoragePath metadataStoragePath = DefaultStoragePath.parse(storagePath,
RodaConstants.STORAGE_AIP_METADATA_FILENAME);
boolean asReference = false;
boolean createIfNotExists = true;
storage.updateBinaryContent(metadataStoragePath, new StringContentPayload(json), asReference, createIfNotExists);
}
private void updateDIPMetadata(DIP dip)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
updateDIPMetadata(dip, ModelUtils.getDIPStoragePath(dip.getId()));
}
public CloseableIterable<OptionalWithCause<AIP>> listAIPs()
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
final boolean recursive = false;
final CloseableIterable<Resource> resourcesIterable = storage
.listResourcesUnderContainer(ModelUtils.getAIPContainerPath(), recursive);
return ResourceParseUtils.convert(getStorage(), resourcesIterable, AIP.class);
}
public AIP retrieveAIP(String aipId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
return ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
}
/**
* Create a new AIP
*
* @param aipId
* Suggested ID for the AIP, if <code>null</code> then an ID will be
* automatically generated. If ID cannot be allowed because it
* already exists or is not valid, another ID will be provided.
* @param sourceStorage
* @param sourcePath
* @return
* @throws RequestNotValidException
* @throws GenericException
* @throws NotFoundException
* @throws AuthorizationDeniedException
* @throws AlreadyExistsException
* @throws ValidationException
*/
public AIP createAIP(String aipId, StorageService sourceStorage, StoragePath sourcePath, boolean notify,
String createdBy) throws RequestNotValidException, GenericException, AuthorizationDeniedException,
AlreadyExistsException, NotFoundException, ValidationException {
// XXX possible optimization would be to allow move between storage
ModelService sourceModelService = new ModelService(sourceStorage);
AIP aip;
Directory sourceDirectory = sourceStorage.getDirectory(sourcePath);
ValidationReport validationReport = isAIPvalid(sourceModelService, sourceDirectory,
FAIL_IF_NO_DESCRIPTIVE_METADATA_SCHEMA);
if (validationReport.isValid()) {
storage.copy(sourceStorage, sourcePath, ModelUtils.getAIPStoragePath(aipId));
Directory newDirectory = storage.getDirectory(ModelUtils.getAIPStoragePath(aipId));
aip = ResourceParseUtils.getAIPMetadata(getStorage(), newDirectory.getStoragePath());
aip.setCreatedBy(createdBy);
aip.setCreatedOn(new Date());
aip.setUpdatedBy(createdBy);
aip.setUpdatedOn(new Date());
if (notify) {
notifyAipCreated(aip);
}
} else {
throw new ValidationException(validationReport);
}
return aip;
}
public AIP createAIP(String parentId, String type, Permissions permissions, List<String> ingestSIPIds,
String ingestJobId, boolean notify, String createdBy, boolean isGhost) throws RequestNotValidException,
NotFoundException, GenericException, AlreadyExistsException, AuthorizationDeniedException {
AIPState state = AIPState.ACTIVE;
Directory directory = storage.createRandomDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_AIP));
String id = directory.getStoragePath().getName();
Permissions inheritedPermissions = this.addParentPermissions(permissions, parentId);
AIP aip = new AIP(id, parentId, type, state, inheritedPermissions, createdBy);
aip.setGhost(isGhost);
aip.setIngestSIPIds(ingestSIPIds);
aip.setIngestJobId(ingestJobId);
createAIPMetadata(aip);
if (notify) {
notifyAipCreated(aip);
}
return aip;
}
public AIP createAIP(String parentId, String type, Permissions permissions, String createdBy)
throws RequestNotValidException, NotFoundException, GenericException, AlreadyExistsException,
AuthorizationDeniedException {
AIPState state = AIPState.ACTIVE;
boolean notify = true;
return createAIP(state, parentId, type, permissions, notify, createdBy);
}
public AIP createAIP(AIPState state, String parentId, String type, Permissions permissions, String createdBy)
throws RequestNotValidException, NotFoundException, GenericException, AlreadyExistsException,
AuthorizationDeniedException {
boolean notify = true;
return createAIP(state, parentId, type, permissions, notify, createdBy);
}
public AIP createAIP(AIPState state, String parentId, String type, Permissions permissions, boolean notify,
String createdBy) throws RequestNotValidException, NotFoundException, GenericException, AlreadyExistsException,
AuthorizationDeniedException {
Directory directory = storage.createRandomDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_AIP));
String id = directory.getStoragePath().getName();
Permissions inheritedPermissions = this.addParentPermissions(permissions, parentId);
AIP aip = new AIP(id, parentId, type, state, inheritedPermissions, createdBy);
createAIPMetadata(aip);
if (notify) {
notifyAipCreated(aip);
}
return aip;
}
public AIP createAIP(AIPState state, String parentId, String type, Permissions permissions, List<String> ingestSIPIds,
String ingestJobId, boolean notify, String createdBy) throws RequestNotValidException, NotFoundException,
GenericException, AlreadyExistsException, AuthorizationDeniedException {
Directory directory = storage.createRandomDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_AIP));
String id = directory.getStoragePath().getName();
Permissions inheritedPermissions = this.addParentPermissions(permissions, parentId);
AIP aip = new AIP(id, parentId, type, state, inheritedPermissions, createdBy).setIngestSIPIds(ingestSIPIds)
.setIngestJobId(ingestJobId);
createAIPMetadata(aip);
if (notify) {
notifyAipCreated(aip);
}
return aip;
}
public AIP createAIP(String aipId, StorageService sourceStorage, StoragePath sourcePath, String createdBy)
throws RequestNotValidException, GenericException, AuthorizationDeniedException, AlreadyExistsException,
NotFoundException, ValidationException {
return createAIP(aipId, sourceStorage, sourcePath, true, createdBy);
}
public AIP notifyAipCreated(String aipId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
notifyAipCreated(aip);
return aip;
}
public AIP notifyAipUpdated(String aipId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
notifyAipUpdated(aip);
return aip;
}
private Permissions addParentPermissions(Permissions permissions, String parentId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
if (parentId != null) {
AIP parentAIP = this.retrieveAIP(parentId);
Set<String> parentGroupnames = parentAIP.getPermissions().getGroupnames();
Set<String> parentUsernames = parentAIP.getPermissions().getUsernames();
Set<String> groupnames = permissions.getGroupnames();
Set<String> usernames = permissions.getUsernames();
for (String user : parentUsernames) {
if (!usernames.contains(user)) {
permissions.setUserPermissions(user, parentAIP.getPermissions().getUserPermissions(user));
}
}
for (String group : parentGroupnames) {
if (!groupnames.contains(group)) {
permissions.setGroupPermissions(group, parentAIP.getPermissions().getGroupPermissions(group));
}
}
}
return permissions;
}
public AIP updateAIP(String aipId, StorageService sourceStorage, StoragePath sourcePath, String updatedBy)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException,
AlreadyExistsException, ValidationException {
// TODO verify structure of source AIP and update it in the storage
ModelService sourceModelService = new ModelService(sourceStorage);
AIP aip;
Directory sourceDirectory = sourceStorage.getDirectory(sourcePath);
ValidationReport validationReport = isAIPvalid(sourceModelService, sourceDirectory,
FAIL_IF_NO_DESCRIPTIVE_METADATA_SCHEMA);
if (validationReport.isValid()) {
StoragePath aipPath = ModelUtils.getAIPStoragePath(aipId);
// XXX possible optimization only creating new files, updating
// changed and removing deleted ones.
storage.deleteResource(aipPath);
storage.copy(sourceStorage, sourcePath, aipPath);
Directory directoryUpdated = storage.getDirectory(aipPath);
aip = ResourceParseUtils.getAIPMetadata(getStorage(), directoryUpdated.getStoragePath());
aip.setUpdatedBy(updatedBy);
aip.setUpdatedOn(new Date());
notifyAipUpdated(aip);
} else {
throw new ValidationException(validationReport);
}
return aip;
}
public AIP updateAIP(AIP aip, String updatedBy)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
aip.setUpdatedBy(updatedBy);
aip.setUpdatedOn(new Date());
updateAIPMetadata(aip);
notifyAipUpdated(aip);
return aip;
}
public AIP updateAIPState(AIP aip, String updatedBy)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
aip.setUpdatedBy(updatedBy);
aip.setUpdatedOn(new Date());
updateAIPMetadata(aip);
notifyAipStateUpdated(aip);
return aip;
}
public AIP moveAIP(String aipId, String parentId)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
if (aipId.equals(parentId)) {
throw new RequestNotValidException("Cannot set itself as its parent: " + aipId);
}
// TODO ADD RESTRICTIONS
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
String oldParentId = aip.getParentId();
aip.setParentId(parentId);
updateAIPMetadata(aip);
notifyAipMoved(aip, oldParentId, parentId);
return aip;
}
public void deleteAIP(String aipId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath aipPath = ModelUtils.getAIPStoragePath(aipId);
storage.deleteResource(aipPath);
notifyAipDeleted(aipId);
}
private ValidationReport isAIPvalid(ModelService model, Directory directory,
boolean failIfNoDescriptiveMetadataSchema)
throws GenericException, RequestNotValidException, NotFoundException, AuthorizationDeniedException {
ValidationReport report = new ValidationReport();
// validate metadata (against schemas)
ValidationReport descriptiveMetadataValidationReport = ValidationUtils.isAIPDescriptiveMetadataValid(model,
directory.getStoragePath().getName(), failIfNoDescriptiveMetadataSchema);
report.setValid(descriptiveMetadataValidationReport.isValid());
report.setIssues(descriptiveMetadataValidationReport.getIssues());
// FIXME validate others aspects
return report;
}
/***************** Descriptive Metadata related *****************/
/****************************************************************/
public Binary retrieveDescriptiveMetadataBinary(String aipId, String descriptiveMetadataId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
return retrieveDescriptiveMetadataBinary(aipId, null, descriptiveMetadataId);
}
public Binary retrieveDescriptiveMetadataBinary(String aipId, String representationId, String descriptiveMetadataId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath binaryPath = ModelUtils.getDescriptiveMetadataStoragePath(aipId, representationId,
descriptiveMetadataId);
return storage.getBinary(binaryPath);
}
public DescriptiveMetadata retrieveDescriptiveMetadata(String aipId, String descriptiveMetadataId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
return retrieveDescriptiveMetadata(aipId, null, descriptiveMetadataId);
}
public DescriptiveMetadata retrieveDescriptiveMetadata(String aipId, String representationId,
String descriptiveMetadataId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
DescriptiveMetadata ret = null;
for (DescriptiveMetadata descriptiveMetadata : getDescriptiveMetadata(aip, representationId)) {
if (descriptiveMetadata.getId().equals(descriptiveMetadataId)) {
ret = descriptiveMetadata;
break;
}
}
if (ret == null) {
throw new NotFoundException("Could not find descriptive metadata: " + descriptiveMetadataId);
}
return ret;
}
public DescriptiveMetadata createDescriptiveMetadata(String aipId, String descriptiveMetadataId,
ContentPayload payload, String descriptiveMetadataType, String descriptiveMetadataVersion, boolean notify)
throws RequestNotValidException, GenericException, AlreadyExistsException, AuthorizationDeniedException,
NotFoundException {
return createDescriptiveMetadata(aipId, null, descriptiveMetadataId, payload, descriptiveMetadataType,
descriptiveMetadataVersion, notify);
}
public DescriptiveMetadata createDescriptiveMetadata(String aipId, String descriptiveMetadataId,
ContentPayload payload, String descriptiveMetadataType, String descriptiveMetadataVersion)
throws RequestNotValidException, GenericException, AlreadyExistsException, AuthorizationDeniedException,
NotFoundException {
return createDescriptiveMetadata(aipId, null, descriptiveMetadataId, payload, descriptiveMetadataType,
descriptiveMetadataVersion, true);
}
public DescriptiveMetadata createDescriptiveMetadata(String aipId, String representationId,
String descriptiveMetadataId, ContentPayload payload, String descriptiveMetadataType,
String descriptiveMetadataVersion) throws RequestNotValidException, GenericException, AlreadyExistsException,
AuthorizationDeniedException, NotFoundException {
return createDescriptiveMetadata(aipId, representationId, descriptiveMetadataId, payload, descriptiveMetadataType,
descriptiveMetadataVersion, true);
}
public DescriptiveMetadata createDescriptiveMetadata(String aipId, String representationId,
String descriptiveMetadataId, ContentPayload payload, String descriptiveMetadataType,
String descriptiveMetadataVersion, boolean notify) throws RequestNotValidException, GenericException,
AlreadyExistsException, AuthorizationDeniedException, NotFoundException {
StoragePath binaryPath = ModelUtils.getDescriptiveMetadataStoragePath(aipId, representationId,
descriptiveMetadataId);
boolean asReference = false;
storage.createBinary(binaryPath, payload, asReference);
DescriptiveMetadata descriptiveMetadata = new DescriptiveMetadata(descriptiveMetadataId, aipId, representationId,
descriptiveMetadataType, descriptiveMetadataVersion);
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
aip.addDescriptiveMetadata(descriptiveMetadata);
updateAIPMetadata(aip);
if (notify) {
notifyDescriptiveMetadataCreated(descriptiveMetadata);
}
return descriptiveMetadata;
}
public DescriptiveMetadata updateDescriptiveMetadata(String aipId, String descriptiveMetadataId,
ContentPayload descriptiveMetadataPayload, String descriptiveMetadataType, String descriptiveMetadataVersion,
Map<String, String> properties) throws RequestNotValidException, GenericException, NotFoundException,
AuthorizationDeniedException, ValidationException {
return updateDescriptiveMetadata(aipId, null, descriptiveMetadataId, descriptiveMetadataPayload,
descriptiveMetadataType, descriptiveMetadataVersion, properties);
}
public DescriptiveMetadata updateDescriptiveMetadata(String aipId, String representationId,
String descriptiveMetadataId, ContentPayload descriptiveMetadataPayload, String descriptiveMetadataType,
String descriptiveMetadataVersion, Map<String, String> properties) throws RequestNotValidException,
GenericException, NotFoundException, AuthorizationDeniedException, ValidationException {
DescriptiveMetadata ret;
StoragePath binaryPath = ModelUtils.getDescriptiveMetadataStoragePath(aipId, representationId,
descriptiveMetadataId);
boolean asReference = false;
boolean createIfNotExists = false;
// Create version snapshot
storage.createBinaryVersion(binaryPath, properties);
// Update
storage.updateBinaryContent(binaryPath, descriptiveMetadataPayload, asReference, createIfNotExists);
// set descriptive metadata type
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
ret = updateDescriptiveMetadata(aip, representationId, descriptiveMetadataId, descriptiveMetadataType,
descriptiveMetadataVersion);
updateAIPMetadata(aip);
notifyDescriptiveMetadataUpdated(ret);
return ret;
}
private DescriptiveMetadata updateDescriptiveMetadata(AIP aip, String representationId, String descriptiveMetadataId,
String descriptiveMetadataType, String descriptiveMetadataVersion) {
DescriptiveMetadata descriptiveMetadata;
Optional<DescriptiveMetadata> odm = getDescriptiveMetadata(aip, representationId).stream()
.filter(dm -> dm.getId().equals(descriptiveMetadataId)).findFirst();
if (odm.isPresent()) {
descriptiveMetadata = odm.get();
descriptiveMetadata.setType(descriptiveMetadataType);
descriptiveMetadata.setVersion(descriptiveMetadataVersion);
} else {
descriptiveMetadata = new DescriptiveMetadata(descriptiveMetadataId, aip.getId(), representationId,
descriptiveMetadataType, descriptiveMetadataVersion);
aip.addDescriptiveMetadata(descriptiveMetadata);
}
return descriptiveMetadata;
}
public void deleteDescriptiveMetadata(String aipId, String descriptiveMetadataId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
deleteDescriptiveMetadata(aipId, null, descriptiveMetadataId);
}
public void deleteDescriptiveMetadata(String aipId, String representationId, String descriptiveMetadataId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath binaryPath = ModelUtils.getDescriptiveMetadataStoragePath(aipId, representationId,
descriptiveMetadataId);
storage.deleteResource(binaryPath);
// update AIP metadata
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
deleteDescriptiveMetadata(aip, representationId, descriptiveMetadataId);
updateAIPMetadata(aip);
notifyDescriptiveMetadataDeleted(aipId, representationId, descriptiveMetadataId);
}
private void deleteDescriptiveMetadata(AIP aip, String representationId, String descriptiveMetadataId) {
for (Iterator<DescriptiveMetadata> it = getDescriptiveMetadata(aip, representationId).iterator(); it.hasNext();) {
DescriptiveMetadata descriptiveMetadata = it.next();
if (descriptiveMetadata.getId().equals(descriptiveMetadataId)) {
it.remove();
break;
}
}
}
private List<DescriptiveMetadata> getDescriptiveMetadata(AIP aip, String representationId) {
List<DescriptiveMetadata> descriptiveMetadataList = Collections.emptyList();
if (representationId == null) {
// AIP descriptive metadata
descriptiveMetadataList = aip.getDescriptiveMetadata();
} else {
// Representation descriptive metadata
Optional<Representation> oRep = aip.getRepresentations().stream()
.filter(rep -> rep.getId().equals(representationId)).findFirst();
if (oRep.isPresent()) {
descriptiveMetadataList = oRep.get().getDescriptiveMetadata();
}
}
return descriptiveMetadataList;
}
public CloseableIterable<BinaryVersion> listDescriptiveMetadataVersions(String aipId, String descriptiveMetadataId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
return listDescriptiveMetadataVersions(aipId, null, descriptiveMetadataId);
}
public CloseableIterable<BinaryVersion> listDescriptiveMetadataVersions(String aipId, String representationId,
String descriptiveMetadataId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath binaryPath = ModelUtils.getDescriptiveMetadataStoragePath(aipId, representationId,
descriptiveMetadataId);
return storage.listBinaryVersions(binaryPath);
}
public BinaryVersion revertDescriptiveMetadataVersion(String aipId, String descriptiveMetadataId, String versionId,
Map<String, String> properties)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
return revertDescriptiveMetadataVersion(aipId, null, descriptiveMetadataId, versionId, properties);
}
public BinaryVersion revertDescriptiveMetadataVersion(String aipId, String representationId,
String descriptiveMetadataId, String versionId, Map<String, String> properties)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath binaryPath = ModelUtils.getDescriptiveMetadataStoragePath(aipId, representationId,
descriptiveMetadataId);
BinaryVersion currentVersion = storage.createBinaryVersion(binaryPath, properties);
storage.revertBinaryVersion(binaryPath, versionId);
notifyDescriptiveMetadataUpdated(retrieveDescriptiveMetadata(aipId, descriptiveMetadataId));
return currentVersion;
}
public CloseableIterable<OptionalWithCause<DescriptiveMetadata>> listDescriptiveMetadata()
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
List<CloseableIterable<OptionalWithCause<DescriptiveMetadata>>> list = new ArrayList<>();
CloseableIterable<OptionalWithCause<AIP>> aips = listAIPs();
for (OptionalWithCause<AIP> oaip : aips) {
if (oaip.isPresent()) {
AIP aip = oaip.get();
StoragePath storagePath = ModelUtils.getDescriptiveMetadataStoragePath(aip.getId(), null);
CloseableIterable<OptionalWithCause<DescriptiveMetadata>> aipDescriptiveMetadata;
try {
boolean recursive = true;
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
aipDescriptiveMetadata = ResourceParseUtils.convert(getStorage(), resources, DescriptiveMetadata.class);
} catch (NotFoundException e) {
// check if AIP exists
storage.getDirectory(ModelUtils.getAIPStoragePath(aip.getId()));
// if no exception was sent by above method, return empty list
aipDescriptiveMetadata = new EmptyClosableIterable<>();
}
list.add(aipDescriptiveMetadata);
// list from all representations
for (Representation representation : aip.getRepresentations()) {
CloseableIterable<OptionalWithCause<DescriptiveMetadata>> rpm = listDescriptiveMetadata(aip.getId(),
representation.getId());
list.add(rpm);
}
}
}
return CloseableIterables.concat(list);
}
public CloseableIterable<OptionalWithCause<DescriptiveMetadata>> listDescriptiveMetadata(String aipId,
boolean includeRepresentations)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath storagePath = ModelUtils.getDescriptiveMetadataStoragePath(aipId, null);
CloseableIterable<OptionalWithCause<DescriptiveMetadata>> aipDescriptiveMetadata;
try {
boolean recursive = true;
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
aipDescriptiveMetadata = ResourceParseUtils.convert(getStorage(), resources, DescriptiveMetadata.class);
} catch (NotFoundException e) {
// check if AIP exists
storage.getDirectory(ModelUtils.getAIPStoragePath(aipId));
// if no exception was sent by above method, return empty list
aipDescriptiveMetadata = new EmptyClosableIterable<>();
}
if (includeRepresentations) {
List<CloseableIterable<OptionalWithCause<DescriptiveMetadata>>> list = new ArrayList<>();
list.add(aipDescriptiveMetadata);
// list from all representations
AIP aip = retrieveAIP(aipId);
for (Representation representation : aip.getRepresentations()) {
CloseableIterable<OptionalWithCause<DescriptiveMetadata>> rpm = listDescriptiveMetadata(aipId,
representation.getId());
list.add(rpm);
}
return CloseableIterables.concat(list);
} else {
return aipDescriptiveMetadata;
}
}
public CloseableIterable<OptionalWithCause<DescriptiveMetadata>> listDescriptiveMetadata(String aipId,
String representationId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath storagePath = ModelUtils.getDescriptiveMetadataDirectoryStoragePath(aipId, representationId);
boolean recursive = true;
CloseableIterable<OptionalWithCause<DescriptiveMetadata>> ret;
try {
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
ret = ResourceParseUtils.convert(getStorage(), resources, DescriptiveMetadata.class);
} catch (NotFoundException e) {
// check if Representation exists
storage.getDirectory(ModelUtils.getRepresentationStoragePath(aipId, representationId));
// if no exception was sent by above method, return empty list
ret = new EmptyClosableIterable<>();
}
return ret;
}
/***************** Representation related *****************/
/**********************************************************/
public Representation retrieveRepresentation(String aipId, String representationId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
Representation ret = null;
for (Representation representation : aip.getRepresentations()) {
if (representation.getId().equals(representationId)) {
ret = representation;
break;
}
}
if (ret == null) {
throw new NotFoundException("Could not find representation: " + representationId);
}
return ret;
}
public Representation createRepresentation(String aipId, String representationId, boolean original, String type,
boolean notify) throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException,
AlreadyExistsException {
Representation representation = new Representation(representationId, aipId, original, type);
StoragePath directoryPath = ModelUtils.getRepresentationStoragePath(aipId, representationId);
storage.createDirectory(directoryPath);
// update AIP metadata
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
aip.getRepresentations().add(representation);
updateAIPMetadata(aip);
if (notify) {
notifyRepresentationCreated(representation);
}
return representation;
}
public Representation createRepresentation(String aipId, String representationId, boolean original, String type,
StorageService sourceStorage, StoragePath sourcePath, boolean justData) throws RequestNotValidException,
GenericException, NotFoundException, AuthorizationDeniedException, AlreadyExistsException, ValidationException {
Representation representation;
if (justData) {
StoragePath dataPath = ModelUtils.getRepresentationDataStoragePath(aipId, representationId);
StoragePath sourceDataPath = DefaultStoragePath.parse(sourcePath, RodaConstants.STORAGE_DIRECTORY_DATA);
storage.copy(sourceStorage, sourceDataPath, dataPath);
} else {
StoragePath directoryPath = ModelUtils.getRepresentationStoragePath(aipId, representationId);
// verify structure of source representation
// 20170324 should we validate the representation???
storage.copy(sourceStorage, sourcePath, directoryPath);
}
representation = new Representation(representationId, aipId, original, type);
// update AIP metadata
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
aip.getRepresentations().add(representation);
updateAIPMetadata(aip);
notifyRepresentationCreated(representation);
return representation;
}
public Representation updateRepresentationInfo(Representation representation) {
notifyRepresentationUpdated(representation);
return representation;
}
public void updateRepresentationType(String aipId, String representationId, String type)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
AIP aip = retrieveAIP(aipId);
Iterator<Representation> it = aip.getRepresentations().iterator();
while (it.hasNext()) {
Representation representation = it.next();
if (representation.getId().equals(representationId)) {
representation.setType(type);
notifyRepresentationUpdated(representation);
break;
}
}
updateAIPMetadata(aip);
}
public Representation updateRepresentation(String aipId, String representationId, boolean original, String type,
StorageService sourceStorage, StoragePath sourcePath) throws RequestNotValidException, NotFoundException,
GenericException, AuthorizationDeniedException, ValidationException {
Representation representation;
// XXX possible optimization only creating new files, updating
// changed and removing deleted
StoragePath representationPath = ModelUtils.getRepresentationStoragePath(aipId, representationId);
storage.deleteResource(representationPath);
try {
storage.copy(sourceStorage, sourcePath, representationPath);
} catch (AlreadyExistsException e) {
throw new GenericException("Copying after delete gave an unexpected already exists exception", e);
}
// build return object
representation = new Representation(representationId, aipId, original, type);
notifyRepresentationUpdated(representation);
return representation;
}
public void deleteRepresentation(String aipId, String representationId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath representationPath = ModelUtils.getRepresentationStoragePath(aipId, representationId);
storage.deleteResource(representationPath);
// update AIP metadata
AIP aip = ResourceParseUtils.getAIPMetadata(getStorage(), aipId);
for (Iterator<Representation> it = aip.getRepresentations().iterator(); it.hasNext();) {
Representation representation = it.next();
if (representation.getId().equals(representationId)) {
it.remove();
break;
}
}
updateAIPMetadata(aip);
notifyRepresentationDeleted(aipId, representationId);
}
public CloseableIterable<OptionalWithCause<File>> listFilesUnder(String aipId, String representationId,
boolean recursive)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
final StoragePath storagePath = ModelUtils.getRepresentationDataStoragePath(aipId, representationId);
CloseableIterable<OptionalWithCause<File>> ret;
try {
final CloseableIterable<Resource> iterable = storage.listResourcesUnderDirectory(storagePath, recursive);
ret = ResourceParseUtils.convert(getStorage(), iterable, File.class);
} catch (NotFoundException e) {
// check if AIP exists
storage.getDirectory(ModelUtils.getAIPStoragePath(aipId));
// if no exception was sent by above method, return empty list
ret = new EmptyClosableIterable<>();
}
return ret;
}
/***************** File related *****************/
/************************************************/
public CloseableIterable<OptionalWithCause<File>> listFilesUnder(File f, boolean recursive)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
return listFilesUnder(f.getAipId(), f.getRepresentationId(), f.getPath(), f.getId(), recursive);
}
public CloseableIterable<OptionalWithCause<File>> listFilesUnder(String aipId, String representationId,
List<String> directoryPath, String fileId, boolean recursive)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
final StoragePath filePath = ModelUtils.getFileStoragePath(aipId, representationId, directoryPath, fileId);
final CloseableIterable<Resource> iterable = storage.listResourcesUnderDirectory(filePath, recursive);
return ResourceParseUtils.convert(getStorage(), iterable, File.class);
}
public File retrieveFile(String aipId, String representationId, List<String> directoryPath, String fileId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
File file;
StoragePath filePath = ModelUtils.getFileStoragePath(aipId, representationId, directoryPath, fileId);
Class<? extends Entity> entity = storage.getEntity(filePath);
if (entity.equals(Binary.class) || entity.equals(DefaultBinary.class)) {
Binary binary = storage.getBinary(filePath);
file = ResourceParseUtils.convertResourceToFile(binary);
} else {
Directory directory = storage.getDirectory(filePath);
file = ResourceParseUtils.convertResourceToFile(directory);
}
return file;
}
public File createFile(String aipId, String representationId, List<String> directoryPath, String fileId,
ContentPayload contentPayload) throws RequestNotValidException, GenericException, AlreadyExistsException,
AuthorizationDeniedException, NotFoundException {
return createFile(aipId, representationId, directoryPath, fileId, contentPayload, true);
}
public File createFile(String aipId, String representationId, List<String> directoryPath, String fileId,
ContentPayload contentPayload, boolean notify) throws RequestNotValidException, GenericException,
AlreadyExistsException, AuthorizationDeniedException, NotFoundException {
boolean asReference = false;
StoragePath filePath = ModelUtils.getFileStoragePath(aipId, representationId, directoryPath, fileId);
final Binary createdBinary = storage.createBinary(filePath, contentPayload, asReference);
File file = ResourceParseUtils.convertResourceToFile(createdBinary);
if (notify) {
notifyFileCreated(file);
}
return file;
}
public File createFile(String aipId, String representationId, List<String> directoryPath, String fileId,
String dirName, boolean notify) throws RequestNotValidException, GenericException, AlreadyExistsException,
AuthorizationDeniedException, NotFoundException {
StoragePath filePath = ModelUtils.getFileStoragePath(aipId, representationId, directoryPath, fileId);
final Directory createdDirectory = storage.createDirectory(DefaultStoragePath.parse(filePath, dirName));
File file = ResourceParseUtils.convertResourceToFile(createdDirectory);
if (notify) {
notifyFileCreated(file);
}
return file;
}
public File updateFile(String aipId, String representationId, List<String> directoryPath, String fileId,
ContentPayload contentPayload, boolean createIfNotExists, boolean notify)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
boolean asReference = false;
StoragePath filePath = ModelUtils.getFileStoragePath(aipId, representationId, directoryPath, fileId);
storage.updateBinaryContent(filePath, contentPayload, asReference, createIfNotExists);
Binary binaryUpdated = storage.getBinary(filePath);
File file = ResourceParseUtils.convertResourceToFile(binaryUpdated);
if (notify) {
notifyFileUpdated(file);
}
return file;
}
public File updateFile(File file, ContentPayload contentPayload, boolean createIfNotExists, boolean notify)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
return updateFile(file.getAipId(), file.getRepresentationId(), file.getPath(), file.getId(), contentPayload,
createIfNotExists, notify);
}
public void deleteFile(String aipId, String representationId, List<String> directoryPath, String fileId,
boolean notify) throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath filePath = ModelUtils.getFileStoragePath(aipId, representationId, directoryPath, fileId);
storage.deleteResource(filePath);
if (notify) {
notifyFileDeleted(aipId, representationId, directoryPath, fileId);
}
}
public File renameFolder(File folder, String newName, boolean replaceExisting, boolean reindexResources)
throws AlreadyExistsException, GenericException, NotFoundException, RequestNotValidException,
AuthorizationDeniedException {
Path basePath = RodaCoreFactory.getStoragePath();
StoragePath fileStoragePath = ModelUtils.getFileStoragePath(folder);
Path fullPath = basePath.resolve(FSUtils.getStoragePathAsString(fileStoragePath, false));
if (FSUtils.exists(fullPath)) {
FSUtils.move(fullPath, fullPath.getParent().resolve(newName), replaceExisting);
if (reindexResources) {
notifyAipUpdated(folder.getAipId());
}
return retrieveFile(folder.getAipId(), folder.getRepresentationId(), folder.getPath(), newName);
} else {
throw new NotFoundException("Folder was moved or does not exist");
}
}
public File moveFile(File file, String newAipId, String newRepresentationId, List<String> newDirectoryPath,
String newId, boolean replaceExisting, boolean reindexResources) throws AlreadyExistsException, GenericException,
NotFoundException, RequestNotValidException, AuthorizationDeniedException {
Path basePath = RodaCoreFactory.getStoragePath();
StoragePath fileStoragePath = ModelUtils.getFileStoragePath(file);
Path fullPath = basePath.resolve(FSUtils.getStoragePathAsString(fileStoragePath, false));
if (!FSUtils.exists(fullPath)) {
throw new NotFoundException("Some files/folders were moved or do not exist");
}
File newFile = new File(newId, newAipId, newRepresentationId, newDirectoryPath, file.isDirectory());
StoragePath newFileStoragePath = ModelUtils.getFileStoragePath(newFile);
Path newFullPath = basePath.resolve(FSUtils.getStoragePathAsString(newFileStoragePath, false));
FSUtils.move(fullPath, newFullPath, replaceExisting);
if (reindexResources) {
notifyRepresentationUpdated(retrieveRepresentation(newAipId, newRepresentationId));
if (!newAipId.equals(file.getAipId()) || !newRepresentationId.equals(file.getRepresentationId())) {
notifyRepresentationUpdated(retrieveRepresentation(file.getAipId(), file.getRepresentationId()));
}
}
return newFile;
}
/***************** Preservation related *****************/
/********************************************************/
public void createRepositoryEvent(PreservationEventType eventType, String eventDescription, PluginState outcomeState,
String outcomeText, String outcomeDetail, String agentName, boolean notify) {
createRepositoryEvent(eventType, eventDescription, null, null, outcomeState, outcomeText, outcomeDetail, agentName,
notify);
}
public void createRepositoryEvent(PreservationEventType eventType, String eventDescription,
List<LinkingIdentifier> sources, List<LinkingIdentifier> targets, PluginState outcomeState, String outcomeText,
String outcomeDetail, String agentName, boolean notify) {
createEvent(null, null, null, null, eventType, eventDescription, sources, targets, outcomeState, outcomeText,
outcomeDetail, agentName, notify);
}
public void createUpdateAIPEvent(String aipId, String representationId, List<String> filePath, String fileId,
PreservationEventType eventType, String eventDescription, PluginState outcomeState, String outcomeText,
String outcomeDetail, String agentName, boolean notify) {
createEvent(aipId, representationId, filePath, fileId, eventType, eventDescription, null, null, outcomeState,
outcomeText, outcomeDetail, agentName, notify);
}
public void createEvent(String aipId, String representationId, List<String> filePath, String fileId,
PreservationEventType eventType, String eventDescription, List<LinkingIdentifier> sources,
List<LinkingIdentifier> targets, PluginState outcomeState, String outcomeText, String outcomeDetail,
String agentName, boolean notify) {
try {
StringBuilder builder = new StringBuilder(outcomeText);
if (StringUtils.isNotBlank(outcomeDetail) && outcomeState.equals(PluginState.SUCCESS)) {
builder.append("\n").append("The following reason has been reported by the user: ").append(agentName)
.append("\n").append(outcomeDetail);
}
createEvent(aipId, representationId, filePath, fileId, eventType, eventDescription, sources, targets,
outcomeState, builder.toString(), "", Arrays.asList(IdUtils.getUserAgentId(agentName)), notify);
} catch (ValidationException | AlreadyExistsException | GenericException | NotFoundException
| RequestNotValidException | AuthorizationDeniedException e1) {
LOGGER.error("Could not create an event for: " + eventDescription, e1);
}
}
public void createEvent(String aipId, String representationId, List<String> filePath, String fileId,
PreservationEventType eventType, String eventDescription, List<LinkingIdentifier> sources,
List<LinkingIdentifier> targets, PluginState outcomeState, String outcomeDetail, String outcomeExtension,
List<String> agentIds, boolean notify) throws GenericException, ValidationException, NotFoundException,
RequestNotValidException, AuthorizationDeniedException, AlreadyExistsException {
String id = IdUtils.createPreservationMetadataId(PreservationMetadataType.EVENT);
ContentPayload premisEvent = PremisV3Utils.createPremisEventBinary(id, new Date(), eventType.toString(),
eventDescription, sources, targets, outcomeState.toString(), outcomeDetail, outcomeExtension, agentIds);
createPreservationMetadata(PreservationMetadataType.EVENT, id, aipId, representationId, filePath, fileId,
premisEvent, notify);
}
public PreservationMetadata retrievePreservationMetadata(String aipId, String representationId,
List<String> fileDirectoryPath, String fileId, PreservationMetadataType type) {
PreservationMetadata pm = new PreservationMetadata();
pm.setId(IdUtils.getPreservationId(type, aipId, representationId, fileDirectoryPath, fileId));
pm.setAipId(aipId);
pm.setRepresentationId(representationId);
pm.setFileDirectoryPath(fileDirectoryPath);
pm.setFileId(fileId);
pm.setType(type);
return pm;
}
public Binary retrievePreservationRepresentation(String aipId, String representationId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
String urn = IdUtils.getPreservationId(PreservationMetadataType.REPRESENTATION, aipId, representationId, null,
null);
StoragePath path = ModelUtils.getPreservationMetadataStoragePath(urn, PreservationMetadataType.REPRESENTATION,
aipId, representationId);
return storage.getBinary(path);
}
public Binary retrievePreservationRepresentation(String aipId, String representationId, List<String> filePath,
String fileId) throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
String urn = IdUtils.getPreservationFileId(fileId);
StoragePath path = ModelUtils.getPreservationMetadataStoragePath(urn, PreservationMetadataType.FILE, aipId,
representationId, filePath, fileId);
return storage.getBinary(path);
}
public Binary retrievePreservationFile(String aipId, String representationId, List<String> fileDirectoryPath,
String fileId) throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
return retrievePreservationFile(aipId, representationId, fileDirectoryPath, fileId, PreservationMetadataType.FILE);
}
public Binary retrievePreservationFile(String aipId, String representationId, List<String> fileDirectoryPath,
String fileId, PreservationMetadataType type)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
String identifier = null;
if (PreservationMetadataType.FILE.equals(type)) {
identifier = IdUtils.getPreservationFileId(fileId);
} else {
identifier = IdUtils.getPreservationId(type, aipId, representationId, fileDirectoryPath, fileId);
}
StoragePath filePath = ModelUtils.getPreservationMetadataStoragePath(identifier, type, aipId, representationId,
fileDirectoryPath, fileId);
return storage.getBinary(filePath);
}
public Binary retrievePreservationFile(File file)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
return retrievePreservationFile(file.getAipId(), file.getRepresentationId(), file.getPath(), file.getId());
}
public Binary retrievePreservationEvent(String aipId, String representationId, List<String> filePath, String fileId,
String preservationID)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath storagePath = ModelUtils.getPreservationMetadataStoragePath(preservationID,
PreservationMetadataType.EVENT, aipId, representationId, filePath, fileId);
return storage.getBinary(storagePath);
}
public Binary retrievePreservationAgent(String preservationID)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath storagePath = ModelUtils.getPreservationMetadataStoragePath(preservationID,
PreservationMetadataType.AGENT);
return storage.getBinary(storagePath);
}
public PreservationMetadata createPreservationMetadata(PreservationMetadataType type, String aipId,
String representationId, List<String> fileDirectoryPath, String fileId, ContentPayload payload, boolean notify)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException,
AlreadyExistsException {
String identifier = fileId;
if (!PreservationMetadataType.FILE.equals(type)) {
identifier = IdUtils.getFileId(aipId, representationId, fileDirectoryPath, fileId);
}
String urn = URNUtils.createRodaPreservationURN(type, identifier);
return createPreservationMetadata(type, urn, aipId, representationId, fileDirectoryPath, fileId, payload, notify);
}
public PreservationMetadata createPreservationMetadata(PreservationMetadataType type, String aipId,
List<String> fileDirectoryPath, String fileId, ContentPayload payload, boolean notify) throws GenericException,
NotFoundException, RequestNotValidException, AuthorizationDeniedException, AlreadyExistsException {
String id = IdUtils.getPreservationId(type, aipId, null, fileDirectoryPath, fileId);
return createPreservationMetadata(type, id, aipId, null, fileDirectoryPath, fileId, payload, notify);
}
public PreservationMetadata createPreservationMetadata(PreservationMetadataType type, String aipId,
String representationId, ContentPayload payload, boolean notify) throws GenericException, NotFoundException,
RequestNotValidException, AuthorizationDeniedException, AlreadyExistsException {
String id = IdUtils.getPreservationId(type, aipId, representationId, null, null);
return createPreservationMetadata(type, id, aipId, representationId, null, null, payload, notify);
}
public PreservationMetadata createPreservationMetadata(PreservationMetadataType type, String id,
ContentPayload payload, boolean notify) throws GenericException, NotFoundException, RequestNotValidException,
AuthorizationDeniedException, AlreadyExistsException {
return createPreservationMetadata(type, id, null, null, null, null, payload, notify);
}
public PreservationMetadata createPreservationMetadata(PreservationMetadataType type, String id, String aipId,
String representationId, List<String> fileDirectoryPath, String fileId, ContentPayload payload, boolean notify)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException,
AlreadyExistsException {
PreservationMetadata pm = new PreservationMetadata();
pm.setId(id);
pm.setAipId(aipId);
pm.setRepresentationId(representationId);
pm.setFileDirectoryPath(fileDirectoryPath);
pm.setFileId(fileId);
pm.setType(type);
StoragePath binaryPath = ModelUtils.getPreservationMetadataStoragePath(pm);
boolean asReference = false;
storage.createBinary(binaryPath, payload, asReference);
if (notify) {
notifyPreservationMetadataCreated(pm);
}
return pm;
}
public PreservationMetadata updatePreservationMetadata(String id, PreservationMetadataType type, String aipId,
String representationId, List<String> fileDirectoryPath, String fileId, ContentPayload payload, boolean notify)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
PreservationMetadata pm = new PreservationMetadata();
pm.setId(id);
pm.setType(type);
pm.setAipId(aipId);
pm.setRepresentationId(representationId);
pm.setFileDirectoryPath(fileDirectoryPath);
pm.setFileId(fileId);
StoragePath binaryPath = ModelUtils.getPreservationMetadataStoragePath(pm);
storage.updateBinaryContent(binaryPath, payload, false, true);
if (notify) {
notifyPreservationMetadataUpdated(pm);
}
return pm;
}
public void deletePreservationMetadata(PreservationMetadataType type, String aipId, String representationId,
String id, boolean notify)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
PreservationMetadata pm = new PreservationMetadata();
pm.setAipId(aipId);
pm.setId(id);
pm.setRepresentationId(representationId);
pm.setType(type);
StoragePath binaryPath = ModelUtils.getPreservationMetadataStoragePath(pm);
storage.deleteResource(binaryPath);
if (notify) {
notifyPreservationMetadataDeleted(pm);
}
}
public CloseableIterable<OptionalWithCause<PreservationMetadata>> listPreservationMetadata()
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
List<CloseableIterable<OptionalWithCause<PreservationMetadata>>> list = new ArrayList<>();
try {
StoragePath storagePath = ModelUtils.getPreservationRepositoryEventStoragePath();
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, true);
CloseableIterable<OptionalWithCause<PreservationMetadata>> pms = ResourceParseUtils.convert(getStorage(),
resources, PreservationMetadata.class);
list.add(pms);
} catch (NotFoundException e) {
// do nothing
}
try {
StoragePath storagePath = ModelUtils.getPreservationAgentStoragePath();
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, true);
CloseableIterable<OptionalWithCause<PreservationMetadata>> pms = ResourceParseUtils.convert(getStorage(),
resources, PreservationMetadata.class);
list.add(pms);
} catch (NotFoundException e) {
// do nothing
}
CloseableIterable<OptionalWithCause<AIP>> aips = listAIPs();
for (OptionalWithCause<AIP> oaip : aips) {
if (oaip.isPresent()) {
AIP aip = oaip.get();
StoragePath storagePath = ModelUtils.getAIPPreservationMetadataStoragePath(aip.getId());
CloseableIterable<OptionalWithCause<PreservationMetadata>> aipPreservationMetadata;
try {
boolean recursive = true;
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
aipPreservationMetadata = ResourceParseUtils.convert(getStorage(), resources, PreservationMetadata.class);
} catch (NotFoundException e) {
// check if AIP exists
storage.getDirectory(ModelUtils.getAIPStoragePath(aip.getId()));
// if no exception was sent by above method, return empty list
aipPreservationMetadata = new EmptyClosableIterable<>();
}
list.add(aipPreservationMetadata);
// list from all representations
for (Representation representation : aip.getRepresentations()) {
CloseableIterable<OptionalWithCause<PreservationMetadata>> rpm = listPreservationMetadata(aip.getId(),
representation.getId());
list.add(rpm);
}
}
}
return CloseableIterables.concat(list);
}
public CloseableIterable<OptionalWithCause<PreservationMetadata>> listPreservationMetadata(String aipId,
boolean includeRepresentations)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath storagePath = ModelUtils.getAIPPreservationMetadataStoragePath(aipId);
CloseableIterable<OptionalWithCause<PreservationMetadata>> aipPreservationMetadata;
try {
boolean recursive = true;
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
aipPreservationMetadata = ResourceParseUtils.convert(getStorage(), resources, PreservationMetadata.class);
} catch (NotFoundException e) {
// check if AIP exists
storage.getDirectory(ModelUtils.getAIPStoragePath(aipId));
// if no exception was sent by above method, return empty list
aipPreservationMetadata = new EmptyClosableIterable<>();
}
if (includeRepresentations) {
List<CloseableIterable<OptionalWithCause<PreservationMetadata>>> list = new ArrayList<>();
list.add(aipPreservationMetadata);
// list from all representations
AIP aip = retrieveAIP(aipId);
for (Representation representation : aip.getRepresentations()) {
CloseableIterable<OptionalWithCause<PreservationMetadata>> rpm = listPreservationMetadata(aipId,
representation.getId());
list.add(rpm);
}
return CloseableIterables.concat(list);
} else {
return aipPreservationMetadata;
}
}
public CloseableIterable<OptionalWithCause<PreservationMetadata>> listPreservationMetadata(String aipId,
String representationId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath storagePath = ModelUtils.getRepresentationPreservationMetadataStoragePath(aipId, representationId);
boolean recursive = true;
CloseableIterable<OptionalWithCause<PreservationMetadata>> ret;
try {
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
ret = ResourceParseUtils.convert(getStorage(), resources, PreservationMetadata.class);
} catch (NotFoundException e) {
// check if Representation exists
storage.getDirectory(ModelUtils.getRepresentationStoragePath(aipId, representationId));
// if no exception was sent by above method, return empty list
ret = new EmptyClosableIterable<>();
}
return ret;
}
public CloseableIterable<OptionalWithCause<PreservationMetadata>> listPreservationAgents()
throws RequestNotValidException, GenericException, AuthorizationDeniedException {
CloseableIterable<OptionalWithCause<PreservationMetadata>> ret;
StoragePath storagePath = ModelUtils.getPreservationAgentStoragePath();
try {
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, false);
ret = ResourceParseUtils.convert(getStorage(), resources, PreservationMetadata.class);
} catch (NotFoundException e) {
ret = new EmptyClosableIterable<>();
}
return ret;
}
public CloseableIterable<OptionalWithCause<PreservationMetadata>> listPreservationRepositoryEvents()
throws RequestNotValidException, GenericException, AuthorizationDeniedException {
CloseableIterable<OptionalWithCause<PreservationMetadata>> ret;
StoragePath storagePath = ModelUtils.getPreservationRepositoryEventStoragePath();
try {
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, false);
ret = ResourceParseUtils.convert(getStorage(), resources, PreservationMetadata.class);
} catch (NotFoundException e) {
ret = new EmptyClosableIterable<>();
}
return ret;
}
/***************** Other metadata related *****************/
/**********************************************************/
public Binary retrieveOtherMetadataBinary(OtherMetadata om)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
return retrieveOtherMetadataBinary(om.getAipId(), om.getRepresentationId(), om.getFileDirectoryPath(),
om.getFileId(), om.getFileSuffix(), om.getType());
}
public Binary retrieveOtherMetadataBinary(String aipId, String representationId, List<String> fileDirectoryPath,
String fileId, String fileSuffix, String type)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
Binary binary;
StoragePath binaryPath = ModelUtils.getOtherMetadataStoragePath(aipId, representationId, fileDirectoryPath, fileId,
fileSuffix, type);
binary = storage.getBinary(binaryPath);
return binary;
}
public OtherMetadata retrieveOtherMetadata(String aipId, String representationId, List<String> fileDirectoryPath,
String fileId, String fileSuffix, String type)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
try {
retrieveOtherMetadataBinary(aipId, representationId, fileDirectoryPath, fileId, fileSuffix, type);
String id = IdUtils.getOtherMetadataId(aipId, representationId, fileDirectoryPath, fileId);
return new OtherMetadata(id, type, aipId, representationId, fileDirectoryPath, fileId, fileSuffix);
} catch (RequestNotValidException | GenericException | NotFoundException | AuthorizationDeniedException e) {
throw e;
}
}
public OtherMetadata createOrUpdateOtherMetadata(String aipId, String representationId,
List<String> fileDirectoryPath, String fileId, String fileSuffix, String type, ContentPayload payload,
boolean notify) throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath binaryPath = ModelUtils.getOtherMetadataStoragePath(aipId, representationId, fileDirectoryPath, fileId,
fileSuffix, type);
boolean asReference = false;
boolean createIfNotExists = true;
try {
storage.createBinary(binaryPath, payload, asReference);
} catch (AlreadyExistsException e) {
storage.updateBinaryContent(binaryPath, payload, asReference, createIfNotExists);
}
String id = IdUtils.getOtherMetadataId(aipId, representationId, fileDirectoryPath, fileId);
OtherMetadata om = new OtherMetadata(id, type, aipId, representationId, fileDirectoryPath, fileId, fileSuffix);
if (notify) {
notifyOtherMetadataCreated(om);
}
return om;
}
public void deleteOtherMetadata(String aipId, String representationId, List<String> fileDirectoryPath, String fileId,
String fileSuffix, String type)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath binaryPath = ModelUtils.getOtherMetadataStoragePath(aipId, representationId, fileDirectoryPath, fileId,
fileSuffix, type);
storage.deleteResource(binaryPath);
}
public CloseableIterable<OptionalWithCause<OtherMetadata>> listOtherMetadata(String aipId, String type,
boolean includeRepresentations)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath storagePath = ModelUtils.getAIPOtherMetadataStoragePath(aipId, type);
boolean recursive = true;
CloseableIterable<OptionalWithCause<OtherMetadata>> aipOtherMetadata;
try {
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
aipOtherMetadata = ResourceParseUtils.convert(getStorage(), resources, OtherMetadata.class);
} catch (NotFoundException e) {
// check if AIP exists
storage.getDirectory(ModelUtils.getAIPStoragePath(aipId));
// if no exception was sent by above method, return empty list
aipOtherMetadata = new EmptyClosableIterable<>();
}
if (includeRepresentations) {
List<CloseableIterable<OptionalWithCause<OtherMetadata>>> list = new ArrayList<>();
list.add(aipOtherMetadata);
// list from all representations
AIP aip = retrieveAIP(aipId);
for (Representation representation : aip.getRepresentations()) {
CloseableIterable<OptionalWithCause<OtherMetadata>> representationOtherMetadata = listOtherMetadata(aipId,
representation.getId(), null, null, type);
list.add(representationOtherMetadata);
}
return CloseableIterables.concat(list);
} else {
return aipOtherMetadata;
}
}
public CloseableIterable<OptionalWithCause<OtherMetadata>> listOtherMetadata(String aipId, String representationId)
throws NotFoundException, GenericException, AuthorizationDeniedException, RequestNotValidException {
StoragePath storagePath = ModelUtils.getRepresentationOtherMetadataFolderStoragePath(aipId, representationId);
boolean recursive = true;
CloseableIterable<OptionalWithCause<OtherMetadata>> ret;
try {
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
ret = ResourceParseUtils.convert(getStorage(), resources, OtherMetadata.class);
} catch (NotFoundException e) {
// check if Representation exists
storage.getDirectory(ModelUtils.getRepresentationStoragePath(aipId, representationId));
// if no exception was sent by above method, return empty list
ret = new EmptyClosableIterable<>();
}
return ret;
}
public CloseableIterable<OptionalWithCause<OtherMetadata>> listOtherMetadata(String aipId, String representationId,
List<String> filePath, String fileId, String type)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
List<String> metadataPath = ModelUtils.getOtherMetadataStoragePath(aipId, representationId, filePath, fileId, type);
StoragePath storagePath = DefaultStoragePath.parse(metadataPath);
boolean recursive = true;
CloseableIterable<OptionalWithCause<OtherMetadata>> ret;
try {
CloseableIterable<Resource> resources = storage.listResourcesUnderDirectory(storagePath, recursive);
ret = ResourceParseUtils.convert(getStorage(), resources, OtherMetadata.class);
} catch (NotFoundException e) {
// check if Representation or AIP exists
if (representationId != null) {
storage.getDirectory(ModelUtils.getRepresentationStoragePath(aipId, representationId));
} else {
storage.getDirectory(ModelUtils.getAIPStoragePath(aipId));
}
// if no exception was sent by above method, return empty list
ret = new EmptyClosableIterable<>();
}
return ret;
}
/***************** Log entry related *****************/
/*****************************************************/
public void addLogEntry(LogEntry logEntry, Path logDirectory, boolean notify)
throws GenericException, RequestNotValidException, AuthorizationDeniedException, NotFoundException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String datePlusExtension = sdf.format(new Date()) + ".log";
Path logFile = logDirectory.resolve(datePlusExtension);
synchronized (logFileLock) {
// verify if file exists and if not, if older files exist (in that case,
// move them to storage)
if (!FSUtils.exists(logFile)) {
findOldLogsAndMoveThemToStorage(logDirectory, logFile);
try {
Files.createFile(logFile);
} catch (FileAlreadyExistsException e) {
// do nothing (just caused due to concurrency)
} catch (IOException e) {
throw new GenericException("Error creating file to write log into", e);
}
}
// write to log file
JsonUtils.appendObjectToFile(logEntry, logFile);
// emit event
if (notify) {
notifyLogEntryCreated(logEntry);
}
}
}
public void addLogEntry(LogEntry logEntry, Path logDirectory)
throws GenericException, RequestNotValidException, AuthorizationDeniedException, NotFoundException {
addLogEntry(logEntry, logDirectory, true);
}
public synchronized void findOldLogsAndMoveThemToStorage(Path logDirectory, Path currentLogFile)
throws RequestNotValidException, AuthorizationDeniedException, NotFoundException {
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(logDirectory)) {
for (Path path : directoryStream) {
if (!path.equals(currentLogFile)) {
try {
StoragePath logPath = ModelUtils.getLogStoragePath(path.getFileName().toString());
storage.createBinary(logPath, new FSPathContentPayload(path), false);
Files.delete(path);
} catch (IOException | GenericException | AlreadyExistsException e) {
LOGGER.error("Error archiving log file", e);
}
}
}
} catch (IOException e) {
LOGGER.error("Error listing directory for log files", e);
}
}
/***************** Users/Groups related *****************/
/********************************************************/
public User retrieveAuthenticatedUser(String name, String password)
throws GenericException, AuthenticationDeniedException {
return UserUtility.getLdapUtility().getAuthenticatedUser(name, password);
}
public User retrieveUserByName(String name) throws GenericException {
return UserUtility.getLdapUtility().getUser(name);
}
public User retrieveUserByEmail(String email) throws GenericException {
return UserUtility.getLdapUtility().getUserWithEmail(email);
}
public User registerUser(User user, String password, boolean notify)
throws GenericException, UserAlreadyExistsException, EmailAlreadyExistsException {
User registeredUser = UserUtility.getLdapUtility().registerUser(user, password);
if (notify) {
notifyUserCreated(registeredUser);
}
return registeredUser;
}
public User createUser(User user, boolean notify) throws GenericException, EmailAlreadyExistsException,
UserAlreadyExistsException, IllegalOperationException, NotFoundException {
return createUser(user, null, notify);
}
public User createUser(User user, String password, boolean notify) throws GenericException,
EmailAlreadyExistsException, UserAlreadyExistsException, IllegalOperationException, NotFoundException {
User createdUser = UserUtility.getLdapUtility().addUser(user);
if (password != null) {
UserUtility.getLdapUtility().setUserPassword(createdUser.getId(), password);
}
if (notify) {
notifyUserCreated(createdUser);
}
return createdUser;
}
public User updateUser(User user, String password, boolean notify)
throws GenericException, AlreadyExistsException, NotFoundException, AuthorizationDeniedException {
try {
if (password != null) {
UserUtility.getLdapUtility().setUserPassword(user.getId(), password);
}
User updatedUser = UserUtility.getLdapUtility().modifyUser(user);
if (notify) {
notifyUserUpdated(updatedUser);
}
return updatedUser;
} catch (IllegalOperationException e) {
throw new AuthorizationDeniedException("Illegal operation", e);
}
}
public User updateMyUser(User user, String password, boolean notify)
throws GenericException, AlreadyExistsException, NotFoundException, AuthorizationDeniedException {
try {
User updatedUser = UserUtility.getLdapUtility().modifySelfUser(user, password);
if (notify) {
notifyUserUpdated(updatedUser);
}
return updatedUser;
} catch (IllegalOperationException e) {
throw new AuthorizationDeniedException("Illegal operation", e);
}
}
public void deleteUser(String id, boolean notify) throws GenericException, AuthorizationDeniedException {
try {
UserUtility.getLdapUtility().removeUser(id);
if (notify) {
notifyUserDeleted(id);
}
} catch (IllegalOperationException e) {
throw new AuthorizationDeniedException("Illegal operation", e);
}
}
public List<User> listUsers() throws GenericException {
return UserUtility.getLdapUtility().getUsers();
}
public RODAMember retrieveRODAMember(String name) throws GenericException {
RODAMember member = null;
try {
member = UserUtility.getLdapUtility().getUser(name);
} catch (GenericException e) {
try {
member = UserUtility.getLdapUtility().getGroup(name);
} catch (GenericException | NotFoundException e1) {
LOGGER.error("Could not retrieve any user or group with name: {}", name);
throw e;
}
}
return member;
}
public User retrieveUser(String name) throws GenericException {
return UserUtility.getLdapUtility().getUser(name);
}
public Group retrieveGroup(String name) throws GenericException, NotFoundException {
return UserUtility.getLdapUtility().getGroup(name);
}
public Group createGroup(Group group, boolean notify) throws GenericException, AlreadyExistsException {
Group createdGroup = UserUtility.getLdapUtility().addGroup(group);
if (notify) {
notifyGroupCreated(createdGroup);
}
return createdGroup;
}
public Group updateGroup(final Group group, boolean notify)
throws GenericException, NotFoundException, AuthorizationDeniedException {
try {
Group updatedGroup = UserUtility.getLdapUtility().modifyGroup(group);
if (notify) {
notifyGroupUpdated(updatedGroup);
}
return updatedGroup;
} catch (IllegalOperationException e) {
throw new AuthorizationDeniedException("Illegal operation", e);
}
}
public void deleteGroup(String id, boolean notify) throws GenericException, AuthorizationDeniedException {
try {
UserUtility.getLdapUtility().removeGroup(id);
if (notify) {
notifyGroupDeleted(id);
}
} catch (IllegalOperationException e) {
throw new AuthorizationDeniedException("Illegal operation", e);
}
}
public List<Group> listGroups() throws GenericException {
return UserUtility.getLdapUtility().getGroups();
}
public User confirmUserEmail(String username, String email, String emailConfirmationToken, boolean useModel,
boolean notify) throws NotFoundException, InvalidTokenException, GenericException {
User user = null;
if (useModel) {
user = UserUtility.getLdapUtility().confirmUserEmail(username, email, emailConfirmationToken);
}
if (user != null && notify) {
notifyUserUpdated(user);
}
return user;
}
public User requestPasswordReset(String username, String email, boolean useModel, boolean notify)
throws IllegalOperationException, NotFoundException, GenericException {
User user = null;
if (useModel) {
user = UserUtility.getLdapUtility().requestPasswordReset(username, email);
}
if (user != null && notify) {
notifyUserUpdated(user);
}
return user;
}
public User resetUserPassword(String username, String password, String resetPasswordToken, boolean useModel,
boolean notify) throws NotFoundException, InvalidTokenException, IllegalOperationException, GenericException {
User user = null;
if (useModel) {
user = UserUtility.getLdapUtility().resetUserPassword(username, password, resetPasswordToken);
}
if (user != null && notify) {
notifyUserUpdated(user);
}
return user;
}
/***************** Jobs related *****************/
/************************************************/
public void createJob(Job job)
throws GenericException, RequestNotValidException, NotFoundException, AuthorizationDeniedException {
createOrUpdateJob(job);
// try to create directory for this job in job report container
try {
StoragePath jobReportsPath = ModelUtils.getJobReportsStoragePath(job.getId());
storage.createDirectory(jobReportsPath);
} catch (AlreadyExistsException e) {
// do nothing & carry on
} catch (RequestNotValidException | AuthorizationDeniedException e) {
throw new GenericException("Error creating/updating job report", e);
}
}
public void createOrUpdateJob(Job job)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
// create or update job in storage
String jobAsJson = JsonUtils.getJsonFromObject(job);
StoragePath jobPath = ModelUtils.getJobStoragePath(job.getId());
storage.updateBinaryContent(jobPath, new StringContentPayload(jobAsJson), false, true);
// index it
notifyJobCreatedOrUpdated(job, false);
}
public Job retrieveJob(String jobId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath jobPath = ModelUtils.getJobStoragePath(jobId);
Binary binary = storage.getBinary(jobPath);
Job ret;
InputStream inputStream = null;
try {
inputStream = binary.getContent().createInputStream();
ret = JsonUtils.getObjectFromJson(inputStream, Job.class);
} catch (IOException | GenericException e) {
throw new GenericException("Error reading job: " + jobId, e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return ret;
}
public void deleteJob(String jobId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath jobPath = ModelUtils.getJobStoragePath(jobId);
// remove it from storage
storage.deleteResource(jobPath);
// remove it from index
notifyJobDeleted(jobId);
}
public Report retrieveJobReport(String jobId, String givenId, boolean generateId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
String id = givenId;
if (generateId) {
id = IdUtils.getJobReportId(jobId, givenId);
}
StoragePath jobReportPath = ModelUtils.getJobReportStoragePath(jobId, id);
Binary binary = storage.getBinary(jobReportPath);
Report ret;
InputStream inputStream = null;
try {
inputStream = binary.getContent().createInputStream();
ret = JsonUtils.getObjectFromJson(inputStream, Report.class);
} catch (IOException e) {
throw new GenericException("Error reading job report", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return ret;
}
public void createOrUpdateJobReport(Report jobReport, Job job) throws GenericException {
// create job report in storage
try {
String jobReportAsJson = JsonUtils.getJsonFromObject(jobReport);
StoragePath jobReportPath = ModelUtils.getJobReportStoragePath(jobReport.getJobId(), jobReport.getId());
storage.updateBinaryContent(jobReportPath, new StringContentPayload(jobReportAsJson), false, true);
} catch (GenericException | RequestNotValidException | AuthorizationDeniedException | NotFoundException e) {
LOGGER.error("Error creating/updating job report in storage", e);
}
// index it
notifyJobReportCreatedOrUpdated(jobReport, job);
}
public void deleteJobReport(String jobId, String jobReportId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath jobReportPath = ModelUtils.getJobReportStoragePath(jobId, jobReportId);
// remove it from storage
storage.deleteResource(jobReportPath);
// remove it from index
notifyJobReportDeleted(jobReportId);
}
public void updateAIPPermissions(AIP aip, String updatedBy)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
aip.setUpdatedBy(updatedBy);
aip.setUpdatedOn(new Date());
updateAIPMetadata(aip);
notifyAipPermissionsUpdated(aip);
}
public void updateDIPPermissions(DIP dip)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
dip.setLastModified(new Date());
updateDIPMetadata(dip);
notifyDipPermissionsUpdated(dip);
}
public void deleteTransferredResource(TransferredResource transferredResource) {
FSUtils.deletePathQuietly(Paths.get(transferredResource.getFullPath()));
notifyTransferredResourceDeleted(transferredResource.getUUID());
}
/***************** Risk related *****************/
/************************************************/
public Risk createRisk(Risk risk, boolean commit) throws GenericException {
try {
if (risk.getId() == null) {
risk.setId(IdUtils.createUUID());
}
risk.setCreatedOn(new Date());
risk.setUpdatedOn(new Date());
String riskAsJson = JsonUtils.getJsonFromObject(risk);
StoragePath riskPath = ModelUtils.getRiskStoragePath(risk.getId());
storage.createBinary(riskPath, new StringContentPayload(riskAsJson), false);
} catch (GenericException | RequestNotValidException | AuthorizationDeniedException | NotFoundException
| AlreadyExistsException e) {
LOGGER.error("Error creating risk in storage", e);
}
notifyRiskCreatedOrUpdated(risk, 0, commit);
return risk;
}
public Risk updateRisk(Risk risk, Map<String, String> properties, boolean commit, int incidences)
throws GenericException {
try {
risk.setUpdatedOn(new Date());
String riskAsJson = JsonUtils.getJsonFromObject(risk);
StoragePath riskPath = ModelUtils.getRiskStoragePath(risk.getId());
// Create version snapshot
if (properties != null && !properties.isEmpty()) {
storage.createBinaryVersion(riskPath, properties);
}
storage.updateBinaryContent(riskPath, new StringContentPayload(riskAsJson), false, true);
} catch (GenericException | RequestNotValidException | AuthorizationDeniedException | NotFoundException e) {
LOGGER.error("Error updating risk in storage", e);
}
notifyRiskCreatedOrUpdated(risk, incidences, commit);
return risk;
}
public void deleteRisk(String riskId, boolean commit)
throws GenericException, NotFoundException, AuthorizationDeniedException, RequestNotValidException {
StoragePath riskPath = ModelUtils.getRiskStoragePath(riskId);
storage.deleteResource(riskPath);
notifyRiskDeleted(riskId, commit);
}
public Risk retrieveRisk(String riskId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath riskPath = ModelUtils.getRiskStoragePath(riskId);
Binary binary = storage.getBinary(riskPath);
Risk ret;
InputStream inputStream = null;
try {
inputStream = binary.getContent().createInputStream();
ret = JsonUtils.getObjectFromJson(inputStream, Risk.class);
} catch (IOException e) {
throw new GenericException("Error reading risk", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return ret;
}
public CloseableIterable<OptionalWithCause<Risk>> listRisks()
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
final boolean recursive = false;
final CloseableIterable<Resource> resourcesIterable = storage
.listResourcesUnderContainer(ModelUtils.getRiskContainerPath(), recursive);
return ResourceParseUtils.convert(getStorage(), resourcesIterable, Risk.class);
}
public BinaryVersion retrieveVersion(String id, String versionId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath binaryPath = ModelUtils.getRiskStoragePath(id);
return storage.getBinaryVersion(binaryPath, versionId);
}
public BinaryVersion revertRiskVersion(String riskId, String versionId, Map<String, String> properties,
boolean commit, int incidences)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath binaryPath = ModelUtils.getRiskStoragePath(riskId);
BinaryVersion currentVersion = storage.createBinaryVersion(binaryPath, properties);
storage.revertBinaryVersion(binaryPath, versionId);
notifyRiskCreatedOrUpdated(retrieveRisk(riskId), incidences, commit);
return currentVersion;
}
public RiskIncidence createRiskIncidence(RiskIncidence riskIncidence, boolean commit) throws GenericException {
try {
riskIncidence.setId(IdUtils.createUUID());
riskIncidence.setDetectedOn(new Date());
String riskIncidenceAsJson = JsonUtils.getJsonFromObject(riskIncidence);
StoragePath riskIncidencePath = ModelUtils.getRiskIncidenceStoragePath(riskIncidence.getId());
storage.createBinary(riskIncidencePath, new StringContentPayload(riskIncidenceAsJson), false);
} catch (GenericException | RequestNotValidException | AuthorizationDeniedException | NotFoundException
| AlreadyExistsException e) {
LOGGER.error("Error creating risk incidence in storage", e);
}
notifyRiskIncidenceCreatedOrUpdated(riskIncidence, commit);
return riskIncidence;
}
public RiskIncidence updateRiskIncidence(RiskIncidence riskIncidence, boolean commit) throws GenericException {
try {
riskIncidence.setRiskId(riskIncidence.getRiskId());
String riskIncidenceAsJson = JsonUtils.getJsonFromObject(riskIncidence);
StoragePath riskIncidencePath = ModelUtils.getRiskIncidenceStoragePath(riskIncidence.getId());
storage.updateBinaryContent(riskIncidencePath, new StringContentPayload(riskIncidenceAsJson), false, true);
} catch (GenericException | RequestNotValidException | AuthorizationDeniedException | NotFoundException e) {
LOGGER.error("Error updating risk incidence in storage", e);
}
notifyRiskIncidenceCreatedOrUpdated(riskIncidence, commit);
return riskIncidence;
}
public void deleteRiskIncidence(String riskIncidenceId, boolean commit)
throws GenericException, NotFoundException, AuthorizationDeniedException, RequestNotValidException {
StoragePath riskIncidencePath = ModelUtils.getRiskIncidenceStoragePath(riskIncidenceId);
storage.deleteResource(riskIncidencePath);
notifyRiskIncidenceDeleted(riskIncidenceId, commit);
}
public RiskIncidence retrieveRiskIncidence(String incidenceId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath riskIncidencePath = ModelUtils.getRiskIncidenceStoragePath(incidenceId);
Binary binary = storage.getBinary(riskIncidencePath);
RiskIncidence ret;
InputStream inputStream = null;
try {
inputStream = binary.getContent().createInputStream();
ret = JsonUtils.getObjectFromJson(inputStream, RiskIncidence.class);
} catch (IOException e) {
throw new GenericException("Error reading risk incidence", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return ret;
}
/***************** Format related *****************/
/************************************************/
public Format createFormat(Format format, boolean commit) throws GenericException {
try {
format.setId(IdUtils.createUUID());
String formatAsJson = JsonUtils.getJsonFromObject(format);
StoragePath formatPath = ModelUtils.getFormatStoragePath(format.getId());
storage.createBinary(formatPath, new StringContentPayload(formatAsJson), false);
} catch (GenericException | RequestNotValidException | AuthorizationDeniedException | NotFoundException
| AlreadyExistsException e) {
LOGGER.error("Error creating format in storage", e);
}
notifyFormatCreatedOrUpdated(format, commit);
return format;
}
public Format updateFormat(Format format, boolean commit) throws GenericException {
try {
String formatAsJson = JsonUtils.getJsonFromObject(format);
StoragePath formatPath = ModelUtils.getFormatStoragePath(format.getId());
storage.updateBinaryContent(formatPath, new StringContentPayload(formatAsJson), false, true);
} catch (GenericException | RequestNotValidException | AuthorizationDeniedException | NotFoundException e) {
LOGGER.error("Error updating format in storage", e);
}
notifyFormatCreatedOrUpdated(format, commit);
return format;
}
public void deleteFormat(String formatId, boolean commit)
throws GenericException, NotFoundException, AuthorizationDeniedException, RequestNotValidException {
StoragePath formatPath = ModelUtils.getFormatStoragePath(formatId);
storage.deleteResource(formatPath);
notifyFormatDeleted(formatId, commit);
}
public Format retrieveFormat(String formatId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
StoragePath formatPath = ModelUtils.getFormatStoragePath(formatId);
Binary binary = storage.getBinary(formatPath);
Format ret;
InputStream inputStream = null;
try {
inputStream = binary.getContent().createInputStream();
ret = JsonUtils.getObjectFromJson(inputStream, Format.class);
} catch (IOException e) {
throw new GenericException("Error reading format", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return ret;
}
/***************** Notification related *****************/
/**********************************************************/
public Notification createNotification(final Notification notification, final NotificationProcessor processor)
throws GenericException, AuthorizationDeniedException {
notification.setId(IdUtils.createUUID());
notification.setAcknowledgeToken(IdUtils.createUUID());
Notification processedNotification = processor.processNotification(this, notification);
try {
String notificationAsJson = JsonUtils.getJsonFromObject(processedNotification);
StoragePath notificationPath = ModelUtils.getNotificationStoragePath(processedNotification.getId());
storage.createBinary(notificationPath, new StringContentPayload(notificationAsJson), false);
notifyNotificationCreatedOrUpdated(processedNotification);
} catch (NotFoundException | RequestNotValidException | AlreadyExistsException e) {
LOGGER.error("Error creating notification in storage", e);
throw new GenericException(e);
}
return processedNotification;
}
public Notification updateNotification(Notification notification)
throws GenericException, NotFoundException, AuthorizationDeniedException {
try {
String notificationAsJson = JsonUtils.getJsonFromObject(notification);
StoragePath notificationPath = ModelUtils.getNotificationStoragePath(notification.getId());
storage.updateBinaryContent(notificationPath, new StringContentPayload(notificationAsJson), false, true);
} catch (GenericException | RequestNotValidException e) {
LOGGER.error("Error updating notification in storage", e);
throw new GenericException(e);
}
notifyNotificationCreatedOrUpdated(notification);
return notification;
}
public void deleteNotification(String notificationId)
throws GenericException, NotFoundException, AuthorizationDeniedException {
try {
StoragePath notificationPath = ModelUtils.getNotificationStoragePath(notificationId);
storage.deleteResource(notificationPath);
notifyNotificationDeleted(notificationId);
} catch (RequestNotValidException e) {
LOGGER.error("Error deleting notification", e);
throw new GenericException(e);
}
}
public Notification retrieveNotification(String notificationId)
throws GenericException, NotFoundException, AuthorizationDeniedException {
InputStream inputStream = null;
Notification ret;
try {
StoragePath notificationPath = ModelUtils.getNotificationStoragePath(notificationId);
Binary binary = storage.getBinary(notificationPath);
inputStream = binary.getContent().createInputStream();
ret = JsonUtils.getObjectFromJson(inputStream, Notification.class);
} catch (IOException | RequestNotValidException e) {
throw new GenericException("Error reading notification", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return ret;
}
public void acknowledgeNotification(String notificationId, String token)
throws GenericException, NotFoundException, AuthorizationDeniedException {
Notification notification = this.retrieveNotification(notificationId);
String ackToken = token.substring(0, 36);
String emailToken = token.substring(36);
if (notification.getAcknowledgeToken().equals(ackToken)) {
for (String recipient : notification.getRecipientUsers()) {
String recipientUUID = IdUtils.createUUID(recipient);
if (recipientUUID.equals(emailToken)) {
DateFormat df = DateFormat.getDateTimeInstance();
String ackDate = df.format(new Date());
notification.addAcknowledgedUser(recipient, ackDate);
notification.setAcknowledged(true);
this.updateNotification(notification);
}
}
}
}
/***************** DIP related *****************/
/**********************************************************/
public CloseableIterable<OptionalWithCause<DIPFile>> listDIPFilesUnder(DIPFile f, boolean recursive)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
return listDIPFilesUnder(f.getDipId(), f.getPath(), f.getId(), recursive);
}
public CloseableIterable<OptionalWithCause<DIPFile>> listDIPFilesUnder(String dipId, List<String> directoryPath,
String fileId, boolean recursive)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
final StoragePath filePath = ModelUtils.getDIPFileStoragePath(dipId, directoryPath, fileId);
final CloseableIterable<Resource> iterable = storage.listResourcesUnderDirectory(filePath, recursive);
return ResourceParseUtils.convert(getStorage(), iterable, DIPFile.class);
}
public CloseableIterable<OptionalWithCause<DIPFile>> listDIPFilesUnder(String dipId, boolean recursive)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
final StoragePath storagePath = ModelUtils.getDIPDataStoragePath(dipId);
CloseableIterable<OptionalWithCause<DIPFile>> ret;
try {
final CloseableIterable<Resource> iterable = storage.listResourcesUnderDirectory(storagePath, recursive);
ret = ResourceParseUtils.convert(getStorage(), iterable, DIPFile.class);
} catch (NotFoundException e) {
// check if AIP exists
storage.getDirectory(ModelUtils.getDIPStoragePath(dipId));
// if no exception was sent by above method, return empty list
ret = new EmptyClosableIterable<>();
}
return ret;
}
private void createDIPMetadata(DIP dip, StoragePath storagePath) throws RequestNotValidException, GenericException,
AlreadyExistsException, AuthorizationDeniedException, NotFoundException {
String json = JsonUtils.getJsonFromObject(dip);
DefaultStoragePath metadataStoragePath = DefaultStoragePath.parse(storagePath,
RodaConstants.STORAGE_DIP_METADATA_FILENAME);
boolean asReference = false;
storage.createBinary(metadataStoragePath, new StringContentPayload(json), asReference);
}
private void updateDIPMetadata(DIP dip, StoragePath storagePath)
throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException {
String json = JsonUtils.getJsonFromObject(dip);
DefaultStoragePath metadataStoragePath = DefaultStoragePath.parse(storagePath,
RodaConstants.STORAGE_DIP_METADATA_FILENAME);
boolean asReference = false;
boolean createIfNotExists = true;
storage.updateBinaryContent(metadataStoragePath, new StringContentPayload(json), asReference, createIfNotExists);
}
public DIP createDIP(DIP dip, boolean notify) throws GenericException, AuthorizationDeniedException {
try {
Directory directory;
if (StringUtils.isNotBlank(dip.getId())) {
try {
directory = storage
.createDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_DIP, dip.getId()));
} catch (AlreadyExistsException | GenericException | AuthorizationDeniedException e) {
directory = storage.createRandomDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_DIP));
dip.setId(directory.getStoragePath().getName());
}
} else {
directory = storage.createRandomDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_DIP));
dip.setId(directory.getStoragePath().getName());
}
dip.setDateCreated(new Date());
dip.setLastModified(new Date());
createDIPMetadata(dip, directory.getStoragePath());
if (notify) {
notifyDIPCreated(dip, false);
}
return dip;
} catch (NotFoundException | RequestNotValidException | AlreadyExistsException e) {
LOGGER.error("Error creating DIP in storage", e);
throw new GenericException(e);
}
}
public DIP updateDIP(DIP dip) throws GenericException, NotFoundException, AuthorizationDeniedException {
try {
dip.setLastModified(new Date());
updateDIPMetadata(dip, DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_DIP, dip.getId()));
} catch (GenericException | RequestNotValidException e) {
LOGGER.error("Error updating DIP in storage", e);
throw new GenericException(e);
}
notifyDIPUpdated(dip, false);
return dip;
}
public void deleteDIP(String dipId) throws GenericException, NotFoundException, AuthorizationDeniedException {
try {
// deleting external service if existing
DIP dip = retrieveDIP(dipId);
OptionalWithCause<String> deleteURL = DIPUtils.getCompleteDeleteExternalURL(dip);
Optional<String> httpMethod = DIPUtils.getDeleteMethod(dip);
if (deleteURL.isPresent() && httpMethod.isPresent()) {
String url = deleteURL.get();
Optional<Pair<String, String>> credentials = DIPUtils.getDeleteCredentials(dip);
String method = httpMethod.get();
HTTPUtility.doMethod(url, method, credentials);
}
StoragePath dipPath = ModelUtils.getDIPStoragePath(dipId);
storage.deleteResource(dipPath);
notifyDIPDeleted(dipId, false);
} catch (RequestNotValidException e) {
LOGGER.error("Error deleting DIP", e);
throw new GenericException(e);
}
}
public DIP retrieveDIP(String dipId) throws GenericException, NotFoundException, AuthorizationDeniedException {
InputStream inputStream = null;
DIP ret;
try {
StoragePath dipPath = ModelUtils.getDIPMetadataStoragePath(dipId);
Binary binary = storage.getBinary(dipPath);
inputStream = binary.getContent().createInputStream();
ret = JsonUtils.getObjectFromJson(inputStream, DIP.class);
} catch (IOException | RequestNotValidException e) {
throw new GenericException("Error reading DIP", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return ret;
}
public DIPFile createDIPFile(String dipId, List<String> directoryPath, String fileId, long size,
ContentPayload contentPayload, boolean notify) throws RequestNotValidException, GenericException,
AlreadyExistsException, AuthorizationDeniedException, NotFoundException {
boolean asReference = false;
StoragePath filePath = ModelUtils.getDIPFileStoragePath(dipId, directoryPath, fileId);
final Binary createdBinary = storage.createBinary(filePath, contentPayload, asReference);
DIPFile file = ResourceParseUtils.convertResourceToDIPFile(createdBinary);
file.setUUID(IdUtils.getDIPFileId(file));
file.setSize(size);
if (notify) {
notifyDIPFileCreated(file);
}
return file;
}
public DIPFile createDIPFile(String dipId, List<String> directoryPath, String fileId, String dirName, boolean notify)
throws RequestNotValidException, GenericException, AlreadyExistsException, AuthorizationDeniedException,
NotFoundException {
StoragePath filePath = ModelUtils.getDIPFileStoragePath(dipId, directoryPath, fileId);
final Directory createdDirectory = storage.createDirectory(DefaultStoragePath.parse(filePath, dirName));
DIPFile file = ResourceParseUtils.convertResourceToDIPFile(createdDirectory);
if (notify) {
notifyDIPFileCreated(file);
}
return file;
}
public DIPFile updateDIPFile(String dipId, List<String> directoryPath, String oldFileId, String fileId, long size,
ContentPayload contentPayload, boolean createIfNotExists, boolean notify) throws RequestNotValidException,
GenericException, NotFoundException, AuthorizationDeniedException, AlreadyExistsException {
DIPFile file;
boolean asReference = false;
StoragePath oldFilePath = ModelUtils.getDIPFileStoragePath(dipId, directoryPath, oldFileId);
storage.deleteResource(oldFilePath);
StoragePath filePath = ModelUtils.getDIPFileStoragePath(dipId, directoryPath, fileId);
final Binary binary = storage.createBinary(filePath, contentPayload, asReference);
file = ResourceParseUtils.convertResourceToDIPFile(binary);
if (notify) {
notifyDIPFileDeleted(dipId, directoryPath, oldFileId);
notifyDIPFileCreated(file);
}
return file;
}
public void deleteDIPFile(String dipId, List<String> directoryPath, String fileId, boolean notify)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
StoragePath filePath = ModelUtils.getDIPFileStoragePath(dipId, directoryPath, fileId);
storage.deleteResource(filePath);
if (notify) {
notifyDIPFileDeleted(dipId, directoryPath, fileId);
}
}
public DIPFile retrieveDIPFile(String dipId, List<String> directoryPath, String fileId)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
DIPFile file;
StoragePath filePath = ModelUtils.getDIPFileStoragePath(dipId, directoryPath, fileId);
Class<? extends Entity> entity = storage.getEntity(filePath);
if (entity.equals(Binary.class) || entity.equals(DefaultBinary.class)) {
Binary binary = storage.getBinary(filePath);
file = ResourceParseUtils.convertResourceToDIPFile(binary);
} else {
Directory directory = storage.getDirectory(filePath);
file = ResourceParseUtils.convertResourceToDIPFile(directory);
}
return file;
}
/****************************************************************
*
* OTHER DIRECTORIES (submission, documentation, schemas)
*
*********************************************************/
/**
*
*/
public Directory getSubmissionDirectory(String aipId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
return storage.getDirectory(ModelUtils.getSubmissionStoragePath(aipId));
}
public void createSubmission(StorageService submissionStorage, StoragePath submissionStoragePath, String aipId)
throws AlreadyExistsException, GenericException, RequestNotValidException, NotFoundException,
AuthorizationDeniedException {
storage.copy(submissionStorage, submissionStoragePath, ModelUtils.getSubmissionStoragePath(aipId));
}
public void createSubmission(Path submissionPath, String aipId) throws AlreadyExistsException, GenericException,
RequestNotValidException, NotFoundException, AuthorizationDeniedException {
StoragePath submissionStoragePath = DefaultStoragePath.parse(ModelUtils.getSubmissionStoragePath(aipId),
submissionPath.getFileName().toString());
storage.createBinary(submissionStoragePath, new FSPathContentPayload(submissionPath), false);
}
public Directory getDocumentationDirectory(String aipId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
return storage.getDirectory(ModelUtils.getDocumentationStoragePath(aipId));
}
public Directory getDocumentationDirectory(String aipId, String representationId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
return storage.getDirectory(ModelUtils.getDocumentationStoragePath(aipId, representationId));
}
public File createDocumentation(String aipId, String representationId, List<String> directoryPath, String fileId,
ContentPayload contentPayload) throws RequestNotValidException, GenericException, AlreadyExistsException,
AuthorizationDeniedException, NotFoundException {
boolean asReference = false;
StoragePath filePath = ModelUtils.getDocumentationStoragePath(aipId, representationId, directoryPath, fileId);
final Binary createdBinary = storage.createBinary(filePath, contentPayload, asReference);
return ResourceParseUtils.convertResourceToFile(createdBinary);
}
public Directory getSchemasDirectory(String aipId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
return storage.getDirectory(ModelUtils.getSchemasStoragePath(aipId));
}
public Directory getSchemasDirectory(String aipId, String representationId)
throws RequestNotValidException, NotFoundException, GenericException, AuthorizationDeniedException {
return storage.getDirectory(ModelUtils.getSchemasStoragePath(aipId, representationId));
}
public File createSchema(String aipId, String representationId, List<String> directoryPath, String fileId,
ContentPayload contentPayload) throws RequestNotValidException, GenericException, AlreadyExistsException,
AuthorizationDeniedException, NotFoundException {
boolean asReference = false;
StoragePath filePath = ModelUtils.getSchemaStoragePath(aipId, representationId, directoryPath, fileId);
final Binary createdBinary = storage.createBinary(filePath, contentPayload, asReference);
return ResourceParseUtils.convertResourceToFile(createdBinary);
}
private CloseableIterable<OptionalWithCause<Representation>> listRepresentations()
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
CloseableIterable<OptionalWithCause<AIP>> aips = listAIPs();
return CloseableIterables.concat(aips, aip -> {
if (aip.isPresent()) {
List<Representation> representations = aip.get().getRepresentations();
return CloseableIterables
.fromList(representations.stream().map(rep -> OptionalWithCause.of(rep)).collect(Collectors.toList()));
} else {
return CloseableIterables.empty();
}
});
}
private CloseableIterable<OptionalWithCause<File>> listFiles()
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
CloseableIterable<OptionalWithCause<Representation>> representations = listRepresentations();
return CloseableIterables.concat(representations, rep -> {
if (rep.isPresent()) {
Representation representation = rep.get();
try {
return listFilesUnder(representation.getAipId(), representation.getId(), true);
} catch (RODAException e) {
LOGGER.error("Error listing files under representation: {}", representation.getId(), e);
return CloseableIterables.empty();
}
} else {
return CloseableIterables.empty();
}
});
}
private CloseableIterable<OptionalWithCause<DIPFile>> listDIPFiles() throws RODAException {
CloseableIterable<OptionalWithCause<DIP>> dips = list(DIP.class);
return CloseableIterables.concat(dips, odip -> {
CloseableIterable<?> dipFiles = CloseableIterables.empty();
if (odip.isPresent()) {
DIP dip = odip.get();
try {
dipFiles = listDIPFilesUnder(dip.getId(), true);
} catch (RODAException e) {
LOGGER.error("Error getting DIP files under a DIP " + dip.getId());
}
}
return (CloseableIterable<OptionalWithCause<DIPFile>>) dipFiles;
});
}
public <T extends IsRODAObject> Optional<LiteRODAObject> retrieveLiteFromObject(T object) {
return LiteRODAObjectFactory.get(object);
}
public <T extends IsModelObject> OptionalWithCause<T> retrieveObjectFromLite(LiteRODAObject liteRODAObject) {
return LiteRODAObjectFactory.get(this, liteRODAObject);
}
public TransferredResource retrieveTransferredResource(String fullPath) {
TransferredResourcesScanner transferredResourcesScanner = RodaCoreFactory.getTransferredResourcesScanner();
return transferredResourcesScanner.instantiateTransferredResource(Paths.get(fullPath),
transferredResourcesScanner.getBasePath());
}
@SuppressWarnings("unchecked")
public <T extends IsRODAObject> CloseableIterable<OptionalWithCause<T>> list(Class<T> objectClass)
throws RODAException {
CloseableIterable<? extends OptionalWithCause<?>> ret;
if (Representation.class.equals(objectClass)) {
ret = listRepresentations();
} else if (File.class.equals(objectClass)) {
ret = listFiles();
} else if (TransferredResource.class.equals(objectClass)) {
ret = LiteRODAObjectFactory.transformFromLite(this,
RodaCoreFactory.getTransferredResourcesScanner().listTransferredResources());
} else if (RODAMember.class.equals(objectClass)) {
ret = listMembers();
} else if (LogEntry.class.equals(objectClass)) {
ret = listLogEntries();
} else if (DIPFile.class.equals(objectClass)) {
ret = listDIPFiles();
} else if (PreservationMetadata.class.equals(objectClass)) {
ret = listPreservationMetadata();
} else if (DescriptiveMetadata.class.equals(objectClass)) {
ret = listDescriptiveMetadata();
} else if (Report.class.equals(objectClass)) {
ret = ResourceParseUtils.convert(getStorage(), listReportResources(), objectClass);
} else {
StoragePath containerPath = ModelUtils.getContainerPath(objectClass);
final CloseableIterable<Resource> resourcesIterable = storage.listResourcesUnderContainer(containerPath, false);
ret = ResourceParseUtils.convert(getStorage(), resourcesIterable, objectClass);
}
return (CloseableIterable<OptionalWithCause<T>>) ret;
}
public <T extends IsRODAObject> CloseableIterable<OptionalWithCause<LiteRODAObject>> listLite(Class<T> objectClass)
throws RODAException {
CloseableIterable<OptionalWithCause<LiteRODAObject>> ret;
if (Representation.class.equals(objectClass)) {
ret = ResourceParseUtils.convertLite(getStorage(), ResourceListUtils.listRepresentationResources(storage),
objectClass);
} else if (File.class.equals(objectClass)) {
ret = ResourceParseUtils.convertLite(getStorage(), ResourceListUtils.listFileResources(storage), objectClass);
} else if (TransferredResource.class.equals(objectClass)) {
ret = RodaCoreFactory.getTransferredResourcesScanner().listTransferredResources();
} else if (RODAMember.class.equals(objectClass)) {
ret = LiteRODAObjectFactory.transformIntoLite(listMembers());
} else if (LogEntry.class.equals(objectClass)) {
ret = LiteRODAObjectFactory.transformIntoLite(listLogEntries());
} else if (DIPFile.class.equals(objectClass)) {
ret = ResourceParseUtils.convertLite(getStorage(), ResourceListUtils.listDIPFileResources(storage), objectClass);
} else if (PreservationMetadata.class.equals(objectClass)) {
ret = ResourceParseUtils.convertLite(getStorage(), ResourceListUtils.listPreservationMetadataResources(storage),
objectClass);
} else if (DescriptiveMetadata.class.equals(objectClass)) {
ret = ResourceParseUtils.convertLite(getStorage(), ResourceListUtils.listDescriptiveMetadataResources(storage),
objectClass);
} else if (Report.class.equals(objectClass)) {
ret = ResourceParseUtils.convertLite(getStorage(), listReportResources(), objectClass);
} else {
StoragePath containerPath = ModelUtils.getContainerPath(objectClass);
final CloseableIterable<Resource> resourcesIterable = storage.listResourcesUnderContainer(containerPath, false);
ret = ResourceParseUtils.convertLite(getStorage(), resourcesIterable, objectClass);
}
return ret;
}
private CloseableIterable<Resource> listReportResources() throws RODAException {
CloseableIterable<Resource> resources = storage
.listResourcesUnderContainer(ModelUtils.getContainerPath(Report.class), true);
return CloseableIterables.filter(resources, resource -> !(resource instanceof Directory));
}
private CloseableIterable<OptionalWithCause<RODAMember>> listMembers() {
List<OptionalWithCause<RODAMember>> members = new ArrayList<>();
try {
List<OptionalWithCause<RODAMember>> users = listUsers().stream()
.map(user -> OptionalWithCause.of((RODAMember) user)).collect(Collectors.toList());
members.addAll(users);
List<OptionalWithCause<RODAMember>> groups = listGroups().stream()
.map(group -> OptionalWithCause.of((RODAMember) group)).collect(Collectors.toList());
members.addAll(groups);
} catch (GenericException e) {
LOGGER.error("Error getting user and/or groups list");
}
return CloseableIterables.fromList(members);
}
public CloseableIterable<OptionalWithCause<LogEntry>> listLogEntries() {
return listLogEntries(0);
}
public CloseableIterable<OptionalWithCause<LogEntry>> listLogEntries(int daysToIndex) {
boolean recursive = false;
CloseableIterable<OptionalWithCause<LogEntry>> inStorage = null;
CloseableIterable<OptionalWithCause<LogEntry>> notStorage = null;
try {
final CloseableIterable<Resource> actionLogs = getStorage()
.listResourcesUnderContainer(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_ACTIONLOG), recursive);
if (daysToIndex > 0) {
inStorage = new LogEntryStorageIterable(
CloseableIterables.filter(actionLogs, r -> isToIndex(r.getStoragePath().getName(), daysToIndex)));
} else {
inStorage = new LogEntryStorageIterable(actionLogs);
}
} catch (NotFoundException | GenericException | AuthorizationDeniedException | RequestNotValidException e) {
LOGGER.error("Error getting action log from storage", e);
}
try {
if (daysToIndex > 0) {
notStorage = new LogEntryFileSystemIterable(RodaCoreFactory.getLogPath(),
p -> isToIndex(p.getFileName().toString(), daysToIndex));
} else {
notStorage = new LogEntryFileSystemIterable(RodaCoreFactory.getLogPath());
}
} catch (IOException e) {
LOGGER.error("Error getting action log from storage", e);
}
return CloseableIterables.concat(inStorage, notStorage);
}
private boolean isToIndex(String fileName, int daysToIndex) {
boolean isToIndex = false;
String fileNameWithoutExtension = fileName.replaceFirst(".log$", "");
try {
DateTime dt = LOG_NAME_DATE_FORMAT.parseDateTime(fileNameWithoutExtension);
if (dt.plusDays(daysToIndex + 1).isAfterNow()) {
isToIndex = true;
}
} catch (IllegalArgumentException | UnsupportedOperationException e) {
LOGGER.error("Could not parse log file name", e);
}
return isToIndex;
}
public boolean hasObjects(Class<? extends IsRODAObject> objectClass) {
try {
if (LogEntry.class.equals(objectClass) || RODAMember.class.equals(objectClass)
|| TransferredResource.class.equals(objectClass) || IndexedPreservationAgent.class.equals(objectClass)) {
return true;
} else {
StoragePath storagePath = ModelUtils.getContainerPath(objectClass);
try {
return RodaCoreFactory.getStorageService().countResourcesUnderContainer(storagePath, false).intValue() > 0;
} catch (NotFoundException e) {
// TODO 20160913 hsilva: we want to handle the non-existence of a
// container
}
}
return false;
} catch (RODAException e) {
return false;
}
}
public boolean checkObjectPermission(String username, String permissionType, String objectClass, String id)
throws GenericException, NotFoundException, AuthorizationDeniedException, RequestNotValidException {
if (UserUtility.isAdministrator(username)) {
return true;
}
boolean hasPermission = false;
Set<String> groups = this.retrieveUser(username).getGroups();
try {
if (DIP.class.getName().equals(objectClass)) {
DIP dip = this.retrieveDIP(id);
Permissions permissions = dip.getPermissions();
Set<PermissionType> userPermissions = permissions.getUserPermissions(username);
for (String group : groups) {
userPermissions.addAll(permissions.getGroupPermissions(group));
}
PermissionType type = PermissionType.valueOf(permissionType.toUpperCase());
hasPermission = userPermissions.contains(type);
} else if (AIP.class.getName().equals(objectClass)) {
AIP aip = this.retrieveAIP(id);
Permissions permissions = aip.getPermissions();
Set<PermissionType> userPermissions = permissions.getUserPermissions(username);
for (String group : groups) {
userPermissions.addAll(permissions.getGroupPermissions(group));
}
PermissionType type = PermissionType.valueOf(permissionType.toUpperCase());
hasPermission = userPermissions.contains(type);
} else {
throw new RequestNotValidException(objectClass + " permission verification is not supported");
}
} catch (IllegalArgumentException e) {
throw new RequestNotValidException(e);
}
return hasPermission;
}
}