001/* 002 * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * <a href="mailto:tdelprat@nuxeo.com">Tiry</a> 016 */ 017 018package org.nuxeo.ecm.quota.size; 019 020import static org.nuxeo.ecm.core.api.LifeCycleConstants.DELETED_STATE; 021import static org.nuxeo.ecm.core.api.LifeCycleConstants.DELETE_TRANSITION; 022import static org.nuxeo.ecm.core.api.LifeCycleConstants.TRANSTION_EVENT_OPTION_TRANSITION; 023import static org.nuxeo.ecm.core.api.LifeCycleConstants.UNDELETE_TRANSITION; 024import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.ABOUT_TO_REMOVE; 025import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.ABOUT_TO_REMOVE_VERSION; 026import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BEFORE_DOC_UPDATE; 027import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDIN; 028import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDOUT; 029import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED; 030import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED_BY_COPY; 031import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_MOVED; 032import static org.nuxeo.ecm.quota.size.SizeUpdateEventContext.DOCUMENT_UPDATE_INITIAL_STATISTICS; 033 034import java.io.IOException; 035import java.io.Serializable; 036import java.util.ArrayList; 037import java.util.Collection; 038import java.util.HashSet; 039import java.util.List; 040import java.util.Map; 041import java.util.Set; 042 043import org.apache.commons.logging.Log; 044import org.apache.commons.logging.LogFactory; 045import org.nuxeo.common.collections.ScopeType; 046import org.nuxeo.ecm.core.api.Blob; 047import org.nuxeo.ecm.core.api.CoreSession; 048import org.nuxeo.ecm.core.api.DocumentModel; 049import org.nuxeo.ecm.core.api.DocumentRef; 050import org.nuxeo.ecm.core.api.IdRef; 051import org.nuxeo.ecm.core.api.IterableQueryResult; 052import org.nuxeo.ecm.core.api.event.CoreEventConstants; 053import org.nuxeo.ecm.core.api.model.Property; 054import org.nuxeo.ecm.core.event.Event; 055import org.nuxeo.ecm.core.event.EventService; 056import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 057import org.nuxeo.ecm.core.utils.BlobsExtractor; 058import org.nuxeo.ecm.quota.AbstractQuotaStatsUpdater; 059import org.nuxeo.ecm.quota.QuotaStatsInitialWork; 060import org.nuxeo.ecm.quota.QuotaUtils; 061import org.nuxeo.runtime.api.Framework; 062 063/** 064 * {@link org.nuxeo.ecm.quota.QuotaStatsUpdater} counting space used by Blobs in document. This default implementation 065 * does not track the space used by versions, or the space used by non-Blob properties 066 * 067 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a> 068 * @since 5.6 069 */ 070public class QuotaSyncListenerChecker extends AbstractQuotaStatsUpdater { 071 072 public static final String DISABLE_QUOTA_CHECK_LISTENER = "disableQuotaListener"; 073 074 private static Log log = LogFactory.getLog(QuotaSyncListenerChecker.class); 075 076 @Override 077 public void computeInitialStatistics(CoreSession unrestrictedSession, QuotaStatsInitialWork currentWorker) { 078 QuotaComputerProcessor processor = new QuotaComputerProcessor(); 079 String query = "SELECT ecm:uuid FROM Document where ecm:isCheckedInVersion=0 and ecm:isProxy=0 order by dc:created desc"; 080 IterableQueryResult res = unrestrictedSession.queryAndFetch(query, "NXQL"); 081 log.debug("Starting initial Quota computation"); 082 long total = res.size(); 083 log.debug("Start iteration on " + total + " items"); 084 try { 085 for (Map<String, Serializable> r : res) { 086 String uuid = (String) r.get("ecm:uuid"); 087 // this will force an update if the plugin was installed and 088 // then removed 089 removeFacet(unrestrictedSession, uuid); 090 } 091 } finally { 092 res.close(); 093 } 094 removeFacet(unrestrictedSession, unrestrictedSession.getRootDocument().getId()); 095 unrestrictedSession.save(); 096 try { 097 long idx = 0; 098 res = unrestrictedSession.queryAndFetch(query, "NXQL"); 099 for (Map<String, Serializable> r : res) { 100 String uuid = (String) r.get("ecm:uuid"); 101 computeSizeOnDocument(unrestrictedSession, uuid, processor); 102 currentWorker.notifyProgress(++idx, total); 103 } 104 } finally { 105 res.close(); 106 } 107 } 108 109 private void removeFacet(CoreSession unrestrictedSession, String uuid) { 110 DocumentModel target = unrestrictedSession.getDocument(new IdRef(uuid)); 111 if (target.hasFacet(QuotaAwareDocument.DOCUMENTS_SIZE_STATISTICS_FACET)) { 112 if (log.isTraceEnabled()) { 113 log.trace("doc with uuid " + uuid + " already up to date"); 114 } 115 QuotaAware quotaDoc = target.getAdapter(QuotaAware.class); 116 quotaDoc.resetInfos(true); 117 if (log.isDebugEnabled()) { 118 log.debug(target.getPathAsString() + " reset to " + quotaDoc.getQuotaInfo()); 119 } 120 target.removeFacet(QuotaAwareDocument.DOCUMENTS_SIZE_STATISTICS_FACET); 121 DocumentModel origTarget = target; 122 QuotaUtils.disableListeners(target); 123 target = unrestrictedSession.saveDocument(target); 124 QuotaUtils.clearContextData(target); 125 QuotaUtils.clearContextData(origTarget); 126 } 127 } 128 129 protected void computeSizeOnDocument(CoreSession unrestrictedSession, String uuid, QuotaComputerProcessor processor) 130 { 131 IdRef ref = new IdRef(uuid); 132 DocumentModel target = unrestrictedSession.getDocument(ref); 133 if (log.isTraceEnabled()) { 134 log.trace("process Quota initial computation on uuid " + uuid); 135 } 136 if (unrestrictedSession.exists(ref)) { 137 if (log.isTraceEnabled()) { 138 log.trace("doc with uuid " + uuid + " started update"); 139 } 140 SizeUpdateEventContext quotaCtx = updateEventToProcessNewDocument(unrestrictedSession, target); 141 quotaCtx.setProperty(SizeUpdateEventContext.SOURCE_EVENT_PROPERTY_KEY, DOCUMENT_UPDATE_INITIAL_STATISTICS); 142 quotaCtx.getProperties().put(SizeUpdateEventContext._UPDATE_TRASH_SIZE, 143 DELETED_STATE.equals(target.getCurrentLifeCycleState())); 144 processor.processQuotaComputation(quotaCtx); 145 if (log.isTraceEnabled()) { 146 log.trace("doc with uuid " + uuid + " update completed"); 147 } 148 } else { 149 if (log.isTraceEnabled()) { 150 log.trace("doc with uuid " + uuid + " does not exist"); 151 } 152 } 153 } 154 155 @Override 156 protected void handleQuotaExceeded(QuotaExceededException e, Event event) { 157 String msg = "Current event " + event.getName() + " would break Quota restriction, rolling back"; 158 log.info(msg); 159 e.addInfo(msg); 160 event.markRollBack("Quota Exceeded", e); 161 } 162 163 @Override 164 protected void processDocumentCreated(CoreSession session, DocumentModel targetDoc, DocumentEventContext docCtx) 165 { 166 167 if (targetDoc.isVersion()) { 168 // version taken into account by checkout 169 // TODO 5.7 version accounting should be different 170 return; 171 } 172 BlobSizeInfo bsi = computeSizeImpact(targetDoc, false); 173 174 // always add the quota facet 175 QuotaAware quotaDoc = targetDoc.getAdapter(QuotaAware.class); 176 if (quotaDoc == null) { 177 log.trace(" add Quota Facet on " + targetDoc.getPathAsString()); 178 QuotaAwareDocumentFactory.make(targetDoc, true); 179 } 180 // only process if blobs are present 181 if (bsi.getBlobSizeDelta() != 0) { 182 checkConstraints(session, targetDoc, targetDoc.getParentRef(), bsi); 183 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, bsi, DOCUMENT_CREATED); 184 sendUpdateEvents(asyncEventCtx); 185 } 186 } 187 188 @Override 189 protected void processDocumentCheckedIn(CoreSession session, DocumentModel doc, DocumentEventContext docCtx) 190 { 191 // on checkin the versions size is incremented (and also the total) 192 193 BlobSizeInfo bsi = computeSizeImpact(doc, false); 194 // only process if blobs are present 195 if (bsi.getBlobSize() != 0) { 196 // no checkConstraints as total size not impacted 197 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, bsi, DOCUMENT_CHECKEDIN); 198 sendUpdateEvents(asyncEventCtx); 199 } 200 } 201 202 @Override 203 protected void processDocumentCheckedOut(CoreSession session, DocumentModel doc, DocumentEventContext docCtx) 204 { 205 // on checkout we account in the total for the last version size 206 BlobSizeInfo bsi = computeSizeImpact(doc, false); 207 // only process if blobs are present 208 if (bsi.getBlobSize() != 0) { 209 checkConstraints(session, doc, doc.getParentRef(), bsi, true); 210 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, bsi, DOCUMENT_CHECKEDOUT); 211 sendUpdateEvents(asyncEventCtx); 212 } 213 } 214 215 @Override 216 protected void processDocumentUpdated(CoreSession session, DocumentModel doc, DocumentEventContext docCtx) 217 { 218 // Nothing to do ! 219 } 220 221 @Override 222 protected void processDocumentBeforeUpdate(CoreSession session, DocumentModel targetDoc, DocumentEventContext docCtx) 223 { 224 225 BlobSizeInfo bsi = computeSizeImpact(targetDoc, true); 226 log.debug("calling processDocumentBeforeUpdate, bsi=" + bsi.toString()); 227 // only process if Blobs where added or removed 228 if (bsi.getBlobSizeDelta() != 0) { 229 checkConstraints(session, targetDoc, targetDoc.getParentRef(), bsi); 230 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, bsi, BEFORE_DOC_UPDATE); 231 sendUpdateEvents(asyncEventCtx); 232 } 233 } 234 235 @Override 236 protected void processDocumentCopied(CoreSession session, DocumentModel targetDoc, DocumentEventContext docCtx) 237 { 238 QuotaAware quotaDoc = targetDoc.getAdapter(QuotaAware.class); 239 if (quotaDoc != null) { 240 long total = quotaDoc.getTotalSize() - quotaDoc.getVersionsSize() - quotaDoc.getTrashSize(); 241 BlobSizeInfo bsi = new BlobSizeInfo(); 242 bsi.blobSize = total; 243 bsi.blobSizeDelta = total; 244 if (total > 0) { 245 // check on parent since Session is not committed for now 246 checkConstraints(session, targetDoc, targetDoc.getParentRef(), bsi); 247 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, bsi, 248 DOCUMENT_CREATED_BY_COPY); 249 sendUpdateEvents(asyncEventCtx); 250 } 251 } 252 } 253 254 @Override 255 protected void processDocumentMoved(CoreSession session, DocumentModel targetDoc, DocumentModel sourceParent, 256 DocumentEventContext docCtx) { 257 258 if (docCtx.getProperties().get(CoreEventConstants.DESTINATION_REF) 259 .equals(sourceParent.getRef())) { 260 log.debug(targetDoc.getPathAsString() + "(" + targetDoc.getId() + ") - document is just being renamed, skipping"); 261 return; 262 } 263 QuotaAware quotaDoc = targetDoc.getAdapter(QuotaAware.class); 264 long total = 0; 265 if (quotaDoc != null) { 266 total = quotaDoc.getTotalSize(); 267 } 268 BlobSizeInfo bsi = new BlobSizeInfo(); 269 bsi.blobSize = total; 270 bsi.blobSizeDelta = total; 271 if (total > 0) { 272 // check on destination parent since Session is not committed for 273 // now 274 checkConstraints(session, targetDoc, targetDoc.getParentRef(), bsi); 275 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, bsi, DOCUMENT_MOVED); 276 long versSize = quotaDoc.getVersionsSize(); 277 asyncEventCtx.setVersionsSize(versSize); 278 sendUpdateEvents(asyncEventCtx); 279 280 // also need to trigger update on source tree 281 BlobSizeInfo bsiRemove = new BlobSizeInfo(); 282 bsiRemove.blobSize = total; 283 bsiRemove.blobSizeDelta = -total; 284 285 asyncEventCtx = new SizeUpdateEventContext(session, docCtx, sourceParent, bsiRemove, DOCUMENT_MOVED); 286 versSize = -quotaDoc.getVersionsSize(); 287 asyncEventCtx.setVersionsSize(versSize); 288 List<String> sourceParentUUIDs = getParentUUIDS(session, sourceParent); 289 sourceParentUUIDs.add(0, sourceParent.getId()); 290 asyncEventCtx.setParentUUIds(sourceParentUUIDs); 291 sendUpdateEvents(asyncEventCtx); 292 } 293 294 } 295 296 @Override 297 protected void processDocumentAboutToBeRemoved(CoreSession session, DocumentModel targetDoc, 298 DocumentEventContext docCtx) { 299 300 if (targetDoc.isVersion()) { 301 // for versions we need to decrement the live doc + it's parents 302 List<String> parentUUIDs = new ArrayList<String>(); 303 parentUUIDs.add(targetDoc.getSourceId()); 304 parentUUIDs.addAll(getParentUUIDS(session, new IdRef(targetDoc.getSourceId()))); 305 306 // We only have to decrement the inner size of this doc 307 QuotaAware quotaDoc = targetDoc.getAdapter(QuotaAware.class); 308 // we do not write the right quota on the version, so we need to recompute it instead of quotaDoc#getInnerSize 309 BlobSizeInfo bsi = computeSizeImpact(targetDoc, false); 310 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, bsi.getBlobSize(), 311 ABOUT_TO_REMOVE_VERSION); 312 asyncEventCtx.setParentUUIds(parentUUIDs); 313 sendUpdateEvents(asyncEventCtx); 314 return; 315 } 316 317 QuotaAware quotaDoc = targetDoc.getAdapter(QuotaAware.class); 318 long total = 0; 319 long versSize = 0; 320 if (quotaDoc == null) { 321 // the document could have been just created and the previous 322 // computation 323 // hasn't finished yet, see NXP-13665 324 BlobSizeInfo bsi = computeSizeImpact(targetDoc, false); 325 total = bsi.getBlobSize(); 326 log.debug("Document " + targetDoc.getId() + " doesn't have the facet quotaDoc. Compute impacted size:" 327 + total); 328 } 329 if (quotaDoc != null) { 330 total = quotaDoc.getTotalSize(); 331 versSize = -quotaDoc.getVersionsSize(); 332 log.debug("Found facet quotaDoc on document " + targetDoc.getId() 333 + ".Notifying QuotaComputerProcessor with total size: " + total + " and versions size: " + versSize); 334 } 335 if (total > 0) { 336 List<String> parentUUIDs = getParentUUIDS(session, targetDoc); 337 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, total, ABOUT_TO_REMOVE); 338 // remove size for all its versions from sizeVersions on parents 339 if (versSize != 0) { 340 asyncEventCtx.setVersionsSize(versSize); 341 } 342 asyncEventCtx.setParentUUIds(parentUUIDs); 343 asyncEventCtx.getProperties().put(SizeUpdateEventContext._UPDATE_TRASH_SIZE, 344 DELETED_STATE.equals(targetDoc.getCurrentLifeCycleState())); 345 sendUpdateEvents(asyncEventCtx); 346 } 347 } 348 349 @Override 350 protected boolean needToProcessEventOnDocument(Event event, DocumentModel targetDoc) { 351 352 if (targetDoc == null) { 353 return false; 354 } 355 if (targetDoc.isProxy()) { 356 log.debug("Escape from listener: not precessing proxies"); 357 return false; 358 } 359 360 Boolean block = (Boolean) targetDoc.getContextData(DISABLE_QUOTA_CHECK_LISTENER); 361 if (Boolean.TRUE.equals(block)) { 362 log.debug("Escape from listener to avoid reentrancy"); 363 // ignore the event - we are blocked by the caller 364 // used to avoid reentrancy when the async event handler 365 // do update the docs to set the new size ! 366 return false; 367 } 368 return true; 369 } 370 371 protected void sendUpdateEvents(SizeUpdateEventContext eventCtx) { 372 373 Event quotaUpdateEvent = eventCtx.newQuotaUpdateEvent(); 374 log.debug("prepared event on target tree with context " + eventCtx.toString()); 375 EventService es = Framework.getLocalService(EventService.class); 376 es.fireEvent(quotaUpdateEvent); 377 } 378 379 protected List<String> getParentUUIDS(CoreSession unrestrictedSession, final DocumentRef docRef) 380 { 381 382 final List<String> result = new ArrayList<String>(); 383 if (docRef == null || docRef.toString() == null) { 384 return result; 385 } 386 DocumentRef[] parentRefs = unrestrictedSession.getParentDocumentRefs(docRef); 387 for (DocumentRef parentRef : parentRefs) { 388 result.add(parentRef.toString()); 389 } 390 return result; 391 } 392 393 protected List<String> getParentUUIDS(CoreSession unrestrictedSession, final DocumentModel doc) 394 { 395 return getParentUUIDS(unrestrictedSession, doc.getRef()); 396 } 397 398 protected void checkConstraints(CoreSession unrestrictedSession, final DocumentModel doc, 399 final DocumentRef parentRef, final BlobSizeInfo bsi) { 400 checkConstraints(unrestrictedSession, doc, parentRef, bsi, false); 401 } 402 403 protected void checkConstraints(CoreSession unrestrictedSession, final DocumentModel doc, 404 final DocumentRef parentRef, final BlobSizeInfo bsi, final boolean checkWithTotalSize) 405 { 406 if (parentRef == null) { 407 return; 408 } 409 410 long addition = bsi.blobSizeDelta; 411 if (checkWithTotalSize) { 412 addition = bsi.getBlobSize(); 413 } 414 415 if (addition <= 0) { 416 return; 417 } 418 List<DocumentModel> parents = unrestrictedSession.getParentDocuments(parentRef); 419 DocumentModel parentDoc = unrestrictedSession.getDocument(parentRef); 420 if (!parents.contains(parentDoc)) { 421 parents.add(parentDoc); 422 } 423 for (DocumentModel parent : parents) { 424 log.debug("processing " + parent.getId() + " " + parent.getPathAsString()); 425 QuotaAware qap = parent.getAdapter(QuotaAware.class); 426 // when enabling quota on user workspaces, the max size set on 427 // the 428 // UserWorkspacesRoot is the max size set on every user workspace 429 if (qap != null && !"UserWorkspacesRoot".equals(parent.getType()) && qap.getMaxQuota() > 0) { 430 Long newTotalSize = new Long(qap.getTotalSize() + addition); 431 try { 432 if (qap.totalSizeCacheExists()) { 433 Long oldTotalSize = qap.getTotalSizeCache(); 434 if (oldTotalSize == null) { 435 newTotalSize = new Long(qap.getTotalSize() + addition); 436 log.debug("to create cache entry to create: " + qap.getDoc().getId() + " total: " + newTotalSize); 437 } else { 438 newTotalSize = new Long(oldTotalSize + addition); 439 log.debug("cache entry to update: " + qap.getDoc().getId() + " total: " + newTotalSize); 440 } 441 qap.putTotalSizeCache(newTotalSize); 442 if (log.isDebugEnabled()) { 443 log.debug("cache vs. DB: " + newTotalSize + " # " + (qap.getTotalSize() + addition)); 444 } 445 } 446 } catch (IOException e) { 447 log.error(e.getMessage() + ": unable to use cache " + QuotaAware.QUOTA_TOTALSIZE_CACHE_NAME + ", fallback to basic mechanism"); 448 newTotalSize = new Long(qap.getTotalSize() + addition); 449 } 450 if (newTotalSize > qap.getMaxQuota()) { 451 log.info("Raising Quota Exception on " + doc.getPathAsString()); 452 try { 453 qap.invalidateTotalSizeCache(); 454 } catch (IOException e) { 455 log.error(e.getMessage() + ": unable to invalidate cache " + QuotaAware.QUOTA_TOTALSIZE_CACHE_NAME + " for " + qap.getDoc().getId()); 456 } 457 throw new QuotaExceededException(parent, doc, qap.getMaxQuota()); 458 } 459 } 460 } 461 } 462 463 protected BlobSizeInfo computeSizeImpact(DocumentModel doc, boolean onlyIfBlobHasChanged) { 464 465 BlobSizeInfo result = new BlobSizeInfo(); 466 467 QuotaAware quotaDoc = doc.getAdapter(QuotaAware.class); 468 if (quotaDoc != null) { 469 result.blobSize = quotaDoc.getInnerSize(); 470 } else { 471 result.blobSize = 0; 472 } 473 474 List<Blob> blobs = getBlobs(doc, onlyIfBlobHasChanged); 475 476 // If we have no blobs, it can mean 477 if (blobs.size() == 0) { 478 if (onlyIfBlobHasChanged) { 479 // Nothing has changed 480 result.blobSizeDelta = 0; 481 } else { 482 // Or the blob have been removed 483 result.blobSizeDelta = -result.blobSize; 484 result.blobSize = 0; 485 } 486 } else { 487 // When we have blobs 488 long size = 0; 489 for (Blob blob : blobs) { 490 if (blob != null) { 491 size += blob.getLength(); 492 } 493 } 494 result.blobSizeDelta = size - result.blobSize; 495 result.blobSize = size; 496 } 497 498 return result; 499 } 500 501 /** 502 * Return the list of changed blob 503 * 504 * @param doc 505 * @param onlyIfBlobHasChanged 506 * @return 507 */ 508 protected List<Blob> getBlobs(DocumentModel doc, boolean onlyIfBlobHasChanged) { 509 QuotaSizeService sizeService = Framework.getService(QuotaSizeService.class); 510 Set<String> excludedPathSet = new HashSet<String>(sizeService.getExcludedPathList()); 511 512 BlobsExtractor extractor = new BlobsExtractor(); 513 extractor.setExtractorProperties(null, new HashSet<String>(excludedPathSet), true); 514 515 Collection<Property> blobProperties = extractor.getBlobsProperties(doc); 516 517 boolean needRecompute = !onlyIfBlobHasChanged; 518 519 if (onlyIfBlobHasChanged) { 520 for (Property blobProperty : blobProperties) { 521 if (blobProperty.isDirty()) { 522 needRecompute = true; 523 break; 524 } 525 } 526 } 527 528 List<Blob> result = new ArrayList<Blob>(); 529 if (needRecompute) { 530 for (Property blobProperty : blobProperties) { 531 Blob blob = (Blob) blobProperty.getValue(); 532 String schema = blobProperty.getParent().getSchema().getName(); 533 String propName = blobProperty.getName(); 534 535 log.debug(String.format("Using [%s:%s] for quota blob computation (size : %d)", schema, propName, 536 blob.getLength())); 537 result.add(blob); 538 } 539 } 540 return result; 541 } 542 543 @Override 544 protected void processDocumentTrashOp(CoreSession session, DocumentModel doc, DocumentEventContext docCtx) 545 { 546 String transition = (String) docCtx.getProperties().get(TRANSTION_EVENT_OPTION_TRANSITION); 547 if (transition != null && (!(DELETE_TRANSITION.equals(transition) || UNDELETE_TRANSITION.equals(transition)))) { 548 return; 549 } 550 551 QuotaAware quotaDoc = doc.getAdapter(QuotaAware.class); 552 if (quotaDoc != null) { 553 long absSize = quotaDoc.getInnerSize(); 554 if (log.isDebugEnabled()) { 555 if (quotaDoc.getDoc().isFolder()) { 556 log.debug(quotaDoc.getDoc().getPathAsString() + " is a folder, just inner size (" + absSize + ") taken into account for trash size"); 557 } 558 } 559 long total = (DELETE_TRANSITION.equals(transition) == true ? absSize : -absSize); 560 BlobSizeInfo bsi = new BlobSizeInfo(); 561 bsi.blobSize = total; 562 bsi.blobSizeDelta = total; 563 if (absSize > 0) { 564 // check constrains not needed, since the documents stays in 565 // the same folder 566 // TODO move this check to QuotaSyncListenerChecker 567 568 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, bsi, transition); 569 sendUpdateEvents(asyncEventCtx); 570 } 571 } 572 } 573 574 @Override 575 protected void processDocumentBeforeRestore(CoreSession session, DocumentModel targetDoc, 576 DocumentEventContext docCtx) { 577 QuotaAware quotaDoc = targetDoc.getAdapter(QuotaAware.class); 578 if (quotaDoc != null) { 579 long total = quotaDoc.getTotalSize(); 580 if (total > 0) { 581 List<String> parentUUIDs = getParentUUIDS(session, targetDoc); 582 SizeUpdateEventContext asyncEventCtx = new SizeUpdateEventContext(session, docCtx, total, 583 ABOUT_TO_REMOVE); 584 // remove size for all its versions from sizeVersions on parents 585 // since they will be recalculated on restore 586 long versSize = -quotaDoc.getVersionsSize(); 587 asyncEventCtx.setVersionsSize(versSize); 588 asyncEventCtx.setParentUUIds(parentUUIDs); 589 sendUpdateEvents(asyncEventCtx); 590 } 591 } 592 } 593 594 @Override 595 protected void processDocumentRestored(CoreSession session, DocumentModel targetDoc, DocumentEventContext docCtx) 596 { 597 QuotaAware quotaDoc = targetDoc.getAdapter(QuotaAware.class); 598 if (quotaDoc == null) { 599 log.debug(" add Quota Facet on " + targetDoc.getPathAsString()); 600 quotaDoc = QuotaAwareDocumentFactory.make(targetDoc, true); 601 } 602 quotaDoc.resetInfos(true); 603 sendUpdateEvents(updateEventToProcessNewDocument(session, targetDoc)); 604 } 605 606 private SizeUpdateEventContext updateEventToProcessNewDocument(CoreSession unrestrictedSession, DocumentModel target) 607 { 608 BlobSizeInfo bsi = computeSizeImpact(target, false); 609 SizeUpdateEventContext quotaCtx = null; 610 611 // process versions if any ; document is not in trash 612 List<DocumentModel> versions = unrestrictedSession.getVersions(target.getRef()); 613 if (versions.isEmpty() && !DELETED_STATE.equals(target.getCurrentLifeCycleState())) { 614 quotaCtx = new SizeUpdateEventContext(unrestrictedSession, bsi, DOCUMENT_CREATED, target); 615 616 } else { 617 long versionsSize = 0; 618 for (DocumentModel documentModel : versions) { 619 versionsSize += computeSizeImpact(documentModel, false).blobSize;; 620 } 621 622 quotaCtx = new SizeUpdateEventContext(unrestrictedSession, bsi, DOCUMENT_UPDATE_INITIAL_STATISTICS, target); 623 quotaCtx.setVersionsSize(versionsSize); 624 } 625 return quotaCtx; 626 } 627}