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.NuxeoDriveManager; 025import org.nuxeo.drive.service.VersioningFileSystemItemFactory; 026import org.nuxeo.ecm.core.api.Blob; 027import org.nuxeo.ecm.core.api.CoreInstance; 028import org.nuxeo.ecm.core.api.CoreSession; 029import org.nuxeo.ecm.core.api.DocumentModel; 030import org.nuxeo.ecm.core.api.NuxeoException; 031import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 032import org.nuxeo.ecm.core.io.download.DownloadService; 033import org.nuxeo.runtime.api.Framework; 034 035/** 036 * {@link DocumentModel} backed implementation of a {@link FileItem}. 037 * 038 * @author Antoine Taillefer 039 */ 040public class DocumentBackedFileItem extends AbstractDocumentBackedFileSystemItem implements FileItem { 041 042 private static final long serialVersionUID = 1L; 043 044 protected String downloadURL; 045 046 protected String digestAlgorithm; 047 048 protected String digest; 049 050 protected boolean canUpdate; 051 052 protected VersioningFileSystemItemFactory factory; 053 054 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc) { 055 this(factory, doc, false); 056 } 057 058 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc, 059 boolean relaxSyncRootConstraint) { 060 this(factory, doc, relaxSyncRootConstraint, true); 061 } 062 063 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, DocumentModel doc, 064 boolean relaxSyncRootConstraint, boolean getLockInfo) { 065 super(factory.getName(), doc, relaxSyncRootConstraint, getLockInfo); 066 initialize(factory, doc); 067 } 068 069 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc) { 070 this(factory, parentItem, doc, false); 071 } 072 073 public DocumentBackedFileItem(VersioningFileSystemItemFactory factory, FolderItem parentItem, DocumentModel doc, 074 boolean relaxSyncRootConstraint) { 075 this(factory, parentItem, doc, relaxSyncRootConstraint, true); 076 } 077 078 public DocumentBackedFileItem(VersioningFileSystemItemFactory 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 protected DocumentBackedFileItem() { 085 // Needed for JSON deserialization 086 } 087 088 /*--------------------- FileSystemItem ---------------------*/ 089 @Override 090 public void rename(String name) { 091 try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 092 /* Update doc properties */ 093 DocumentModel doc = getDocument(session); 094 // Handle versioning 095 FileSystemItemHelper.versionIfNeeded(factory, doc, session); 096 BlobHolder bh = getBlobHolder(doc); 097 Blob blob = getBlob(bh); 098 blob.setFilename(name); 099 bh.setBlob(blob); 100 updateDocTitleIfNeeded(doc, name); 101 doc = session.saveDocument(doc); 102 session.save(); 103 /* Update FileSystemItem attributes */ 104 this.name = name; 105 updateDownloadURL(); 106 updateLastModificationDate(doc); 107 } 108 } 109 110 /*--------------------- FileItem -----------------*/ 111 @Override 112 public Blob getBlob() { 113 try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 114 DocumentModel doc = getDocument(session); 115 return getBlob(doc); 116 } 117 } 118 119 @Override 120 public String getDownloadURL() { 121 return downloadURL; 122 } 123 124 @Override 125 public String getDigestAlgorithm() { 126 return digestAlgorithm; 127 } 128 129 @Override 130 public String getDigest() { 131 return digest; 132 } 133 134 @Override 135 public boolean getCanUpdate() { 136 return canUpdate; 137 } 138 139 @Override 140 public void setBlob(Blob blob) { 141 try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) { 142 /* Update doc properties */ 143 DocumentModel doc = getDocument(session); 144 // Handle versioning 145 FileSystemItemHelper.versionIfNeeded(factory, doc, session); 146 // If blob's filename is empty, set it to the current name 147 String blobFileName = blob.getFilename(); 148 if (StringUtils.isEmpty(blobFileName)) { 149 blob.setFilename(name); 150 } else { 151 updateDocTitleIfNeeded(doc, blobFileName); 152 name = blobFileName; 153 updateDownloadURL(); 154 } 155 BlobHolder bh = getBlobHolder(doc); 156 bh.setBlob(blob); 157 doc = session.saveDocument(doc); 158 session.save(); 159 /* Update FileSystemItem attributes */ 160 updateLastModificationDate(doc); 161 updateDigest(getBlob(doc)); 162 } 163 } 164 165 /*--------------------- Protected -----------------*/ 166 protected final void initialize(VersioningFileSystemItemFactory factory, DocumentModel doc) { 167 this.factory = factory; 168 Blob blob = getBlob(doc); 169 name = getFileName(blob, doc.getTitle()); 170 folder = false; 171 updateDownloadURL(); 172 updateDigest(blob); 173 if (digest == null) { 174 digestAlgorithm = null; 175 } 176 canUpdate = canRename; 177 } 178 179 protected BlobHolder getBlobHolder(DocumentModel doc) { 180 BlobHolder bh = doc.getAdapter(BlobHolder.class); 181 if (bh == null) { 182 throw new NuxeoException( 183 String.format( 184 "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.", 185 doc.getId())); 186 } 187 return bh; 188 } 189 190 protected Blob getBlob(BlobHolder blobHolder) { 191 Blob blob = blobHolder.getBlob(); 192 if (blob == null) { 193 throw new NuxeoException( 194 "Document has no blob, it is not adaptable as a FileItem and therefore it cannot not be part of the items to synchronize."); 195 } 196 return blob; 197 } 198 199 protected Blob getBlob(DocumentModel doc) { 200 BlobHolder bh = getBlobHolder(doc); 201 return getBlob(bh); 202 } 203 204 protected String getFileName(Blob blob, String docTitle) { 205 String filename = blob.getFilename(); 206 return filename != null ? filename : docTitle; 207 } 208 209 protected void updateDocTitleIfNeeded(DocumentModel doc, String name) { 210 // TODO: not sure about the behavior for the doc title 211 if (this.name.equals(docTitle)) { 212 doc.setPropertyValue("dc:title", name); 213 docTitle = name; 214 } 215 } 216 217 protected void updateDownloadURL() { 218 DownloadService downloadService = Framework.getService(DownloadService.class); 219 // Remove chars that are invalid in filesystem names 220 String escapedFilename = name.replaceAll("(/|\\\\|\\*|<|>|\\?|\"|:|\\|)", "-"); 221 downloadURL = downloadService.getDownloadUrl(repositoryName, docId, DownloadService.BLOBHOLDER_0, 222 escapedFilename); 223 } 224 225 protected void updateDigest(Blob blob) { 226 String blobDigest = blob.getDigest(); 227 if (StringUtils.isEmpty(blobDigest)) { 228 // Force md5 digest algorithm and digest computation for a StringBlob, 229 // typically the note:note property of a Note document 230 digestAlgorithm = FileSystemItemHelper.MD5_DIGEST_ALGORITHM; 231 digest = FileSystemItemHelper.getMD5Digest(blob); 232 } else { 233 digestAlgorithm = blob.getDigestAlgorithm(); 234 digest = blobDigest; 235 } 236 } 237 238 protected NuxeoDriveManager getNuxeoDriveManager() { 239 return Framework.getLocalService(NuxeoDriveManager.class); 240 } 241 242 /*---------- Needed for JSON deserialization ----------*/ 243 protected void setDownloadURL(String downloadURL) { 244 this.downloadURL = downloadURL; 245 } 246 247 protected void setDigestAlgorithm(String digestAlgorithm) { 248 this.digestAlgorithm = digestAlgorithm; 249 } 250 251 protected void setDigest(String digest) { 252 this.digest = digest; 253 } 254 255 protected void setCanUpdate(boolean canUpdate) { 256 this.canUpdate = canUpdate; 257 } 258 259}