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