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 */ 019package org.nuxeo.drive.adapter.impl; 020 021import org.apache.commons.lang3.StringUtils; 022import org.nuxeo.drive.adapter.FileItem; 023import org.nuxeo.drive.adapter.FolderItem; 024import org.nuxeo.drive.service.FileSystemItemFactory; 025import org.nuxeo.drive.service.NuxeoDriveManager; 026import org.nuxeo.drive.service.VersioningFileSystemItemFactory; 027import org.nuxeo.ecm.core.api.Blob; 028import org.nuxeo.ecm.core.api.CloseableCoreSession; 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 private static final long serialVersionUID = 1L; 045 046 protected String downloadURL; 047 048 protected String digestAlgorithm; 049 050 protected String digest; 051 052 protected boolean canUpdate; 053 054 protected FileSystemItemFactory factory; 055 056 public DocumentBackedFileItem(FileSystemItemFactory factory, DocumentModel doc) { 057 this(factory, doc, false); 058 } 059 060 public DocumentBackedFileItem(FileSystemItemFactory factory, DocumentModel doc, boolean relaxSyncRootConstraint) { 061 this(factory, doc, relaxSyncRootConstraint, true); 062 } 063 064 public DocumentBackedFileItem(FileSystemItemFactory factory, DocumentModel doc, boolean relaxSyncRootConstraint, 065 boolean getLockInfo) { 066 super(factory.getName(), doc, relaxSyncRootConstraint, getLockInfo); 067 initialize(factory, doc); 068 } 069 070 public DocumentBackedFileItem(FileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc) { 071 this(factory, parentItem, doc, false); 072 } 073 074 public DocumentBackedFileItem(FileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 075 boolean relaxSyncRootConstraint) { 076 this(factory, parentItem, doc, relaxSyncRootConstraint, true); 077 } 078 079 public DocumentBackedFileItem(FileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 080 boolean relaxSyncRootConstraint, boolean getLockInfo) { 081 super(factory.getName(), parentItem, doc, relaxSyncRootConstraint, getLockInfo); 082 initialize(factory, doc); 083 } 084 085 /** 086 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 087 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 088 */ 089 @Deprecated 090 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc) { 091 this(factory, doc, false); 092 } 093 094 /** 095 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 096 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 097 */ 098 @Deprecated 099 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc, 100 boolean relaxSyncRootConstraint) { 101 this(factory, doc, relaxSyncRootConstraint, true); 102 } 103 104 /** 105 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 106 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 107 */ 108 @Deprecated 109 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc, 110 boolean relaxSyncRootConstraint, boolean getLockInfo) { 111 super(factory.getName(), doc, relaxSyncRootConstraint, getLockInfo); 112 initialize(factory, doc); 113 } 114 115 /** 116 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 117 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 118 */ 119 @Deprecated 120 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc) { 121 this(factory, parentItem, doc, false); 122 } 123 124 /** 125 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 126 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 127 */ 128 @Deprecated 129 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 130 boolean relaxSyncRootConstraint) { 131 this(factory, parentItem, doc, relaxSyncRootConstraint, true); 132 } 133 134 /** 135 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 136 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 137 */ 138 @Deprecated 139 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 140 boolean relaxSyncRootConstraint, boolean getLockInfo) { 141 super(factory.getName(), parentItem, doc, relaxSyncRootConstraint, getLockInfo); 142 initialize(factory, doc); 143 } 144 145 protected DocumentBackedFileItem() { 146 // Needed for JSON deserialization 147 } 148 149 /*--------------------- FileSystemItem ---------------------*/ 150 @Override 151 public void rename(String name) { 152 try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 153 /* Update doc properties */ 154 DocumentModel doc = getDocument(session); 155 BlobHolder bh = getBlobHolder(doc); 156 Blob blob = getBlob(bh); 157 blob.setFilename(name); 158 bh.setBlob(blob); 159 updateDocTitleIfNeeded(doc, name); 160 doc.putContextData(CoreSession.SOURCE, "drive"); 161 doc = session.saveDocument(doc); 162 session.save(); 163 /* Update FileSystemItem attributes */ 164 this.name = name; 165 updateDownloadURL(); 166 updateLastModificationDate(doc); 167 } 168 } 169 170 /*--------------------- FileItem -----------------*/ 171 @Override 172 public Blob getBlob() { 173 try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 174 DocumentModel doc = getDocument(session); 175 return getBlob(doc); 176 } 177 } 178 179 @Override 180 public String getDownloadURL() { 181 return downloadURL; 182 } 183 184 @Override 185 public String getDigestAlgorithm() { 186 return digestAlgorithm; 187 } 188 189 @Override 190 public String getDigest() { 191 return digest; 192 } 193 194 @Override 195 public boolean getCanUpdate() { 196 return canUpdate; 197 } 198 199 @Override 200 public void setBlob(Blob blob) { 201 try (CloseableCoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 202 /* Update doc properties */ 203 DocumentModel doc = getDocument(session); 204 // If blob's filename is empty, set it to the current name 205 String blobFileName = blob.getFilename(); 206 if (StringUtils.isEmpty(blobFileName)) { 207 blob.setFilename(name); 208 } else { 209 updateDocTitleIfNeeded(doc, blobFileName); 210 name = blobFileName; 211 updateDownloadURL(); 212 } 213 BlobHolder bh = getBlobHolder(doc); 214 bh.setBlob(blob); 215 doc.putContextData(CoreSession.SOURCE, "drive"); 216 doc = session.saveDocument(doc); 217 session.save(); 218 /* Update FileSystemItem attributes */ 219 updateLastModificationDate(doc); 220 updateDigest(getBlob(doc)); 221 } 222 } 223 224 /*--------------------- Protected -----------------*/ 225 /** 226 * @deprecated since 9.1 versioning policy is now handled at versioning service level, as versioning is removed at 227 * drive level, the {@link VersioningFileSystemItemFactory} is not used anymore 228 */ 229 @Deprecated 230 protected final void initialize(VersioningFileSystemItemFactory factory, DocumentModel doc) { 231 initialize((FileSystemItemFactory) factory, doc); 232 } 233 234 protected final void initialize(FileSystemItemFactory factory, DocumentModel doc) { 235 this.factory = factory; 236 Blob blob = getBlob(doc); 237 name = getFileName(blob, doc.getTitle()); 238 folder = false; 239 updateDownloadURL(); 240 updateDigest(blob); 241 if (digest == null) { 242 digestAlgorithm = null; 243 } 244 canUpdate = canRename; 245 } 246 247 protected BlobHolder getBlobHolder(DocumentModel doc) { 248 BlobHolder bh = doc.getAdapter(BlobHolder.class); 249 if (bh == null) { 250 throw new NuxeoException(String.format( 251 "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.", 252 doc.getId())); 253 } 254 return bh; 255 } 256 257 protected Blob getBlob(BlobHolder blobHolder) { 258 Blob blob = blobHolder.getBlob(); 259 if (blob == null) { 260 throw new NuxeoException( 261 "Document has no blob, it is not adaptable as a FileItem and therefore it cannot not be part of the items to synchronize."); 262 } 263 return blob; 264 } 265 266 protected Blob getBlob(DocumentModel doc) { 267 BlobHolder bh = getBlobHolder(doc); 268 return getBlob(bh); 269 } 270 271 protected String getFileName(Blob blob, String docTitle) { 272 String filename = blob.getFilename(); 273 return filename != null ? filename : docTitle; 274 } 275 276 protected void updateDocTitleIfNeeded(DocumentModel doc, String name) { 277 // TODO: not sure about the behavior for the doc title 278 if (this.name.equals(docTitle)) { 279 doc.setPropertyValue("dc:title", name); 280 docTitle = name; 281 } 282 } 283 284 protected void updateDownloadURL() { 285 DownloadService downloadService = Framework.getService(DownloadService.class); 286 // Remove chars that are invalid in filesystem names 287 String escapedFilename = name.replaceAll("(/|\\\\|\\*|<|>|\\?|\"|:|\\|)", "-"); 288 downloadURL = downloadService.getDownloadUrl(repositoryName, docId, DownloadService.BLOBHOLDER_0, 289 escapedFilename); 290 } 291 292 protected void updateDigest(Blob blob) { 293 String blobDigest = blob.getDigest(); 294 if (StringUtils.isEmpty(blobDigest)) { 295 // Force md5 digest algorithm and digest computation for a StringBlob, 296 // typically the note:note property of a Note document 297 digestAlgorithm = FileSystemItemHelper.MD5_DIGEST_ALGORITHM; 298 digest = FileSystemItemHelper.getMD5Digest(blob); 299 } else { 300 digestAlgorithm = blob.getDigestAlgorithm(); 301 digest = blobDigest; 302 } 303 } 304 305 protected NuxeoDriveManager getNuxeoDriveManager() { 306 return Framework.getService(NuxeoDriveManager.class); 307 } 308 309 /*---------- Needed for JSON deserialization ----------*/ 310 protected void setDownloadURL(String downloadURL) { 311 this.downloadURL = downloadURL; 312 } 313 314 protected void setDigestAlgorithm(String digestAlgorithm) { 315 this.digestAlgorithm = digestAlgorithm; 316 } 317 318 protected void setDigest(String digest) { 319 this.digest = digest; 320 } 321 322 protected void setCanUpdate(boolean canUpdate) { 323 this.canUpdate = canUpdate; 324 } 325 326}