RepositoryFile.java
/*
* Copyright 2018 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.filerepository.model;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.UUID;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.genesys.blocks.model.AuditedVersionedModel;
import org.genesys.blocks.model.Copyable;
import org.genesys.blocks.model.SelfCleaning;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.filerepository.metadata.BaseMetadata;
import org.genesys.filerepository.service.BytesStorageService;
import org.hibernate.annotations.Type;
import org.springframework.data.domain.Sort;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
/**
* The RepositoryFile.
*/
@Cacheable
@Entity
@Table(name = "repository_file",
// indexes
indexes = { @Index(unique = false, columnList = "folder_id", name = "IX_repoFile_path") }
// unique
, uniqueConstraints = { @UniqueConstraint(columnNames = { "folder_id", "originalFilename" }) })
@Inheritance(strategy = InheritanceType.JOINED)
@Getter
@Setter
public class RepositoryFile extends AuditedVersionedModel implements AclAwareModel, BaseMetadata, Copyable<RepositoryFile>, SelfCleaning {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 568963297049937168L;
/** Default sort order */
public static final Sort DEFAULT_SORT = Sort.by("originalFilename");
/** The uuid. */
@Column(unique = true, nullable = false, updatable = false)
@Type(type = "uuid-binary")
private UUID uuid;
/**
* The repository folder.
*
* @since 1.1
*/
@ManyToOne(cascade = {}, fetch = FetchType.LAZY, optional = false)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "path")
@JsonIdentityReference(alwaysAsId = true)
@JsonProperty(access = Access.READ_ONLY)
private RepositoryFolder folder;
/** The original filename as provided by the end user. */
@Column(nullable = false, length = 250)
private String originalFilename;
/** Extension based on originalFilename. */
@Column(length = 50)
private String extension;
/** The title. */
@Column(length = 200)
private String title;
/** The subject. */
@Column
@Lob
@Type(type = "org.hibernate.type.TextType")
private String subject;
/** The description. */
@Column
@Lob
@Type(type = "org.hibernate.type.TextType")
private String description;
/** The creator. */
@Column(length = 200)
private String creator;
/** The created. */
@Column(length = 200)
private String created;
/** The rights holder. */
@Column(length = 200)
private String rightsHolder;
/** The access rights. */
@Column(length = 200)
private String accessRights;
/** The license. */
@Column(length = 50)
private String license;
/** The content type. */
@Column(length = 200)
private String contentType;
/** The extent. */
@Column(length = 200)
private String extent;
/** The bibliographic citation. */
@Column
@Lob
@Type(type = "org.hibernate.type.TextType")
private String bibliographicCitation;
/** URL where the resource was originally retrieved from. */
@Column(length = 500)
private String originalUrl;
/**
* For resources retrieved from {@link #originalUrl} we maintain the date of
* retrieval.
*/
@Column
private Instant dateRetrieved;
/** Sha1sum hash of the bytes. */
@Column(length = 40, nullable = false)
@JsonProperty(access = Access.READ_ONLY)
private String sha1Sum;
/** Sha1sum hash of the bytes. */
@Column(length = 48, nullable = true)
@JsonProperty(access = Access.READ_ONLY)
private byte[] sha384;
/** MD5 hash of the bytes. */
@Column(length = 32, nullable = false)
@JsonProperty(access = Access.READ_ONLY)
private String md5Sum;
/** Byte length. */
@Column(nullable = false)
private int size;
/** URN identifier generated from the UUID */
@Transient
private String identifier;
/** Name of file in BytesStorageService */
@Transient
private String filename;
/** Name of metadata file in BytesStorageService */
@Transient
private String metadataFilename;
/** Folder in BytesStorageService where bytes are saved */
@Transient
private String storageFolder;
/** Full path (including filename) where bytes are located in BytesStorageService */
@Transient
private String storagePath;
/**
* Before persist and any update
*/
@PrePersist
@PreUpdate
protected void prePersist() {
if (uuid == null) {
uuid = UUID.randomUUID();
}
trimStringsToNull();
}
/**
* Calculate transient fields
*/
@PostLoad
@PostUpdate
@PostPersist
protected void postLoad() {
if (this.uuid != null) {
var uuidStr = uuid.toString();
this.identifier = "urn:uuid:" + uuidStr;
this.filename = uuidStr + StringUtils.defaultIfBlank(extension, "");
this.metadataFilename = uuidStr + (StringUtils.equals(".json", extension) ? extension : "") + ".json";
this.storageFolder = "/" + String.valueOf(uuid).substring(0, 3);
this.storagePath = this.storageFolder + "/" + this.filename;
}
}
/**
* For repository files, the parent object is always a {@link RepositoryFolder}
* (can't be null).
*/
@Override
public AclAwareModel aclParentObject() {
return this.folder;
}
/*
* (non-Javadoc)
* @see org.genesys.filerepository.metadata.BaseMetadata#getIdentifier()
*/
@Override
public String getIdentifier() {
return this.identifier;
}
/**
* Gets the filename as used by {@link BytesStorageService}.
*
* @return the filename
*/
public String getFilename() {
return this.filename;
}
/**
* Gets the filename for metadata as saved in {@link BytesStorageService}.
*
* @return the filename
*/
public String getMetadataFilename() {
return this.metadataFilename;
}
/**
* Get the path of the file used by {@link BytesStorageService}.
*
* @return the storage path
*/
@JsonGetter
public String getStorageFolder() {
return this.storageFolder;
}
/**
* Get the full path to the file as used by {@link BytesStorageService}. This is
* the concatenation of {@link #getStorageFolder()} and {@link #getFilename()}.
*
* @return the storage full path
*/
@JsonGetter
public String getStoragePath() {
return this.storagePath;
}
/**
* Get the {@link Path} version of the full path to the file as used by {@link BytesStorageService}.
* @return the full path of file in storage
*/
public Path storagePath() {
if (uuid == null) {
return null;
}
return Paths.get(getStorageFolder(), getFilename());
}
/**
* Get {@link Path} representation of the {@link #getStorageFolder()}
*
* @return a {@code Path}
*/
public Path storageFolder() {
if (uuid == null) {
return null;
}
return Paths.get(getStorageFolder());
}
/*
* (non-Javadoc)
* @see org.genesys.filerepository.metadata.BaseMetadata#getFormat()
*/
@Override
public String getFormat() {
return getContentType();
}
/*
* (non-Javadoc)
* @see org.genesys.filerepository.metadata.BaseMetadata#getDateSubmitted ()
*/
@Override
public Instant getDateSubmitted() {
return getCreatedDate();
}
/*
* (non-Javadoc)
* @see org.genesys.filerepository.metadata.BaseMetadata#getModified()
*/
@Override
public Instant getModified() {
return getLastModifiedDate();
}
/**
* Sets the original filename.
*
* @param originalFilename the new original filename
*/
public void setOriginalFilename(final String originalFilename) {
if (originalFilename != null) {
final int dotIndex = originalFilename.lastIndexOf('.');
setExtension(dotIndex > 0 ? originalFilename.substring(dotIndex) : null);
}
this.originalFilename = originalFilename;
}
/*
* (non-Javadoc)
* @see org.genesys.blocks.model.Copyable#apply(java.lang.Object)
*/
@Override
public RepositoryFile apply(final RepositoryFile source) {
if (StringUtils.isNotBlank(source.originalFilename)) {
// also updates extension
setOriginalFilename(source.originalFilename);
}
this.active = source.active;
// this.folder = source.folder;
this.accessRights = source.accessRights;
this.bibliographicCitation = source.bibliographicCitation;
this.contentType = source.contentType;
this.created = source.created;
this.creator = source.creator;
this.dateRetrieved = source.dateRetrieved;
this.description = source.description;
this.extent = source.extent;
this.license = source.license;
this.originalUrl = source.originalUrl;
this.rightsHolder = source.rightsHolder;
this.subject = source.subject;
this.title = source.title;
return this;
}
/*
* (non-Javadoc)
* @see org.genesys.blocks.model.Copyable#copy()
*/
@Override
public RepositoryFile copy() {
final RepositoryFile copy = new RepositoryFile();
copy.apply(this);
// copy.id=this.id;
// copy.uuid=this.uuid;
// copy.setVersion(this.getVersion());
copy.sha1Sum = this.sha1Sum;
copy.md5Sum = this.md5Sum;
copy.size = this.size;
copy.extension = this.extension;
return copy;
}
/**
* Copy meta data.
*
* @param <T> the generic type
* @param target the result file
*/
public <T extends RepositoryFile> void copyMetaData(final T target) {
target.setContentType(this.getContentType());
target.setDescription(this.getDescription());
target.setOriginalFilename(this.getOriginalFilename());
target.setAccessRights(this.getAccessRights());
target.setBibliographicCitation(this.getBibliographicCitation());
target.setCreated(this.getCreated());
target.setCreatedDate(this.getCreatedDate());
target.setCreator(this.getCreator());
target.setExtent(this.getExtent());
target.setLastModifiedDate(this.getLastModifiedDate());
target.setLicense(this.getLicense());
target.setFolder(this.getFolder());
target.setRightsHolder(this.getRightsHolder());
target.setSubject(this.getSubject());
target.setTitle(this.getTitle());
}
@Override
public boolean canEqual(Object other) {
return other instanceof RepositoryFile;
}
}