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