package models.filestore; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Calendar; import javax.jcr.nodetype.NodeType; import javax.xml.bind.annotation.adapters.HexBinaryAdapter; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.jcrom.AbstractJcrEntity; import org.jcrom.JcrDataProvider; import org.jcrom.JcrDataProviderImpl; import org.jcrom.JcrFile; import org.jcrom.annotations.JcrBaseVersionName; import org.jcrom.annotations.JcrFileNode; import org.jcrom.annotations.JcrIdentifier; import org.jcrom.annotations.JcrNode; import org.jcrom.annotations.JcrParentNode; import org.jcrom.annotations.JcrProperty; import org.jcrom.annotations.JcrProtectedProperty; import org.jcrom.annotations.JcrVersionName; import play.api.libs.Files.TemporaryFile; import play.api.libs.Files.TemporaryFile$; @JcrNode( nodeType = NodeType.NT_UNSTRUCTURED, mixinTypes = { NodeType.MIX_CREATED, NodeType.MIX_LAST_MODIFIED, NodeType.MIX_REFERENCEABLE, NodeType.MIX_VERSIONABLE }, classNameProperty = "className") public class File extends AbstractJcrEntity implements Child<Folder> { private static final long serialVersionUID = 1L; @JcrIdentifier protected String id; /** * Immediate predecessor of the next version created. * (In this case, also the latest version.) */ @JcrBaseVersionName protected String latestVersion; @JcrVersionName protected String version; @JcrProtectedProperty(name="jcr:created") protected Calendar created; @JcrProtectedProperty(name="jcr:createdBy") protected String createdBy; @JcrProperty(name="jcr:lastModified") protected Calendar lastModified; @JcrProperty(name="jcr:lastModifiedBy") protected String lastModifiedBy; @JcrProperty(name="sha512") protected byte[] digest = new byte[512/8]; @JcrParentNode protected Folder parent; @JcrFileNode protected JcrFile data; public File() { super(); } public File(Folder folder, String name, String mime, InputStream data) { super(); this.parent = folder; this.setName(name); this.data = new JcrFile(); this.data.setName(name); this.setMimeType(mime); this.setData(data); } protected InputStream bufferAndDigest(InputStream data) { final MessageDigest md; try { md = MessageDigest.getInstance("SHA-512"); } catch (NoSuchAlgorithmException e) { // Should never happen throw new RuntimeException(e); } final TemporaryFile tf = TemporaryFile$.MODULE$.apply("aorraTempData", ""); try { FileUtils.copyInputStreamToFile(new DigestInputStream(data, md), tf.file()); } catch (IOException e) { throw new RuntimeException(e); } this.digest = md.digest(); try { return new FileInputStream(tf.file()) { // Reference to couple FileInputStream lifetime to TemporaryFile @SuppressWarnings("unused") private final TemporaryFile temporaryFile = tf; }; } catch (FileNotFoundException e) { // Rather unlikely to occur - certainly not recoverable throw new RuntimeException(e); } } public void setMimeType(final String mime) { this.data.setMimeType(mime); } public void setData(final InputStream data) { this.data.setDataProvider(new JcrDataProviderImpl(bufferAndDigest(data))); this.data.setLastModified(Calendar.getInstance()); } public String getMimeType() { return this.data.getMimeType(); } public JcrDataProvider getDataProvider() { return this.data.getDataProvider(); } @Override public String getId() { return id; } public String getVersion() { return version; } public String getLatestVersion() { return version; } @Override public Folder getParent() { return parent; } protected void setDigest(byte[] digest) { this.digest = digest; } public String getDigest() { return new HexBinaryAdapter().marshal(digest).toLowerCase(); } @Override public String toString() { return String.format("%s [%s] (%s)", getPath(), getId(), getDigest()); } public String getLastModifiedBy() { return lastModifiedBy; } public Calendar getLastModified() { return lastModified; } public void setLastModified(String userId) { this.lastModifiedBy = userId; this.lastModified = Calendar.getInstance(); } public boolean containsSameDataAs(File other) { if (other == null) return false; return new EqualsBuilder() .append(getDigest(), other.getDigest()) .append(getLastModified(), other.getLastModified()) .isEquals(); } }