package com.emc.ecs.sync.storage.s3; import com.emc.ecs.sync.model.ObjectMetadata; import com.emc.ecs.sync.model.SyncObject; import com.emc.ecs.sync.storage.SyncStorage; import org.apache.commons.compress.utils.Charsets; import javax.xml.bind.DatatypeConverter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.List; public class S3ObjectVersion extends SyncObject { private String versionId; private String eTag; private boolean latest; private boolean deleteMarker; public S3ObjectVersion(SyncStorage source, String relativePath, ObjectMetadata metadata) { super(source, relativePath, metadata); } public String getVersionId() { return versionId; } public void setVersionId(String versionId) { this.versionId = versionId; } public S3ObjectVersion withVersionId(String versionId) { setVersionId(versionId); return this; } public String getETag() { return eTag; } public void setETag(String eTag) { this.eTag = eTag; } public S3ObjectVersion withETag(String eTag) { setETag(eTag); return this; } public boolean isLatest() { return latest; } public void setLatest(boolean latest) { this.latest = latest; } public S3ObjectVersion withLatest(boolean latest) { setLatest(latest); return this; } public boolean isDeleteMarker() { return deleteMarker; } public void setDeleteMarker(boolean deleteMarker) { this.deleteMarker = deleteMarker; } public S3ObjectVersion withDeleteMarker(boolean deleteMarker) { setDeleteMarker(deleteMarker); return this; } /** * Generates a standard MD5 (from the object data) for individual versions, but for an instance that holds the entire * version list, generates an aggregate MD5 (of the individual MD5s) of all versions */ @Override public String getMd5Hex(boolean forceRead) { // only the latest version (the one that is referenced by the ObjectContext) will have this property List versions = (List) getProperty(AbstractS3Storage.PROP_OBJECT_VERSIONS); if (versions == null) return super.getMd5Hex(forceRead); // build canonical string of all versions (deleteMarker, eTag) and hash it StringBuilder canonicalString = new StringBuilder("["); for (Object versionO : versions) { S3ObjectVersion version = (S3ObjectVersion) versionO; String md5 = (version == this) ? super.getMd5Hex(forceRead) : version.getMd5Hex(forceRead); canonicalString.append("{") .append("\"deleteMarker\":").append(version.isDeleteMarker()) .append("\"md5\":\"").append(md5).append("\"") .append("}"); } canonicalString.append("]"); try { MessageDigest digest = MessageDigest.getInstance("md5"); return DatatypeConverter.printHexBinary(digest.digest(canonicalString.toString().getBytes(Charsets.UTF_8))); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Could not initialize MD5", e); } } }