/**
* 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.storage.fedora;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.fcrepo.client.BadRequestException;
import org.fcrepo.client.FedoraContent;
import org.fcrepo.client.FedoraDatastream;
import org.fcrepo.client.FedoraException;
import org.fcrepo.client.FedoraObject;
import org.fcrepo.client.FedoraRepository;
import org.fcrepo.client.FedoraResource;
import org.fcrepo.client.ForbiddenException;
import org.fcrepo.client.impl.FedoraRepositoryImpl;
import org.roda.core.common.iterables.CloseableIterable;
import org.roda.core.data.exceptions.AlreadyExistsException;
import org.roda.core.data.exceptions.AuthorizationDeniedException;
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.exceptions.NotFoundException;
import org.roda.core.data.exceptions.RequestNotValidException;
import org.roda.core.data.utils.JsonUtils;
import org.roda.core.data.v2.ip.StoragePath;
import org.roda.core.storage.Binary;
import org.roda.core.storage.BinaryVersion;
import org.roda.core.storage.Container;
import org.roda.core.storage.ContentPayload;
import org.roda.core.storage.DefaultContainer;
import org.roda.core.storage.DefaultDirectory;
import org.roda.core.storage.DefaultStoragePath;
import org.roda.core.storage.DirectResourceAccess;
import org.roda.core.storage.Directory;
import org.roda.core.storage.Entity;
import org.roda.core.storage.Resource;
import org.roda.core.storage.StorageService;
import org.roda.core.storage.StorageServiceUtils;
import org.roda.core.storage.fedora.utils.FedoraConversionUtils;
import org.roda.core.storage.fedora.utils.FedoraUtils;
import org.roda.core.storage.fs.FileStorageService;
import org.roda.core.storage.utils.StorageRecursiveListingUtils;
import org.roda.core.util.Base64;
import org.roda.core.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class that persists binary files and their containers in Fedora.
*
* @author Sébastien Leroux <sleroux@keep.pt>
* @author Hélder Silva <hsilva@keep.pt>
*/
public class FedoraStorageService implements StorageService {
public static final String RODA_PREFIX = "roda";
public static final String RODA_NAMESPACE = "http://www.roda-project.org/roda#";
public static final String FEDORA_CONTAINER = "fedora:Container";
public static final String FEDORA_BINARY = "fedora:Binary";
public static final String FEDORA_RESOURCE_METADATA = "fcr:metadata";
private static final Logger LOGGER = LoggerFactory.getLogger(FedoraStorageService.class);
private String fedoraURL;
private String fedoraUsername;
private String fedoraPassword;
private FedoraRepository fedoraRepository;
/**
* Public constructor (for using without user credentials)
*
* @param fedoraURL
* Fedora base URL
*/
public FedoraStorageService(String fedoraURL) {
this.fedoraURL = fedoraURL;
this.fedoraUsername = null;
this.fedoraPassword = null;
this.fedoraRepository = new FedoraRepositoryImpl(fedoraURL);
}
/**
* Public constructor
*
* @param fedoraURL
* Fedora base URL
* @param username
* Fedora username
* @param password
* Fedora password
*/
public FedoraStorageService(String fedoraURL, String username, String password) {
this.fedoraURL = fedoraURL;
this.fedoraUsername = username;
this.fedoraPassword = password;
this.fedoraRepository = new FedoraRepositoryImpl(fedoraURL, username, password);
}
public String getFedoraURL() {
return fedoraURL;
}
public String getFedoraUsername() {
return fedoraUsername;
}
public String getFedoraPassword() {
return fedoraPassword;
}
public FedoraRepository getFedoraRepository() {
return fedoraRepository;
}
@Override
public CloseableIterable<Container> listContainers()
throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException {
return new IterableContainer(fedoraRepository);
}
@Override
public Container createContainer(StoragePath storagePath)
throws AuthorizationDeniedException, AlreadyExistsException, RequestNotValidException, GenericException {
try {
fedoraRepository.createObject(FedoraUtils.storagePathToFedoraPath(storagePath));
return new DefaultContainer(storagePath);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Could not create container", e);
} catch (org.fcrepo.client.AlreadyExistsException e) {
throw new AlreadyExistsException("Could not create container", e);
} catch (FedoraException e) {
throw new GenericException("Could not create container", e);
}
}
@Override
public Container getContainer(StoragePath storagePath)
throws RequestNotValidException, AuthorizationDeniedException, NotFoundException, GenericException {
if (!storagePath.isFromAContainer()) {
throw new RequestNotValidException("The storage path provided isn't from a container: " + storagePath);
}
try {
return FedoraConversionUtils
.fedoraObjectToContainer(fedoraRepository.getObject(FedoraUtils.storagePathToFedoraPath(storagePath)));
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Could not get container", e);
} catch (BadRequestException e) {
throw new RequestNotValidException("Could not get container", e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Could not get container", e);
} catch (FedoraException e) {
throw new GenericException("Could not get container", e);
}
}
@Override
public void deleteContainer(StoragePath storagePath)
throws AuthorizationDeniedException, NotFoundException, GenericException {
try {
fedoraRepository.getObject(FedoraUtils.storagePathToFedoraPath(storagePath)).forceDelete();
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Could not delete container", e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Could not delete container", e);
} catch (FedoraException e) {
throw new GenericException("Could not get container", e);
}
}
@Override
public CloseableIterable<Resource> listResourcesUnderContainer(StoragePath storagePath, boolean recursive)
throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException {
if (recursive == true) {
return StorageRecursiveListingUtils.listAllUnderContainer(this, storagePath);
} else {
return new IterableResource(fedoraRepository, storagePath);
}
}
@Override
public Long countResourcesUnderContainer(StoragePath storagePath, boolean recursive)
throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException {
if (recursive == true) {
return StorageRecursiveListingUtils.countAllUnderContainer(this, storagePath);
} else {
try {
Collection<FedoraResource> children = fedoraRepository
.getObject(FedoraUtils.storagePathToFedoraPath(storagePath)).getChildren(null);
return Long.valueOf(children.size());
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Could not count resource under directory", e);
} catch (BadRequestException e) {
throw new RequestNotValidException("Could not count resource under directory", e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Could not count resource under directory", e);
} catch (FedoraException e) {
throw new GenericException("Could not count resource under directory", e);
}
}
}
@Override
public Directory createDirectory(StoragePath storagePath)
throws AuthorizationDeniedException, AlreadyExistsException, GenericException {
try {
fedoraRepository.createObject(FedoraUtils.storagePathToFedoraPath(storagePath));
return new DefaultDirectory(storagePath);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Could not create directory", e);
} catch (org.fcrepo.client.AlreadyExistsException e) {
throw new AlreadyExistsException("Could not create directory", e);
} catch (FedoraException e) {
throw new GenericException("Could not create directory", e);
}
}
@Override
public Directory createRandomDirectory(StoragePath parentStoragePath)
throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException {
FedoraObject directory;
try {
StoragePath storagePath = DefaultStoragePath.parse(parentStoragePath, IdUtils.createUUID());
do {
try {
// XXX may want to change create object to native Fedora method that
// creates a random object
directory = fedoraRepository.createObject(FedoraUtils.storagePathToFedoraPath(storagePath));
} catch (org.fcrepo.client.AlreadyExistsException e) {
directory = null;
LOGGER.warn("Got a colision when creating random directory", e);
}
} while (directory == null);
return new DefaultDirectory(storagePath);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Error creating random directory under " + parentStoragePath, e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Error creating random directory under " + parentStoragePath, e);
} catch (FedoraException e) {
throw new GenericException("Error creating random directory under " + parentStoragePath, e);
}
}
@Override
public Directory getDirectory(StoragePath storagePath)
throws RequestNotValidException, GenericException, AuthorizationDeniedException, NotFoundException {
if (storagePath.isFromAContainer()) {
throw new RequestNotValidException("Invalid storage path for a directory: " + storagePath);
}
try {
FedoraObject object = fedoraRepository.getObject(FedoraUtils.storagePathToFedoraPath(storagePath));
return FedoraConversionUtils.fedoraObjectToDirectory(fedoraRepository.getRepositoryUrl(), object);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Error getting directory " + storagePath, e);
} catch (BadRequestException e) {
throw new RequestNotValidException("Error getting directory " + storagePath, e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Error getting directory " + storagePath, e);
} catch (FedoraException e) {
// Unfortunately Fedora does not give a better error when requesting a
// file as a directory
throw new RequestNotValidException("Error getting directory " + storagePath, e);
}
}
@Override
public CloseableIterable<Resource> listResourcesUnderDirectory(StoragePath storagePath, boolean recursive)
throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException {
if (recursive) {
return StorageRecursiveListingUtils.listAllUnderDirectory(this, storagePath);
} else {
return new IterableResource(fedoraRepository, storagePath);
}
}
@Override
public Long countResourcesUnderDirectory(StoragePath storagePath, boolean recursive)
throws NotFoundException, GenericException, AuthorizationDeniedException, RequestNotValidException {
if (recursive) {
return StorageRecursiveListingUtils.countAllUnderDirectory(this, storagePath);
} else {
try {
Collection<FedoraResource> children = fedoraRepository
.getObject(FedoraUtils.storagePathToFedoraPath(storagePath)).getChildren(null);
return Long.valueOf(children.size());
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Could not count resource under directory", e);
} catch (BadRequestException e) {
throw new RequestNotValidException("Could not count resource under directory", e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Could not count resource under directory", e);
} catch (FedoraException e) {
throw new GenericException("Could not count resource under directory", e);
}
}
}
@Override
public Binary createBinary(StoragePath storagePath, ContentPayload payload, boolean asReference)
throws GenericException, RequestNotValidException, AuthorizationDeniedException, AlreadyExistsException,
NotFoundException {
if (asReference) {
// TODO method to create binary as reference.
throw new GenericException("Creating binary as reference not yet supported");
} else {
try {
String path = FedoraUtils.storagePathToFedoraPath(storagePath);
LOGGER.debug("PATH {}", path);
FedoraContent fedoraContentPayload = FedoraConversionUtils.contentPayloadToFedoraContent(payload);
FedoraDatastream binary = fedoraRepository.createDatastream(path, fedoraContentPayload);
IOUtils.closeQuietly(fedoraContentPayload.getContent());
return FedoraConversionUtils.fedoraDatastreamToBinary(binary);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Error creating binary", e);
} catch (org.fcrepo.client.AlreadyExistsException e) {
throw new AlreadyExistsException("Error creating binary", e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Error creating binary", e);
} catch (FedoraException e) {
LOGGER.error(e.getMessage(), e);
throw new GenericException("Error creating binary", e);
}
}
}
@Override
public Binary createRandomBinary(StoragePath parentStoragePath, ContentPayload payload, boolean asReference)
throws GenericException, RequestNotValidException, AuthorizationDeniedException, NotFoundException {
if (asReference) {
// TODO method to create binary as reference.
throw new GenericException("Creating binary as reference not yet supported");
} else {
try {
FedoraDatastream binary;
StoragePath storagePath = DefaultStoragePath.parse(parentStoragePath, IdUtils.createUUID());
do {
try {
// XXX may want to change create object to native Fedora method that
// creates a random datastream
FedoraContent fedoraContentPayload = FedoraConversionUtils.contentPayloadToFedoraContent(payload);
binary = fedoraRepository.createDatastream(FedoraUtils.storagePathToFedoraPath(storagePath),
fedoraContentPayload);
IOUtils.closeQuietly(fedoraContentPayload.getContent());
} catch (org.fcrepo.client.AlreadyExistsException e) {
binary = null;
LOGGER.warn("Got a colision when creating random bianry", e);
}
} while (binary == null);
return FedoraConversionUtils.fedoraDatastreamToBinary(binary);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException(e.getMessage(), e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException(e.getMessage(), e);
} catch (FedoraException e) {
throw new GenericException(e.getMessage(), e);
}
}
}
@Override
public Binary updateBinaryContent(StoragePath storagePath, ContentPayload payload, boolean asReference,
boolean createIfNotExists)
throws GenericException, AuthorizationDeniedException, RequestNotValidException, NotFoundException {
if (asReference) {
// TODO method to update binary as reference.
throw new GenericException("Updating binary as reference not yet supported");
} else {
try {
FedoraDatastream datastream = fedoraRepository.getDatastream(FedoraUtils.storagePathToFedoraPath(storagePath));
FedoraContent fedoraContentPayload = FedoraConversionUtils.contentPayloadToFedoraContent(payload);
datastream.updateContent(fedoraContentPayload);
IOUtils.closeQuietly(fedoraContentPayload.getContent());
return FedoraConversionUtils.fedoraDatastreamToBinary(datastream);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Error updating binary content", e);
} catch (org.fcrepo.client.NotFoundException e) {
if (createIfNotExists) {
try {
return createBinary(storagePath, payload, asReference);
} catch (AlreadyExistsException e1) {
throw new GenericException("Error updating binary content", e1);
}
} else {
throw new NotFoundException("Error updating binary content", e);
}
} catch (FedoraException e) {
throw new GenericException("Error updating binary content", e);
}
}
}
@Override
public Binary getBinary(StoragePath storagePath)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
try {
FedoraDatastream ds = fedoraRepository.getDatastream(FedoraUtils.storagePathToFedoraPath(storagePath));
if (!isDatastream(ds)) {
throw new RequestNotValidException("The resource obtained as being a datastream isn't really a datastream");
}
return FedoraConversionUtils.fedoraDatastreamToBinary(ds);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException(e.getMessage(), e);
} catch (BadRequestException e) {
throw new RequestNotValidException(e.getMessage(), e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException(e.getMessage(), e);
} catch (FedoraException e) {
throw new GenericException(e.getMessage(), e);
}
}
private boolean isDatastream(FedoraDatastream ds) throws FedoraException {
Collection<String> mixins = ds.getMixins();
return !mixins.contains(FEDORA_CONTAINER);
}
@Override
public void deleteResource(StoragePath storagePath)
throws NotFoundException, AuthorizationDeniedException, GenericException {
String fedoraPath = FedoraUtils.storagePathToFedoraPath(storagePath);
try {
if (fedoraRepository.exists(fedoraPath)) {
boolean deleted = false;
try {
FedoraDatastream fds = fedoraRepository.getDatastream(fedoraPath);
if (fds != null) {
fds.forceDelete();
deleted = true;
}
} catch (FedoraException e) {
// FIXME add proper error handling
}
if (!deleted) {
try {
FedoraObject object = fedoraRepository.getObject(fedoraPath);
if (object != null) {
object.forceDelete();
}
} catch (FedoraException e) {
// FIXME add proper error handling
}
}
} else {
throw new NotFoundException("The resource identified by the path \"" + storagePath + "\" was not found");
}
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Error deleting resource: " + storagePath, e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException(e.getMessage(), e);
} catch (FedoraException e) {
throw new GenericException("Error deleting resource: " + storagePath, e);
}
}
@Override
public void copy(StorageService fromService, StoragePath fromStoragePath, StoragePath toStoragePath)
throws GenericException, RequestNotValidException, AuthorizationDeniedException, NotFoundException,
AlreadyExistsException {
Class<? extends Entity> rootEntity = fromService.getEntity(fromStoragePath);
if (fromService instanceof FedoraStorageService
&& ((FedoraStorageService) fromService).getFedoraURL().equalsIgnoreCase(getFedoraURL())) {
copyInsideFedora(fromStoragePath, toStoragePath, rootEntity);
} else {
StorageServiceUtils.copyBetweenStorageServices(fromService, fromStoragePath, this, toStoragePath, rootEntity);
}
}
private void copyInsideFedora(StoragePath fromStoragePath, StoragePath toStoragePath,
Class<? extends Entity> rootEntity)
throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException {
try {
if (rootEntity.equals(Container.class) || rootEntity.equals(Directory.class)) {
FedoraObject object = fedoraRepository.getObject(FedoraUtils.storagePathToFedoraPath(fromStoragePath));
object.copy(FedoraUtils.storagePathToFedoraPath(toStoragePath));
} else {
FedoraDatastream datastream = fedoraRepository
.getDatastream(FedoraUtils.storagePathToFedoraPath(fromStoragePath));
datastream.copy(FedoraUtils.storagePathToFedoraPath(toStoragePath));
}
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Error while copying from one storage path to another", e);
} catch (BadRequestException e) {
throw new RequestNotValidException("Error while copying from one storage path to another", e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Error while copying from one storage path to another", e);
} catch (FedoraException e) {
throw new GenericException("Error while copying from one storage path to another", e);
}
}
@Override
public void move(StorageService fromService, StoragePath fromStoragePath, StoragePath toStoragePath)
throws GenericException, AuthorizationDeniedException, RequestNotValidException, NotFoundException,
AlreadyExistsException {
Class<? extends Entity> rootEntity = fromService.getEntity(fromStoragePath);
if (fromService instanceof FedoraStorageService
&& ((FedoraStorageService) fromService).getFedoraURL().equalsIgnoreCase(getFedoraURL())) {
moveInsideFedora(fromStoragePath, toStoragePath, rootEntity);
} else {
StorageServiceUtils.moveBetweenStorageServices(fromService, fromStoragePath, this, toStoragePath, rootEntity);
}
}
private void moveInsideFedora(StoragePath fromStoragePath, StoragePath toStoragePath,
Class<? extends Entity> rootEntity)
throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException {
try {
if (rootEntity.equals(Container.class) || rootEntity.equals(Directory.class)) {
FedoraObject object = fedoraRepository.getObject(FedoraUtils.storagePathToFedoraPath(fromStoragePath));
object.forceMove(FedoraUtils.storagePathToFedoraPath(toStoragePath));
} else {
FedoraDatastream datastream = fedoraRepository
.getDatastream(FedoraUtils.storagePathToFedoraPath(fromStoragePath));
datastream.forceMove(FedoraUtils.storagePathToFedoraPath(toStoragePath));
}
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException("Error while moving from one storage path to another", e);
} catch (BadRequestException e) {
throw new RequestNotValidException("Error while moving from one storage path to another", e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException("Error while moving from one storage path to another", e);
} catch (FedoraException e) {
throw new GenericException("Error while moving from one storage path to another", e);
}
}
@Override
public Class<? extends Entity> getEntity(StoragePath storagePath)
throws GenericException, RequestNotValidException, AuthorizationDeniedException, NotFoundException {
if (storagePath.isFromAContainer()) {
if (getContainer(storagePath) != null) {
return Container.class;
} else {
throw new GenericException("There is no Container in the storage represented by \"" + storagePath + "\"");
}
} else {
// it's a directory or binary. but first let's see if that entity
// exists in the storage
try {
FedoraObject object = fedoraRepository
.getObject(FedoraUtils.storagePathToFedoraPath(storagePath) + "/" + FEDORA_RESOURCE_METADATA);
if (object.getMixins().contains(FEDORA_CONTAINER)) {
return Directory.class;
} else {
// it exists, it's not a directory, so it can only be a
// binary
return Binary.class;
}
} catch (FedoraException e) {
throw new GenericException(
"There is no Directory or Binary in the storage represented by \"" + storagePath + "\"", e);
}
}
}
@Override
public DirectResourceAccess getDirectAccess(final StoragePath storagePath) {
return new DirectResourceAccess() {
Path temp = null;
@Override
public Path getPath()
throws GenericException, RequestNotValidException, AuthorizationDeniedException, NotFoundException {
Class<? extends Entity> entity = getEntity(storagePath);
Path path;
try {
temp = Files.createTempDirectory("temp", getTempDirFilePermissions());
if (entity.equals(Container.class) || entity.equals(Directory.class)) {
StorageService tempStorage = new FileStorageService(temp);
tempStorage.copy(FedoraStorageService.this, storagePath, storagePath);
path = temp;
} else {
path = temp.resolve(entity.getName());
Binary binary = getBinary(storagePath);
ContentPayload payload = binary.getContent();
InputStream inputStream = payload.createInputStream();
Files.copy(inputStream, path);
IOUtils.closeQuietly(inputStream);
}
} catch (IOException | AlreadyExistsException e) {
throw new GenericException(e);
}
return path;
}
@Override
public void close() throws IOException {
if (temp != null) {
Files.delete(temp);
temp = null;
}
}
};
}
private static FileAttribute<Set<PosixFilePermission>> getTempDirFilePermissions() {
Set<PosixFilePermission> perms = new HashSet<>();
// add owners permission
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_EXECUTE);
// add group permissions
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_WRITE);
perms.add(PosixFilePermission.GROUP_EXECUTE);
// add others permissions
perms.add(PosixFilePermission.OTHERS_READ);
perms.add(PosixFilePermission.OTHERS_EXECUTE);
return PosixFilePermissions.asFileAttribute(perms);
}
class ListBinaryVersionsIterable implements CloseableIterable<BinaryVersion> {
private final String fedoraPath;
private final Iterator<String> versionsIterator;
public ListBinaryVersionsIterable(String fedoraPath, List<String> versions) {
this.fedoraPath = fedoraPath;
this.versionsIterator = versions.iterator();
}
@Override
public Iterator<BinaryVersion> iterator() {
return new Iterator<BinaryVersion>() {
@Override
public boolean hasNext() {
return versionsIterator.hasNext();
}
@Override
public BinaryVersion next() {
String next = versionsIterator.next();
String id = next.substring(0, 36);
String propertiesString = next.substring(36);
Map<String, String> properties = decodeProperties(propertiesString);
BinaryVersion ret;
try {
FedoraDatastream version = fedoraRepository.getDatastreamVersion(fedoraPath, next);
ret = FedoraConversionUtils.convertDataStreamToBinaryVersion(version, id, properties);
} catch (FedoraException | GenericException | RequestNotValidException e) {
ret = null;
}
return ret;
}
};
}
@Override
public void close() throws IOException {
// nothing to do
}
}
@Override
public CloseableIterable<BinaryVersion> listBinaryVersions(StoragePath storagePath)
throws GenericException, RequestNotValidException, NotFoundException, AuthorizationDeniedException {
try {
String fedoraPath = FedoraUtils.storagePathToFedoraPath(storagePath);
FedoraDatastream ds = fedoraRepository.getDatastream(fedoraPath);
if (!isDatastream(ds)) {
throw new RequestNotValidException("The resource obtained as being a datastream isn't really a datastream");
}
List<String> versions = ds.getVersionsName();
return new ListBinaryVersionsIterable(fedoraPath, versions);
} catch (ForbiddenException e) {
throw new AuthorizationDeniedException(e.getMessage(), e);
} catch (BadRequestException e) {
throw new RequestNotValidException(e.getMessage(), e);
} catch (org.fcrepo.client.NotFoundException e) {
throw new NotFoundException(e.getMessage(), e);
} catch (FedoraException e) {
throw new GenericException(e.getMessage(), e);
}
}
@Override
public BinaryVersion getBinaryVersion(StoragePath storagePath, String version)
throws RequestNotValidException, NotFoundException, GenericException {
try {
FedoraDatastream binary = fedoraRepository.getDatastream(FedoraUtils.storagePathToFedoraPath(storagePath));
String fullVersionID = getFullVersionID(binary, version);
LOGGER.debug("FULL {}", fullVersionID);
FedoraDatastream ds = fedoraRepository.getDatastreamVersion(FedoraUtils.storagePathToFedoraPath(storagePath),
fullVersionID);
if (!isDatastream(ds)) {
throw new RequestNotValidException("The resource obtained as being a datastream isn't really a datastream");
}
Map<String, String> properties;
if (fullVersionID != null) {
String propertiesString = fullVersionID.replace(version, "");
properties = decodeProperties(propertiesString);
} else {
properties = new HashMap<>();
}
return FedoraConversionUtils.convertDataStreamToBinaryVersion(ds, version, properties);
} catch (ForbiddenException | org.fcrepo.client.NotFoundException e) {
throw new NotFoundException(e.getMessage(), e);
} catch (BadRequestException e) {
throw new RequestNotValidException(e.getMessage(), e);
} catch (FedoraException | GenericException | RequestNotValidException e) {
throw new GenericException(e.getMessage(), e);
}
}
private static String encodeProperties(Map<String, String> properties) {
String propertiesAsJSON = JsonUtils.getJsonFromObject(properties);
String propertiesAsJsonAsBase64 = new String(Base64.encode(propertiesAsJSON.getBytes()));
return propertiesAsJsonAsBase64.replace('=', '_');
}
private static Map<String, String> decodeProperties(String encodedProperties) {
String propertiesAsJsonAsBase64 = encodedProperties.replace('_', '=');
String propertiesAsJSON = new String(Base64.decode(propertiesAsJsonAsBase64.toCharArray()));
return JsonUtils.getMapFromJson(propertiesAsJSON);
}
@Override
public BinaryVersion createBinaryVersion(StoragePath storagePath, Map<String, String> properties)
throws RequestNotValidException, NotFoundException, GenericException {
try {
String id = IdUtils.createUUID();
FedoraDatastream binary = fedoraRepository.getDatastream(FedoraUtils.storagePathToFedoraPath(storagePath));
String versionID = id + encodeProperties(properties);
binary.createVersionSnapshot(versionID);
return FedoraConversionUtils.convertDataStreamToBinaryVersion(binary, id, properties);
} catch (FedoraException e) {
throw new GenericException(e.getMessage(), e);
}
}
@Override
public void revertBinaryVersion(StoragePath storagePath, String version)
throws NotFoundException, RequestNotValidException, GenericException {
try {
FedoraDatastream binary = fedoraRepository.getDatastream(FedoraUtils.storagePathToFedoraPath(storagePath));
String fullVersionID = getFullVersionID(binary, version);
binary.revertToVersion(fullVersionID);
} catch (FedoraException e) {
throw new GenericException(e.getMessage(), e);
}
}
@Override
public void deleteBinaryVersion(StoragePath storagePath, String version)
throws NotFoundException, GenericException, RequestNotValidException {
try {
FedoraDatastream binary = fedoraRepository.getDatastream(FedoraUtils.storagePathToFedoraPath(storagePath));
String fullVersionID = getFullVersionID(binary, version);
binary.deleteVersion(fullVersionID);
} catch (FedoraException e) {
throw new GenericException(e.getMessage(), e);
}
}
private String getFullVersionID(FedoraDatastream binary, String shortVersion) {
LOGGER.debug("Getting full from {}", shortVersion);
String fullID = null;
try {
List<String> versions = binary.getVersionsName();
if (versions != null) {
for (String version : versions) {
LOGGER.debug("V: {}", version);
if (version.startsWith(shortVersion)) {
fullID = version;
break;
}
}
}
} catch (FedoraException e) {
LOGGER.error("Error getting full version Id", e);
}
return fullID;
}
@Override
public boolean hasDirectory(StoragePath storagePath) {
try {
this.getDirectory(storagePath);
return true;
} catch (NotFoundException | RequestNotValidException | GenericException | AuthorizationDeniedException e) {
return false;
}
}
@Override
public boolean hasBinary(StoragePath storagePath) {
try {
this.getBinary(storagePath);
return true;
} catch (NotFoundException | RequestNotValidException | GenericException | AuthorizationDeniedException e) {
return false;
}
}
}