/*
* Copyright 2009-2015 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
*/
package com.eucalyptus.objectstorage.entities;
import java.util.Date;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PersistenceContext;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import org.apache.log4j.Logger;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking;
import com.eucalyptus.auth.Accounts;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.auth.principal.UserPrincipal;
import com.eucalyptus.objectstorage.ObjectState;
import com.eucalyptus.objectstorage.exceptions.s3.AccountProblemException;
import com.eucalyptus.objectstorage.util.ObjectStorageProperties;
import com.eucalyptus.storage.msgs.s3.Part;
//TODO: make the a child-class of ObjectEntity
@Entity
@OptimisticLocking(type = OptimisticLockType.NONE)
@PersistenceContext(name = "eucalyptus_osg")
@Table(name = "parts", indexes = {
@Index(name = "IDX_part_uuid", columnList = "part_uuid")
})
public class PartEntity extends S3AccessControlledEntity<ObjectState> implements Comparable {
@Column(name = "object_key")
private String objectKey;
@NotFound(action = NotFoundAction.EXCEPTION)
@ManyToOne(optional = false, targetEntity = Bucket.class, fetch = FetchType.EAGER)
@ForeignKey(name = "FK_bucket")
@JoinColumn(name = "bucket_fk")
private Bucket bucket;
@Column(name = "part_uuid", unique = true, nullable = false)
private String partUuid; // The a uuid for this specific object content & request
@Column(name = "size", nullable = false)
private Long size;
@Column(name = "storage_class", nullable = false)
private String storageClass;
@Column(name = "object_last_modified")
// Distinct from the record modification date, tracks the backend response
private Date objectModifiedTimestamp;
@Column(name = "etag")
private String eTag;
@Column(name = "is_latest")
private Boolean isLatest;
@Column(name = "creation_expiration")
private Long creationExpiration; // Expiration time in system/epoch time to guarantee monotonically increasing values
public Long getCreationExpiration() {
return creationExpiration;
}
public void setCreationExpiration(Long creationExpiration) {
this.creationExpiration = creationExpiration;
}
@Column(name = "upload_id")
private String uploadId;
@Column(name = "part_number")
private Integer partNumber;
private static Logger LOG = Logger.getLogger(PartEntity.class);
public PartEntity() {
super();
}
public PartEntity(Bucket parentBucket, String objectKey, String uploadId) {
super();
this.bucket = parentBucket;
this.uploadId = uploadId;
this.objectKey = objectKey;
}
public PartEntity withBucket(Bucket parentBucket) {
this.setBucket(parentBucket);
return this;
}
public PartEntity withKey(String key) {
this.setObjectKey(key);
return this;
}
public PartEntity withUuid(String uuid) {
this.setPartUuid(uuid);
return this;
}
public PartEntity withUploadId(String uploadId) {
this.setUploadId(uploadId);
return this;
}
public PartEntity withPartNumber(int partNumber) {
this.setPartNumber(partNumber);
return this;
}
/**
* Sets state only, explicitly nulls 'lastState'. Use ONLY for query examples
*
* @param s
* @return
*/
public PartEntity withState(ObjectState s) {
this.setState(s);
this.setLastState(null);
return this;
}
public Bucket getBucket() {
return bucket;
}
public void setBucket(Bucket bucket) {
this.bucket = bucket;
}
@PrePersist
public void ensureFieldsNotNull() {
if (this.partUuid == null) {
// generate a new one
this.partUuid = generateInternalKey(this.objectKey);
}
}
/**
* Initialize this as a new object entity representing an object to PUT
*
* @param bucket
* @param objectKey
* @param usr
*/
public static PartEntity newInitializedForCreate(@Nonnull Bucket bucket, @Nonnull String objectKey, @Nonnull String uploadId,
@Nonnull Integer partNumber, long contentLength, @Nonnull UserPrincipal usr) throws Exception {
PartEntity entity = new PartEntity(bucket, objectKey, uploadId);
entity.setPartUuid(generateInternalKey(objectKey));
entity.setOwnerCanonicalId(usr.getCanonicalId());
entity.setOwnerDisplayName(usr.getAccountAlias());
entity.setPartNumber(partNumber);
entity.setUploadId(uploadId);
entity.setOwnerIamUserId(usr.getUserId());
entity.setObjectModifiedTimestamp(null);
entity.setSize(contentLength);
entity.setIsLatest(false);
entity.setStorageClass(ObjectStorageProperties.STORAGE_CLASS.STANDARD.toString());
entity.updateCreationExpiration();
entity.setState(ObjectState.creating);
return entity;
}
public void updateCreationExpiration() {
this.creationExpiration = System.currentTimeMillis() + (1000 * ObjectStorageProperties.OBJECT_CREATION_EXPIRATION_INTERVAL_SEC);
}
private static String generateInternalKey(@Nonnull String key) {
return UUID.randomUUID().toString() + "-" + key;
}
public String geteTag() {
return eTag;
}
public void seteTag(String eTag) {
this.eTag = eTag;
}
public Date getObjectModifiedTimestamp() {
return objectModifiedTimestamp;
}
public void setObjectModifiedTimestamp(Date objectModifiedTimestamp) {
this.objectModifiedTimestamp = objectModifiedTimestamp;
}
@Override
public String getResourceFullName() {
return getBucket().getBucketName() + "/" + getObjectKey() + "?uploadId=" + this.uploadId + "&partNumber=" + this.partNumber;
}
public String getObjectKey() {
return objectKey;
}
public void setObjectKey(String objectKey) {
this.objectKey = objectKey;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
public String getStorageClass() {
return storageClass;
}
public void setStorageClass(String storageClass) {
this.storageClass = storageClass;
}
public int compareTo(Object o) {
return this.objectKey.compareTo(((PartEntity) o).getObjectKey());
}
public boolean isPending() {
return (getObjectModifiedTimestamp() == null);
}
public String getPartUuid() {
return partUuid;
}
public void setPartUuid(String partUuid) {
this.partUuid = partUuid;
}
public Boolean getIsLatest() {
return isLatest;
}
public void setIsLatest(Boolean isLatest) {
this.isLatest = isLatest;
}
public String getUploadId() {
return uploadId;
}
public void setUploadId(String uploadId) {
this.uploadId = uploadId;
}
public Integer getPartNumber() {
return partNumber;
}
public void setPartNumber(Integer partNumber) {
this.partNumber = partNumber;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((partUuid == null) ? 0 : partUuid.hashCode());
result = prime * result + ((objectKey == null) ? 0 : objectKey.hashCode());
result = prime * result + ((uploadId == null) ? 0 : uploadId.hashCode());
result = prime * result + ((partNumber == null) ? 0 : partNumber.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PartEntity other = (PartEntity) obj;
if (objectKey == null) {
if (other.objectKey != null)
return false;
} else if (!objectKey.equals(other.objectKey))
return false;
if (partUuid == null) {
if (other.partUuid != null)
return false;
} else if (!partUuid.equals(other.partUuid))
return false;
if (uploadId == null) {
if (other.uploadId != null)
return false;
} else if (!uploadId.equals(other.uploadId))
return false;
if (partNumber == null) {
if (other.partNumber != null)
return false;
} else if (!partNumber.equals(other.partNumber))
return false;
return true;
}
/**
* Return a ListEntry for this entity
*
* @return
*/
public Part toPartListEntry() {
Part e = new Part();
e.setEtag("\"" + this.geteTag() + "\"");
e.setPartNumber(this.getPartNumber());
e.setLastModified(this.getObjectModifiedTimestamp());
e.setSize(this.getSize());
return e;
}
}