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