package org.dcache.vehicles;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import javax.annotation.Nonnull;
import javax.security.auth.Subject;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import diskCacheV111.util.AccessLatency;
import diskCacheV111.util.PnfsId;
import diskCacheV111.util.RetentionPolicy;
import diskCacheV111.vehicles.StorageInfo;
import org.dcache.acl.ACL;
import org.dcache.auth.Subjects;
import org.dcache.namespace.FileAttribute;
import org.dcache.namespace.FileType;
import org.dcache.util.Checksum;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toMap;
import static org.dcache.namespace.FileAttribute.*;
/**
* <code>FileAttributes</code> encapsulates attributes about a logical file.
*
* The attributes represented by an instance of this class belong to
* a logical file as seen by a client or user of dCache. That is,
* FileAttributes represent the information about a file stored, or that
* should be stored, in the name space or other central components.
*
* Besides their location, the class does not represent any properties
* of physical replicas on a pool. Eg the size or checksum stored in a
* FileAttributes instance represents the expected file size and expected
* checksum of the file. An broken replica may have a different size or a
* different checksum.
*
* The distinction between the logical and physical instance is relevant when
* considering response types to pool query messages: These should NOT return
* attributes of a replica using FileAttributes, except when those represent
* cached information from the name space or other central components.
*
* Not all attributes may be defined. Attempts to read undefined attributes
* will throw IllegalStateException.
*
* @since 1.9.5
*/
public class FileAttributes implements Serializable {
private static final long serialVersionUID = -3689129805631724432L;
/**
* Set of attributes which have been set.
*/
private final EnumSet<FileAttribute> _definedAttributes =
EnumSet.noneOf(FileAttribute.class);
/**
* NFSv4 Access control list.
*/
private ACL _acl;
/**
* file's size
*/
private long _size;
/**
* file's attribute change time
*/
private long _ctime;
/**
* file's creation time
*/
private long _creationTime;
/**
* file's last access time
*/
private long _atime;
/**
* file's last modification time
*/
private long _mtime;
/**
* file's known checksums
*/
private Set<Checksum> _checksums;
/**
* file's owner's id
*/
private int _owner;
/**
* file's group id
*/
private int _group;
/**
* POSIX.1 file mode
*/
private int _mode;
/**
* @since 3.0
*/
private int _nlink;
/**
* file's access latency ( e.g. ONLINE/NEARLINE )
*/
private AccessLatency _accessLatency;
/**
* file's retention policy ( e.g. CUSTODIAL/REPLICA )
*/
private RetentionPolicy _retentionPolicy;
/**
* type of the file ( e.g. REG, DIR, LINK, SPECIAL )
*/
private FileType _fileType;
/**
* File locations within dCache.
*/
private Collection<String> _locations;
/**
* Key value map of flags associated with the file.
*/
private Map<String, String> _flags;
/**
* The unique PNFS ID of a file.
*/
private PnfsId _pnfsId;
/**
* The storage info of a file.
*/
private StorageInfo _storageInfo;
/**
* The storage class of a file.
*/
private String _storageClass;
/**
* The HSM of a file.
*/
private String _hsm;
/**
* The cache class of a file.
*/
private String _cacheClass;
/** Throws IllegalStateException if attribute is not defined. */
private void guard(FileAttribute attribute)
throws IllegalStateException
{
if (!_definedAttributes.contains(attribute)) {
throw new IllegalStateException("Attribute is not defined: " +
attribute);
}
}
private void define(FileAttribute... attributes)
{
_definedAttributes.addAll(asList(attributes));
}
public void undefine(FileAttribute... attributes)
{
_definedAttributes.removeAll(asList(attributes));
}
public boolean isUndefined(FileAttribute attribute)
{
return !_definedAttributes.contains(attribute);
}
/**
* @return true iff all attributes are not define.
*/
public boolean isUndefined(Set<FileAttribute> attributes)
{
return EnumSet.complementOf(_definedAttributes).containsAll(attributes);
}
public boolean isDefined(FileAttribute attribute)
{
return _definedAttributes.contains(attribute);
}
/**
* @return true iff all attributes are defined.
*/
public boolean isDefined(Set<FileAttribute> attributes)
{
return _definedAttributes.containsAll(attributes);
}
/**
* Get the set of available attributes. The set may have zero or
* more entries.
* @return set of defined attribute.
*/
@Nonnull
public Set<FileAttribute> getDefinedAttributes() {
return _definedAttributes;
}
@Nonnull
public AccessLatency getAccessLatency() {
guard(ACCESS_LATENCY);
return _accessLatency;
}
@Nonnull
public Optional<AccessLatency> getAccessLatencyIfPresent() {
return toOptional(ACCESS_LATENCY, _accessLatency);
}
public long getAccessTime()
{
guard(ACCESS_TIME);
return _atime;
}
@Nonnull
public ACL getAcl()
{
guard(ACL);
return _acl;
}
@Nonnull
public Set<Checksum> getChecksums() {
guard(CHECKSUM);
return _checksums;
}
@Nonnull
public Optional<Set<Checksum>> getChecksumsIfPresent() {
return toOptional(CHECKSUM, _checksums);
}
/**
* Get {@link FileType} corresponding to the file.
* @return file type
*/
@Nonnull
public FileType getFileType() {
guard(TYPE);
return _fileType;
}
/**
* Get group id to which file belongs to.
* @return group id
*/
public int getGroup() {
guard(OWNER_GROUP);
return _group;
}
public Optional<Integer> getGroupIfPresent() {
return toOptional(OWNER_GROUP, _group);
}
public int getMode() {
guard(MODE);
return _mode;
}
public Optional<Integer> getModeIfPresent() {
return toOptional(MODE, _mode);
}
/**
* Get file's attribute change time.
*
* @return time in milliseconds since 1 of January 1970 00:00.00
*/
public long getChangeTime() {
guard(CHANGE_TIME);
return _ctime;
}
/**
* Get file's creation time.
* @return time in milliseconds since 1 of January 1970 00:00.00
*/
public long getCreationTime() {
guard(CREATION_TIME);
return _creationTime;
}
/**
* Get file's last modification time.
* @return time in milliseconds since 1 of January 1970 00:00.00
*/
public long getModificationTime() {
guard(MODIFICATION_TIME);
return _mtime;
}
/**
* Get owner id to whom file belongs to.
* @return owner id
*/
public int getOwner() {
guard(OWNER);
return _owner;
}
public Optional<Integer> getOwnerIfPresent() {
return toOptional(OWNER, _owner);
}
@Nonnull
public RetentionPolicy getRetentionPolicy() {
guard(RETENTION_POLICY);
return _retentionPolicy;
}
@Nonnull
public Optional<RetentionPolicy> getRetentionPolicyIfPresent() {
return toOptional(RETENTION_POLICY, _retentionPolicy);
}
public long getSize() {
guard(SIZE);
return _size;
}
public Optional<Long> getSizeIfPresent() {
return toOptional(SIZE, _size);
}
@Nonnull
public PnfsId getPnfsId()
{
guard(PNFSID);
return _pnfsId;
}
@Nonnull
public StorageInfo getStorageInfo()
{
guard(STORAGEINFO);
return _storageInfo;
}
public void setAccessTime(long atime)
{
define(ACCESS_TIME);
_atime = atime;
}
public void setAccessLatency(AccessLatency accessLatency) {
define(ACCESS_LATENCY);
_accessLatency = accessLatency;
}
public void setAcl(ACL acl)
{
define(ACL);
_acl = acl;
}
public void setChecksums(Set<Checksum> checksums) {
define(CHECKSUM);
_checksums = checksums;
}
public void setFileType(FileType fileType) {
define(TYPE);
_fileType = fileType;
}
public void setGroup(int group) {
define(OWNER_GROUP);
_group = group;
}
public void setMode(int mode) {
define(MODE);
_mode = mode;
}
public void setChangeTime(long ctime) {
define(CHANGE_TIME);
_ctime = ctime;
}
public void setCreationTime(long creationTime) {
define(CREATION_TIME);
_creationTime = creationTime;
}
public void setModificationTime(long mtime) {
define(MODIFICATION_TIME);
_mtime = mtime;
}
public void setOwner(int owner) {
define(OWNER);
_owner = owner;
}
public void setRetentionPolicy(RetentionPolicy retentionPolicy) {
define(RETENTION_POLICY);
_retentionPolicy = retentionPolicy;
}
public void setSize(long size) {
define(SIZE);
_size = size;
}
public void setLocations(Collection<String> pools) {
define(LOCATIONS);
_locations = pools;
}
@Nonnull
public Collection<String> getLocations() {
guard(LOCATIONS);
return _locations;
}
@Nonnull
public Map<String, String> getFlags() {
guard(FLAGS);
return _flags;
}
public void setFlags(Map<String, String> flags) {
define(FLAGS);
_flags = flags;
}
public void setPnfsId(String pnfsId)
{
setPnfsId(new PnfsId(pnfsId));
}
public void setPnfsId(PnfsId pnfsId)
{
define(PNFSID);
_pnfsId = pnfsId;
}
public void setStorageInfo(StorageInfo storageInfo)
{
define(STORAGEINFO);
_storageInfo = storageInfo;
}
public String getStorageClass()
{
guard(STORAGECLASS);
return _storageClass;
}
public void setStorageClass(String storageClass)
{
define(STORAGECLASS);
_storageClass = storageClass;
}
public void setCacheClass(String cacheClass)
{
define(CACHECLASS);
_cacheClass = cacheClass;
}
public String getCacheClass()
{
guard(CACHECLASS);
return _cacheClass;
}
public void setHsm(String hsm)
{
define(HSM);
_hsm = hsm;
}
public String getHsm()
{
guard(HSM);
return _hsm;
}
public void setNlink(int nlink)
{
define(NLINK);
_nlink = nlink;
}
public int getNlink()
{
guard(NLINK);
return _nlink;
}
/**
* Remove the {@link FileType} corresponding to the file. The FileType
* must be specified before this method is called. Subsequent getFileType
* or removeFileType will fail unless setFileType is called subsequently.
*
* @return the removed file type
* @throws IllegalStateException if the FileType is not specified.
*/
@Nonnull
public FileType removeFileType() {
guard(TYPE);
undefine(TYPE);
return _fileType;
}
@Override
public String toString()
{
return MoreObjects.toStringHelper(this)
.add("defined", _definedAttributes)
.add("acl", _acl)
.add("size", _size)
.add("ctime", _ctime)
.add("creationTime", _creationTime)
.add("atime", _atime)
.add("mtime", _mtime)
.add("checksums", _checksums)
.add("owner", _owner)
.add("group", _group)
.add("mode", _mode)
.add("accessLatency", _accessLatency)
.add("retentionPolicy", _retentionPolicy)
.add("fileType", _fileType)
.add("locations", _locations)
.add("flags", _flags)
.add("pnfsId", _pnfsId)
.add("storageInfo", _storageInfo)
.add("storageClass", _storageClass)
.add("cacheClass", _cacheClass)
.add("hsm", _hsm)
.omitNullValues()
.toString();
}
@Nonnull
private <T> Optional<T> toOptional(FileAttribute attribute, T value)
{
return isDefined(attribute) ? Optional.of(value) :
Optional.absent();
}
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
if (_flags != null) {
_flags = _flags.entrySet().stream().collect(toMap(e -> e.getKey().intern(), e -> e.getValue()));
}
if (_storageClass != null) {
_storageClass = _storageClass.intern();
}
if (_cacheClass != null) {
if (_cacheClass.isEmpty()) {
_cacheClass = null; // For compatibility with pre-2.17- remove after next golden
} else {
_cacheClass = _cacheClass.intern();
}
}
if (_hsm != null) {
_hsm = _hsm.intern();
}
}
public static FileAttributes ofAccessTime(long when)
{
return of().accessTime(when).build();
}
public static FileAttributes ofAcl(ACL acl)
{
return of().acl(acl).build();
}
public static FileAttributes ofChecksum(Checksum value)
{
return of().checksum(value).build();
}
public static FileAttributes ofCreationTime(long when)
{
return of().creationTime(when).build();
}
public static FileAttributes ofFlag(String name, String value)
{
return of().flag(name, value).build();
}
public static FileAttributes ofFlags(Map<String,String> flags)
{
return of().flags(flags).build();
}
public static FileAttributes ofGid(int gid)
{
return of().gid(gid).build();
}
public static FileAttributes ofMode(int mode)
{
return of().mode(mode).build();
}
public static FileAttributes ofModificationTime(long when)
{
return of().modificationTime(when).build();
}
public static FileAttributes ofPnfsId(String id)
{
return of().pnfsId(id).build();
}
public static FileAttributes ofPnfsId(PnfsId id)
{
return of().pnfsId(id).build();
}
public static FileAttributes ofSize(long size)
{
return of().size(size).build();
}
public static FileAttributes ofStorageInfo(StorageInfo info)
{
return of().storageInfo(info).build();
}
public static FileAttributes ofFileType(FileType type)
{
return of().fileType(type).build();
}
public static FileAttributes ofLocation(String pool)
{
return of().location(pool).build();
}
public static FileAttributes ofLocations(Collection<String> pools)
{
return of().locations(pools).build();
}
public static FileAttributes ofHsm(String hsm)
{
return of().hsm(hsm).build();
}
public static FileAttributes ofStorageClass(String storageClass)
{
return of().storageClass(storageClass).build();
}
public static Builder of()
{
return new FileAttributes().new Builder();
}
public class Builder
{
public Builder accessLatency(AccessLatency al)
{
setAccessLatency(al);
return this;
}
public Builder accessTime(long when)
{
setAccessTime(when);
return this;
}
public Builder acl(ACL acl)
{
setAcl(acl);
return this;
}
public FileAttributes build()
{
return FileAttributes.this;
}
public Builder checksum(Checksum checksum)
{
setChecksums(Collections.singleton(checksum));
return this;
}
public Builder checksums(Set<Checksum> checksums)
{
setChecksums(checksums);
return this;
}
public Builder creationTime(long when)
{
setCreationTime(when);
return this;
}
public Builder fileType(FileType type)
{
setFileType(type);
return this;
}
public Builder flag(String name, String value)
{
if (!isDefined(FLAGS)) {
setFlags(new HashMap());
}
getFlags().put(name, value);
return this;
}
public Builder flags(Map<String, String> flags)
{
if (!isDefined(FLAGS)) {
setFlags(new HashMap());
}
getFlags().putAll(flags);
return this;
}
/**
* Add the primary gid taken from the primary GidPrincipal within the
* Subject.
*/
public Builder gid(Subject subject)
{
return gid((int)Subjects.getPrimaryGid(subject));
}
public Builder gid(int gid)
{
setGroup(gid);
return this;
}
public Builder location(String pool)
{
if (!isDefined(LOCATIONS)) {
setLocations(new ArrayList());
}
getLocations().add(pool);
return this;
}
public Builder locations(Collection<String> pools)
{
if (!isDefined(LOCATIONS)) {
setLocations(new ArrayList());
}
getLocations().addAll(pools);
return this;
}
public Builder mode(int mode)
{
setMode(mode);
return this;
}
public Builder modificationTime(long when)
{
setModificationTime(when);
return this;
}
public Builder pnfsId(String id)
{
setPnfsId(id);
return this;
}
public Builder pnfsId(PnfsId id)
{
setPnfsId(id);
return this;
}
public Builder retentionPolicy(RetentionPolicy rp)
{
setRetentionPolicy(rp);
return this;
}
public Builder size(long size)
{
setSize(size);
return this;
}
public Builder storageInfo(StorageInfo info)
{
setStorageInfo(info);
return this;
}
/**
* Add the uid taken from the UidPrincipal within the Subject.
*/
public Builder uid(Subject subject)
{
return uid((int)Subjects.getUid(subject));
}
public Builder uid(int uid)
{
setOwner(uid);
return this;
}
public Builder hsm(String hsm)
{
setHsm(hsm);
return this;
}
public Builder storageClass(String storageClass)
{
setStorageClass(storageClass);
return this;
}
}
}