/* * Copyright 2013-2016 EMC Corporation. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * or in the "license" file accompanying this file. This file 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 com.emc.ecs.sync.model; import com.emc.ecs.sync.storage.SyncStorage; import com.emc.ecs.sync.util.EnhancedInputStream; import com.emc.ecs.sync.util.LazyValue; import com.emc.ecs.sync.util.PerformanceListener; import com.emc.ecs.sync.util.SyncUtil; import com.emc.object.util.ProgressInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.xml.bind.DatatypeConverter; import java.io.InputStream; import java.util.HashMap; import java.util.Map; public class SyncObject implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(SyncObject.class); private SyncStorage source; private String relativePath; private ObjectMetadata metadata; private EnhancedInputStream enhancedStream; private ObjectAcl acl; private boolean postStreamUpdateRequired; private Map<String, Object> properties = new HashMap<>(); private byte[] md5; private LazyValue<InputStream> lazyStream; private LazyValue<ObjectAcl> lazyAcl; private long bytesRead; public SyncObject(SyncStorage source, String relativePath, ObjectMetadata metadata) { this(source, relativePath, metadata, null, null); } public SyncObject(SyncStorage source, String relativePath, ObjectMetadata metadata, InputStream dataStream, ObjectAcl acl) { assert source != null : "source cannot be null"; assert relativePath != null : "relativePath cannot be null"; assert metadata != null : "metadata cannot be null"; this.source = source; this.relativePath = relativePath; this.metadata = metadata; if (dataStream != null) setDataStream(dataStream); this.acl = acl; } public SyncStorage getSource() { return source; } /** * Gets the relative path for the object. If the target is a * namespace target, this path will be used when computing the * absolute path in the target, relative to the target root. */ public String getRelativePath() { return relativePath; } public ObjectMetadata getMetadata() { return metadata; } public void setMetadata(ObjectMetadata metadata) { this.metadata = metadata; } public synchronized InputStream getDataStream() { if (enhancedStream == null && lazyStream != null) { setDataStream(lazyStream.get()); } return enhancedStream; } public void setDataStream(InputStream dataStream) { if (dataStream == null) { enhancedStream = null; } else { wrap(dataStream); } } public void setLazyStream(LazyValue<InputStream> lazyStream) { this.lazyStream = lazyStream; } public synchronized ObjectAcl getAcl() { if (acl == null && lazyAcl != null) { setAcl(lazyAcl.get()); } return acl; } public void setAcl(ObjectAcl acl) { this.acl = acl; } public void setLazyAcl(LazyValue<ObjectAcl> lazyAcl) { this.lazyAcl = lazyAcl; } public boolean isPostStreamUpdateRequired() { return postStreamUpdateRequired; } /** * Specifies whether this object has stream-dependent metadata (metadata that changes after the object data is * streamed). I.e. the encryption filter will add a checksum, unencrypted size and signature. * <p> * If set, the object's metadata will be updated in the target after its data is transferred. * <p> * Default is false. Set to true if your object has metadata that changes after its data is streamed. */ public void setPostStreamUpdateRequired(boolean postStreamUpdateRequired) { this.postStreamUpdateRequired = postStreamUpdateRequired; } public Map<String, Object> getProperties() { return properties; } public void setProperties(Map<String, Object> properties) { assert properties != null : "properties must not be null"; this.properties = properties; } public Object getProperty(String name) { return properties.get(name); } public void setProperty(String name, Object value) { properties.put(name, value); } public void removeProperty(String name) { properties.remove(name); } public long getBytesRead() { if (bytesRead > 0) { return bytesRead; } else if (enhancedStream != null) { return enhancedStream.getBytesRead(); } else { return 0; } } public void setBytesRead(long bytesRead) { this.bytesRead = bytesRead; } public String getMd5Hex(boolean forceRead) { byte[] md5 = getMd5(forceRead); if (md5 == null) return null; return DatatypeConverter.printHexBinary(md5); } @Override public void close() throws Exception { try { if (enhancedStream != null) enhancedStream.close(); } catch (Throwable t) { log.warn("could not close data stream", t); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SyncObject object = (SyncObject) o; return relativePath.equals(object.relativePath); } @Override public int hashCode() { return relativePath.hashCode(); } private synchronized byte[] getMd5(boolean forceRead) { if (md5 == null) { if (forceRead) getDataStream(); // make sure lazy streams are initialized if (enhancedStream == null) return null; if (!enhancedStream.isClosed()) { if (!forceRead || enhancedStream.getBytesRead() > 0) throw new IllegalStateException("Cannot call getMd5 until stream is closed"); SyncUtil.consumeAndCloseStream(enhancedStream); } md5 = enhancedStream.getMd5Digest(); } return md5; } private void wrap(InputStream dataStream) { if (source != null && source.getOptions().isMonitorPerformance()) dataStream = new ProgressInputStream(dataStream, new PerformanceListener(source.getReadWindow())); enhancedStream = new EnhancedInputStream(dataStream, true); } public SyncObject withAcl(ObjectAcl acl) { setAcl(acl); return this; } public SyncObject withLazyStream(LazyValue<InputStream> lazyStream) { setLazyStream(lazyStream); return this; } public SyncObject withLazyAcl(LazyValue<ObjectAcl> lazyAcl) { setLazyAcl(lazyAcl); return this; } }