001/* 002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Antoine Taillefer <ataillefer@nuxeo.com> 016 */ 017package org.nuxeo.drive.adapter.impl; 018 019import org.apache.commons.lang.StringUtils; 020import org.nuxeo.drive.adapter.FileItem; 021import org.nuxeo.drive.adapter.FolderItem; 022import org.nuxeo.drive.service.NuxeoDriveManager; 023import org.nuxeo.drive.service.VersioningFileSystemItemFactory; 024import org.nuxeo.ecm.core.api.Blob; 025import org.nuxeo.ecm.core.api.CoreInstance; 026import org.nuxeo.ecm.core.api.CoreSession; 027import org.nuxeo.ecm.core.api.DocumentModel; 028import org.nuxeo.ecm.core.api.NuxeoException; 029import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 030import org.nuxeo.ecm.core.io.download.DownloadService; 031import org.nuxeo.runtime.api.Framework; 032 033/** 034 * {@link DocumentModel} backed implementation of a {@link FileItem}. 035 * 036 * @author Antoine Taillefer 037 */ 038public class DocumentBackedFileItem extends AbstractDocumentBackedFileSystemItem implements FileItem { 039 040 private static final long serialVersionUID = 1L; 041 042 protected String downloadURL; 043 044 protected String digestAlgorithm; 045 046 protected String digest; 047 048 protected boolean canUpdate; 049 050 protected VersioningFileSystemItemFactory factory; 051 052 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc) { 053 this(factory, doc, false); 054 } 055 056 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc, 057 boolean relaxSyncRootConstraint) { 058 super(factory.getName(), doc, relaxSyncRootConstraint); 059 initialize(factory, doc); 060 } 061 062 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc) { 063 this(factory, parentItem, doc, false); 064 } 065 066 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 067 boolean relaxSyncRootConstraint) { 068 super(factory.getName(), parentItem, doc, relaxSyncRootConstraint); 069 initialize(factory, doc); 070 } 071 072 protected DocumentBackedFileItem() { 073 // Needed for JSON deserialization 074 } 075 076 /*--------------------- FileSystemItem ---------------------*/ 077 @Override 078 public void rename(String name) { 079 try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 080 /* Update doc properties */ 081 DocumentModel doc = getDocument(session); 082 // Handle versioning 083 FileSystemItemHelper.versionIfNeeded(factory, doc, session); 084 BlobHolder bh = getBlobHolder(doc); 085 Blob blob = getBlob(bh); 086 blob.setFilename(name); 087 bh.setBlob(blob); 088 updateDocTitleIfNeeded(doc, name); 089 doc = session.saveDocument(doc); 090 session.save(); 091 /* Update FileSystemItem attributes */ 092 this.name = name; 093 updateDownloadURL(); 094 updateLastModificationDate(doc); 095 } 096 } 097 098 /*--------------------- FileItem -----------------*/ 099 @Override 100 public Blob getBlob() { 101 try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 102 DocumentModel doc = getDocument(session); 103 return getBlob(doc); 104 } 105 } 106 107 @Override 108 public String getDownloadURL() { 109 return downloadURL; 110 } 111 112 @Override 113 public String getDigestAlgorithm() { 114 return digestAlgorithm; 115 } 116 117 @Override 118 public String getDigest() { 119 return digest; 120 } 121 122 @Override 123 public boolean getCanUpdate() { 124 return canUpdate; 125 } 126 127 @Override 128 public void setBlob(Blob blob) { 129 try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 130 /* Update doc properties */ 131 DocumentModel doc = getDocument(session); 132 // Handle versioning 133 FileSystemItemHelper.versionIfNeeded(factory, doc, session); 134 // If blob's filename is empty, set it to the current name 135 String blobFileName = blob.getFilename(); 136 if (StringUtils.isEmpty(blobFileName)) { 137 blob.setFilename(name); 138 } else { 139 updateDocTitleIfNeeded(doc, blobFileName); 140 name = blobFileName; 141 updateDownloadURL(); 142 } 143 BlobHolder bh = getBlobHolder(doc); 144 bh.setBlob(blob); 145 doc = session.saveDocument(doc); 146 session.save(); 147 /* Update FileSystemItem attributes */ 148 updateLastModificationDate(doc); 149 updateDigest(getBlob(doc)); 150 } 151 } 152 153 /*--------------------- Protected -----------------*/ 154 protected final void initialize(VersioningFileSystemItemFactory factory, DocumentModel doc) { 155 this.factory = factory; 156 Blob blob = getBlob(doc); 157 name = getFileName(blob, doc.getTitle()); 158 folder = false; 159 updateDownloadURL(); 160 updateDigest(blob); 161 if (digest == null) { 162 digestAlgorithm = null; 163 } 164 canUpdate = canRename; 165 } 166 167 protected BlobHolder getBlobHolder(DocumentModel doc) { 168 BlobHolder bh = doc.getAdapter(BlobHolder.class); 169 if (bh == null) { 170 throw new NuxeoException( 171 String.format( 172 "Document %s is not a BlobHolder, it is not adaptable as a FileItem and therefore it cannot not be part of the items to synchronize.", 173 doc.getId())); 174 } 175 return bh; 176 } 177 178 protected Blob getBlob(BlobHolder blobHolder) { 179 Blob blob = blobHolder.getBlob(); 180 if (blob == null) { 181 throw new NuxeoException( 182 "Document has no blob, it is not adaptable as a FileItem and therefore it cannot not be part of the items to synchronize."); 183 } 184 return blob; 185 } 186 187 protected Blob getBlob(DocumentModel doc) { 188 BlobHolder bh = getBlobHolder(doc); 189 return getBlob(bh); 190 } 191 192 protected String getFileName(Blob blob, String docTitle) { 193 String filename = blob.getFilename(); 194 return filename != null ? filename : docTitle; 195 } 196 197 protected void updateDocTitleIfNeeded(DocumentModel doc, String name) { 198 // TODO: not sure about the behavior for the doc title 199 if (this.name.equals(docTitle)) { 200 doc.setPropertyValue("dc:title", name); 201 docTitle = name; 202 } 203 } 204 205 protected void updateDownloadURL() { 206 DownloadService downloadService = Framework.getService(DownloadService.class); 207 // Remove chars that are invalid in filesystem names 208 String escapedFilename = name.replaceAll("(/|\\\\|\\*|<|>|\\?|\"|:|\\|)", "-"); 209 downloadURL = downloadService.getDownloadUrl(repositoryName, docId, DownloadService.BLOBHOLDER_0, escapedFilename); 210 } 211 212 protected void updateDigest(Blob blob) { 213 String blobDigest = blob.getDigest(); 214 if (StringUtils.isEmpty(blobDigest)) { 215 // Force md5 digest algorithm and digest computation for a StringBlob, 216 // typically the note:note property of a Note document 217 digestAlgorithm = FileSystemItemHelper.MD5_DIGEST_ALGORITHM; 218 digest = FileSystemItemHelper.getMD5Digest(blob); 219 } else { 220 digestAlgorithm = blob.getDigestAlgorithm(); 221 digest = blobDigest; 222 } 223 } 224 225 protected NuxeoDriveManager getNuxeoDriveManager() { 226 return Framework.getLocalService(NuxeoDriveManager.class); 227 } 228 229 /*---------- Needed for JSON deserialization ----------*/ 230 protected void setDownloadURL(String downloadURL) { 231 this.downloadURL = downloadURL; 232 } 233 234 protected void setDigestAlgorithm(String digestAlgorithm) { 235 this.digestAlgorithm = digestAlgorithm; 236 } 237 238 protected void setDigest(String digest) { 239 this.digest = digest; 240 } 241 242 protected void setCanUpdate(boolean canUpdate) { 243 this.canUpdate = canUpdate; 244 } 245 246}