001/* 002 * (C) Copyright 2012-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Antoine Taillefer <ataillefer@nuxeo.com> 018 * Mickaƫl Schoentgen <mschoentgen@nuxeo.com> 019 */ 020package org.nuxeo.drive.adapter.impl; 021 022import org.apache.commons.lang3.StringUtils; 023import org.nuxeo.drive.adapter.FileItem; 024import org.nuxeo.drive.adapter.FolderItem; 025import org.nuxeo.drive.service.FileSystemItemFactory; 026import org.nuxeo.drive.service.NuxeoDriveManager; 027import org.nuxeo.drive.service.VersioningFileSystemItemFactory; 028import org.nuxeo.ecm.core.api.Blob; 029import org.nuxeo.ecm.core.api.CoreInstance; 030import org.nuxeo.ecm.core.api.CoreSession; 031import org.nuxeo.ecm.core.api.DocumentModel; 032import org.nuxeo.ecm.core.api.NuxeoException; 033import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 034import org.nuxeo.ecm.core.io.download.DownloadService; 035import org.nuxeo.runtime.api.Framework; 036 037/** 038 * {@link DocumentModel} backed implementation of a {@link FileItem}. 039 * 040 * @author Antoine Taillefer 041 */ 042public class DocumentBackedFileItem extends AbstractDocumentBackedFileSystemItem implements FileItem { 043 044 protected String downloadURL; 045 046 protected String digestAlgorithm; 047 048 protected String digest; 049 050 /** @since 11.1 */ 051 protected long size; 052 053 protected boolean canUpdate; 054 055 protected FileSystemItemFactory factory; 056 057 public DocumentBackedFileItem(FileSystemItemFactory factory, DocumentModel doc) { 058 this(factory, doc, false); 059 } 060 061 public DocumentBackedFileItem(FileSystemItemFactory factory, DocumentModel doc, boolean relaxSyncRootConstraint) { 062 this(factory, doc, relaxSyncRootConstraint, true); 063 } 064 065 public DocumentBackedFileItem(FileSystemItemFactory factory, DocumentModel doc, boolean relaxSyncRootConstraint, 066 boolean getLockInfo) { 067 super(factory.getName(), doc, relaxSyncRootConstraint, getLockInfo); 068 initialize(factory, doc); 069 } 070 071 public DocumentBackedFileItem(FileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc) { 072 this(factory, parentItem, doc, false); 073 } 074 075 public DocumentBackedFileItem(FileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 076 boolean relaxSyncRootConstraint) { 077 this(factory, parentItem, doc, relaxSyncRootConstraint, true); 078 } 079 080 public DocumentBackedFileItem(FileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 081 boolean relaxSyncRootConstraint, boolean getLockInfo) { 082 super(factory.getName(), parentItem, doc, relaxSyncRootConstraint, getLockInfo); 083 initialize(factory, doc); 084 } 085 086 /** 087 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 088 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 089 */ 090 @Deprecated 091 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc) { 092 this(factory, doc, false); 093 } 094 095 /** 096 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 097 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 098 */ 099 @Deprecated 100 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc, 101 boolean relaxSyncRootConstraint) { 102 this(factory, doc, relaxSyncRootConstraint, true); 103 } 104 105 /** 106 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 107 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 108 */ 109 @Deprecated 110 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc, 111 boolean relaxSyncRootConstraint, boolean getLockInfo) { 112 super(factory.getName(), doc, relaxSyncRootConstraint, getLockInfo); 113 initialize(factory, doc); 114 } 115 116 /** 117 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 118 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 119 */ 120 @Deprecated 121 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc) { 122 this(factory, parentItem, doc, false); 123 } 124 125 /** 126 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 127 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 128 */ 129 @Deprecated 130 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 131 boolean relaxSyncRootConstraint) { 132 this(factory, parentItem, doc, relaxSyncRootConstraint, true); 133 } 134 135 /** 136 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 137 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 138 */ 139 @Deprecated 140 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 141 boolean relaxSyncRootConstraint, boolean getLockInfo) { 142 super(factory.getName(), parentItem, doc, relaxSyncRootConstraint, getLockInfo); 143 initialize(factory, doc); 144 } 145 146 protected DocumentBackedFileItem() { 147 // Needed for JSON deserialization 148 } 149 150 /*--------------------- FileSystemItem ---------------------*/ 151 @Override 152 public void rename(String name) { 153 CoreSession session = CoreInstance.getCoreSession(repositoryName, principal); 154 /* Update doc properties */ 155 DocumentModel doc = getDocument(session); 156 BlobHolder bh = getBlobHolder(doc); 157 Blob blob = getBlob(bh); 158 blob.setFilename(name); 159 bh.setBlob(blob); 160 updateDocTitleIfNeeded(doc, name); 161 doc.putContextData(CoreSession.SOURCE, "drive"); 162 doc = session.saveDocument(doc); 163 session.save(); 164 /* Update FileSystemItem attributes */ 165 this.name = name; 166 updateDownloadURL(); 167 updateLastModificationDate(doc); 168 } 169 170 /*--------------------- FileItem -----------------*/ 171 @Override 172 public Blob getBlob() { 173 CoreSession session = CoreInstance.getCoreSession(repositoryName, principal); 174 DocumentModel doc = getDocument(session); 175 return getBlob(doc); 176 } 177 178 @Override 179 public String getDownloadURL() { 180 return downloadURL; 181 } 182 183 @Override 184 public String getDigestAlgorithm() { 185 return digestAlgorithm; 186 } 187 188 @Override 189 public String getDigest() { 190 return digest; 191 } 192 193 @Override 194 public boolean getCanUpdate() { 195 return canUpdate; 196 } 197 198 /** @since 11.1 */ 199 @Override 200 public long getSize() { 201 return size; 202 } 203 204 @Override 205 public void setBlob(Blob blob) { 206 CoreSession session = CoreInstance.getCoreSession(repositoryName, principal); 207 /* Update doc properties */ 208 DocumentModel doc = getDocument(session); 209 // If blob's filename is empty, set it to the current name 210 String blobFileName = blob.getFilename(); 211 if (StringUtils.isEmpty(blobFileName)) { 212 blob.setFilename(name); 213 } else { 214 updateDocTitleIfNeeded(doc, blobFileName); 215 name = blobFileName; 216 updateDownloadURL(); 217 } 218 BlobHolder bh = getBlobHolder(doc); 219 bh.setBlob(blob); 220 doc.putContextData(CoreSession.SOURCE, "drive"); 221 doc = session.saveDocument(doc); 222 session.save(); 223 /* Update FileSystemItem attributes */ 224 updateLastModificationDate(doc); 225 updateDigest(getBlob(doc)); 226 updateSize(blob); 227 } 228 229 /*--------------------- Object -----------------*/ 230 // Override equals and hashCode to explicitly show that their implementation rely on the parent class and doesn't 231 // depend on the fields added to this class. 232 @Override 233 public boolean equals(Object obj) { 234 return super.equals(obj); 235 } 236 237 @Override 238 public int hashCode() { 239 return super.hashCode(); 240 } 241 242 /*--------------------- Protected -----------------*/ 243 /** 244 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 245 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 246 */ 247 @Deprecated 248 protected final void initialize(VersioningFileSystemItemFactory factory, DocumentModel doc) { 249 initialize((FileSystemItemFactory) factory, doc); 250 } 251 252 protected final void initialize(FileSystemItemFactory factory, DocumentModel doc) { 253 this.factory = factory; 254 Blob blob = getBlob(doc); 255 name = getFileName(blob, doc.getTitle()); 256 folder = false; 257 updateDownloadURL(); 258 updateDigest(blob); 259 updateSize(blob); 260 if (digest == null) { 261 digestAlgorithm = null; 262 } 263 canUpdate = canRename; 264 } 265 266 protected BlobHolder getBlobHolder(DocumentModel doc) { 267 BlobHolder bh = doc.getAdapter(BlobHolder.class); 268 if (bh == null) { 269 throw new NuxeoException(String.format( 270 "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.", 271 doc.getId())); 272 } 273 return bh; 274 } 275 276 protected Blob getBlob(BlobHolder blobHolder) { 277 Blob blob = blobHolder.getBlob(); 278 if (blob == null) { 279 throw new NuxeoException( 280 "Document has no blob, it is not adaptable as a FileItem and therefore it cannot not be part of the items to synchronize."); 281 } 282 return blob; 283 } 284 285 protected Blob getBlob(DocumentModel doc) { 286 BlobHolder bh = getBlobHolder(doc); 287 return getBlob(bh); 288 } 289 290 protected String getFileName(Blob blob, String docTitle) { 291 String filename = blob.getFilename(); 292 return filename != null ? filename : docTitle; 293 } 294 295 protected void updateDocTitleIfNeeded(DocumentModel doc, String name) { 296 // TODO: not sure about the behavior for the doc title 297 if (this.name.equals(docTitle)) { 298 doc.setPropertyValue("dc:title", name); 299 docTitle = name; 300 } 301 } 302 303 protected void updateDownloadURL() { 304 DownloadService downloadService = Framework.getService(DownloadService.class); 305 // Remove chars that are invalid in filesystem names 306 String escapedFilename = name.replaceAll("(/|\\\\|\\*|<|>|\\?|\"|:|\\|)", "-"); 307 downloadURL = downloadService.getDownloadUrl(repositoryName, docId, DownloadService.BLOBHOLDER_0, 308 escapedFilename); 309 } 310 311 protected void updateDigest(Blob blob) { 312 String blobDigest = blob.getDigest(); 313 if (StringUtils.isEmpty(blobDigest)) { 314 // Force md5 digest algorithm and digest computation for a StringBlob, 315 // typically the note:note property of a Note document 316 digestAlgorithm = FileSystemItemHelper.MD5_DIGEST_ALGORITHM; 317 digest = FileSystemItemHelper.getMD5Digest(blob); 318 } else { 319 digestAlgorithm = blob.getDigestAlgorithm(); 320 digest = blobDigest; 321 } 322 } 323 324 /** @since 11.1 */ 325 protected void updateSize(Blob blob) { 326 long length = blob.getLength(); 327 size = length > 0 ? length : 0; 328 } 329 330 protected NuxeoDriveManager getNuxeoDriveManager() { 331 return Framework.getService(NuxeoDriveManager.class); 332 } 333 334 /*---------- Needed for JSON deserialization ----------*/ 335 protected void setDownloadURL(String downloadURL) { 336 this.downloadURL = downloadURL; 337 } 338 339 protected void setDigestAlgorithm(String digestAlgorithm) { 340 this.digestAlgorithm = digestAlgorithm; 341 } 342 343 protected void setDigest(String digest) { 344 this.digest = digest; 345 } 346 347 protected void setCanUpdate(boolean canUpdate) { 348 this.canUpdate = canUpdate; 349 } 350 351 /** @since 11.1 */ 352 protected void setSize(long size) { 353 this.size = size; 354 } 355 356}