diff --git a/config/alfresco/scheduled-jobs-context.xml b/config/alfresco/scheduled-jobs-context.xml index 17e6d5bf5b..f6157c444b 100644 --- a/config/alfresco/scheduled-jobs-context.xml +++ b/config/alfresco/scheduled-jobs-context.xml @@ -233,7 +233,7 @@ - + @@ -252,38 +252,17 @@ + + + + + + ${system.usages.enabled} - - - - - - - - - - - - - - - - - - - - - ${system.usages.enabled} - - - true - - - @@ -293,8 +272,8 @@ - - + + @@ -326,8 +305,8 @@ - - + + diff --git a/config/alfresco/usage-services-context.xml b/config/alfresco/usage-services-context.xml index 6bc4f1e4b9..a233f235f3 100644 --- a/config/alfresco/usage-services-context.xml +++ b/config/alfresco/usage-services-context.xml @@ -5,15 +5,9 @@ - - - - - - - - - + + + @@ -22,6 +16,7 @@ + ${system.usages.enabled} diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java index 71ac99903a..b5854aaafd 100755 --- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java @@ -50,6 +50,8 @@ import org.alfresco.repo.node.db.DbNodeServiceImpl; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.usage.UserUsageBootstrapJob; +import org.alfresco.repo.usage.UserUsageTrackingComponent; import org.alfresco.repo.workflow.WorkflowDeployer; import org.alfresco.service.cmr.admin.RepoAdminService; import org.alfresco.service.cmr.attributes.AttributeService; @@ -870,6 +872,10 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten props.put("alfresco_user_store.guestusername", getTenantGuestUser(tenantDomain)); spacesImporterBootstrap.bootstrap(); + + // calculate any missing usages + UserUsageTrackingComponent userUsageTrackingComponent = (UserUsageTrackingComponent)getApplicationContext().getBean(UserUsageBootstrapJob.KEY_COMPONENT); + userUsageTrackingComponent.bootstrapInternal(); logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); } diff --git a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java index 939093b3fc..56e20f363b 100644 --- a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java +++ b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java @@ -33,6 +33,7 @@ import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; @@ -63,6 +64,7 @@ public class ContentUsageImpl implements ContentUsageService, private PolicyComponent policyComponent; private UsageService usageService; private AuthenticationComponent authenticationComponent; + private TenantService tenantService; private boolean enabled = true; @@ -92,6 +94,11 @@ public class ContentUsageImpl implements ContentUsageService, { this.authenticationComponent = authenticationComponent; } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } public void setEnabled(boolean enabled) { @@ -144,7 +151,7 @@ public class ContentUsageImpl implements ContentUsageService, public void onCreateNode(ChildAssociationRef childAssocRef) { NodeRef nodeRef = childAssocRef.getChildRef(); - if (stores.contains(nodeRef.getStoreRef().toString())) + if (stores.contains(tenantService.getBaseName(nodeRef.getStoreRef()).toString())) { // Get content size @@ -180,7 +187,7 @@ public class ContentUsageImpl implements ContentUsageService, Map before, Map after) { - if (stores.contains(nodeRef.getStoreRef().toString())) + if (stores.contains(tenantService.getBaseName(nodeRef.getStoreRef()).toString())) { // Check for change in content size @@ -273,7 +280,7 @@ public class ContentUsageImpl implements ContentUsageService, */ public void beforeDeleteNode(NodeRef nodeRef) { - if (stores.contains(nodeRef.getStoreRef().toString())) + if (stores.contains(tenantService.getBaseName(nodeRef.getStoreRef()).toString())) { // TODO use data dictionary to get content property ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); diff --git a/source/java/org/alfresco/repo/usage/UsageServiceImpl.java b/source/java/org/alfresco/repo/usage/UsageServiceImpl.java index 9eee352bee..e12cb44888 100644 --- a/source/java/org/alfresco/repo/usage/UsageServiceImpl.java +++ b/source/java/org/alfresco/repo/usage/UsageServiceImpl.java @@ -85,11 +85,11 @@ public class UsageServiceImpl implements UsageService { Set nodes = usageDeltaDao.getUsageDeltaNodes(); - // convert nodes to nodeRefs + // convert nodes to nodeRefs (tenant-specific) Set results = new HashSet(nodes.size()); for (Node node : nodes) { - results.add(tenantService.getBaseName(node.getNodeRef())); + results.add(node.getNodeRef()); } return results; } diff --git a/source/java/org/alfresco/repo/usage/UserUsageBootstrapJob.java b/source/java/org/alfresco/repo/usage/UserUsageBootstrapJob.java index f5488b6dd5..0a7aa529e6 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageBootstrapJob.java +++ b/source/java/org/alfresco/repo/usage/UserUsageBootstrapJob.java @@ -40,7 +40,7 @@ import org.quartz.JobExecutionException; */ public class UserUsageBootstrapJob implements Job { - private static final String KEY_COMPONENT = "userUsageBootstrapComponent"; + public static final String KEY_COMPONENT = "userUsageTrackingComponent"; public void execute(JobExecutionContext context) throws JobExecutionException { @@ -50,7 +50,7 @@ public class UserUsageBootstrapJob implements Job { throw new JobExecutionException("Missing job data: " + KEY_COMPONENT); } - // perform the content usage calculations - usageComponent.execute(); + // perform the content usage bootstrap + usageComponent.bootstrap(); } } diff --git a/source/java/org/alfresco/repo/usage/UserUsageCollapseJob.java b/source/java/org/alfresco/repo/usage/UserUsageCollapseJob.java index 7ad8edb4c5..5ecd40d6f1 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageCollapseJob.java +++ b/source/java/org/alfresco/repo/usage/UserUsageCollapseJob.java @@ -34,7 +34,7 @@ import org.quartz.JobExecutionException; */ public class UserUsageCollapseJob implements Job { - private static final String KEY_COMPONENT = "userUsageCollapseComponent"; + private static final String KEY_COMPONENT = "userUsageTrackingComponent"; public void execute(JobExecutionContext context) throws JobExecutionException { diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java index dcf60ec490..c490cb445c 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java +++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java @@ -33,6 +33,10 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.Node; import org.alfresco.repo.node.db.NodeDaoService; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.Tenant; +import org.alfresco.repo.tenant.TenantDeployerService; +import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.TransactionServiceImpl; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; @@ -58,8 +62,6 @@ public class UserUsageTrackingComponent private static boolean busy = false; - private boolean bootstrap = false; - private NodeDaoService nodeDaoService; private TransactionServiceImpl transactionService; private ContentUsageImpl contentUsageImpl; @@ -67,6 +69,8 @@ public class UserUsageTrackingComponent private PersonService personService; private NodeService nodeService; private UsageService usageService; + private TenantDeployerService tenantDeployerService; + private TenantService tenantService; private boolean enabled = true; @@ -86,11 +90,6 @@ public class UserUsageTrackingComponent this.contentUsageImpl = contentUsageImpl; } - public void setBootstrap(boolean bootstrap) - { - this.bootstrap = bootstrap; - } - public void setPersonService(PersonService personService) { this.personService = personService; @@ -106,159 +105,164 @@ public class UserUsageTrackingComponent this.usageService = usageService; } + public void setTenantDeployerService(TenantDeployerService tenantDeployerService) + { + this.tenantDeployerService = tenantDeployerService; + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + public void setEnabled(boolean enabled) { this.enabled = enabled; } - - + public void execute() { - try + if (enabled == true) { - if (! busy && ! enabled) - { - busy = true; - - // disabled - remove all usages - if (bootstrap == true) - { - if (logger.isDebugEnabled()) - { - logger.debug("Disabled - clear usages for all users ..."); - } - - RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - - // wrap to make the request in a transaction - RetryingTransactionCallback clearAllUsages = new RetryingTransactionCallback() - { - public Integer execute() throws Throwable - { - Set allPeople = personService.getAllPeople(); - - for (NodeRef personNodeRef : allPeople) - { - nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT, null); - usageService.deleteDeltas(personNodeRef); - } - return allPeople.size(); - } - }; - // execute in txn - int count = txnHelper.doInTransaction(clearAllUsages, false); - - if (logger.isDebugEnabled()) - { - logger.debug("... cleared usages for " + count + " users"); - } - } - } - else if (! busy && enabled) - { - busy = true; - - if (bootstrap == true) - { - if (logger.isDebugEnabled()) - { - logger.debug("Enabled - calculate missing usages ..."); - } - - RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - - // wrap to make the request in a transaction - RetryingTransactionCallback> getAllPeople = new RetryingTransactionCallback>() - { - public Set execute() throws Throwable - { - Set allPeople = personService.getAllPeople(); - Set userNames = new HashSet(); - - for (NodeRef personNodeRef : allPeople) - { - Long currentUsage = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT); - if (currentUsage == null) - { - String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); - userNames.add(userName); - } - } - return userNames; - } - }; - // execute in READ-ONLY txn - final Set userNames = txnHelper.doInTransaction(getAllPeople, true); - - for (String userName : userNames) - { - recalculateUsage(userName); - } - - if (logger.isDebugEnabled()) - { - logger.debug("... calculated missing usages for " + userNames.size() + " users"); - } - } - else - { - // Collapse usage deltas (if a person has initial usage set) - final RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - - // wrap to make the request in a transaction and run as System user - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public Object doWork() throws Exception - { - return txnHelper.doInTransaction(new RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Get distinct candidates - Set usageNodeRefs = usageService.getUsageDeltaNodes(); - - for(NodeRef usageNodeRef : usageNodeRefs) - { - QName nodeType = nodeService.getType(usageNodeRef); - - if (nodeType.equals(ContentModel.TYPE_PERSON)) - { - NodeRef personNodeRef = usageNodeRef; - String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); - - long currentUsage = contentUsageImpl.getUserStoredUsage(personNodeRef); - if (currentUsage != -1) - { - // collapse the usage deltas - currentUsage = contentUsageImpl.getUserUsage(userName); - usageService.deleteDeltas(personNodeRef); - contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage); - - if (logger.isDebugEnabled()) - { - logger.debug("Collapsed usage: username=" + userName + ", usage=" + currentUsage); - } - } - else - { - if (logger.isWarnEnabled()) - { - logger.warn("Initial usage for user has not yet been calculated: " + userName); - } - } - } - } - return null; - } - }); - } - }, AuthenticationUtil.getSystemUserName()); - } - } + if (! busy) + { + try + { + busy = true; + + // collapse usages - note: for MT environment, will collapse for all tenants + collapseUsages(); + } + finally + { + busy = false; + } + } } - finally + } + + // called once on startup + public void bootstrap() + { + // default domain + bootstrapInternal(); + + if (tenantService.isEnabled()) + { + List tenants = tenantDeployerService.getAllTenants(); + for (Tenant tenant : tenants) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + bootstrapInternal(); + return null; + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant.getTenantDomain())); + } + } + } + + public void bootstrapInternal() + { + if (! busy) + { + try + { + busy = true; + + if (enabled) + { + // enabled - calculate missing usages + calculateMissingUsages(); + } + else + { + // disabled - remove all usages + clearAllUsages(); + } + } + finally + { + busy = false; + } + } + } + + public void clearAllUsages() + { + if (logger.isDebugEnabled()) { - busy = false; + logger.debug("Disabled - clear usages for all users ..."); + } + + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + + // wrap to make the request in a transaction + RetryingTransactionCallback clearAllUsages = new RetryingTransactionCallback() + { + public Integer execute() throws Throwable + { + Set allPeople = personService.getAllPeople(); + + for (NodeRef personNodeRef : allPeople) + { + nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT, null); + usageService.deleteDeltas(personNodeRef); + } + return allPeople.size(); + } + }; + // execute in txn + int count = txnHelper.doInTransaction(clearAllUsages, false); + + if (logger.isDebugEnabled()) + { + logger.debug("... cleared usages for " + count + " users"); + } + } + + public void calculateMissingUsages() + { + if (logger.isDebugEnabled()) + { + logger.debug("Enabled - calculate missing usages ..."); + } + + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + + // wrap to make the request in a transaction + RetryingTransactionCallback> getAllPeople = new RetryingTransactionCallback>() + { + public Set execute() throws Throwable + { + Set allPeople = personService.getAllPeople(); + Set userNames = new HashSet(); + + for (NodeRef personNodeRef : allPeople) + { + Long currentUsage = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT); + if (currentUsage == null) + { + String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); + userNames.add(userName); + } + } + return userNames; + } + }; + // execute in READ-ONLY txn + final Set userNames = txnHelper.doInTransaction(getAllPeople, true); + + for (String userName : userNames) + { + recalculateUsage(userName); + } + + if (logger.isDebugEnabled()) + { + logger.debug("... calculated missing usages for " + userNames.size() + " users"); } } @@ -323,22 +327,81 @@ public class UserUsageTrackingComponent // execute in READ-ONLY txn final Long currentUsage = txnHelper.doInTransaction(calculatePersonCurrentUsage, true); - // wrap to make the request in a transaction and run as System user - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + // wrap to make the request in a transaction + RetryingTransactionCallback updatePersonCurrentUsage = new RetryingTransactionCallback() { - public Object doWork() throws Exception + public Object execute() throws Throwable { - return txnHelper.doInTransaction(new RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - NodeRef personNodeRef = personService.getPerson(userName); - contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage); - usageService.deleteDeltas(personNodeRef); - return null; - } - }); + NodeRef personNodeRef = personService.getPerson(userName); + contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage); + usageService.deleteDeltas(personNodeRef); + return null; } - }, AuthenticationUtil.getSystemUserName()); + }; + + // execute in txn + txnHelper.doInTransaction(updatePersonCurrentUsage, false); + } + + /** + * Collapse usages - note: for MT environment, will collapse all tenants + */ + private void collapseUsages() + { + // Collapse usage deltas (if a person has initial usage set) + final RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + + // wrap to make the request in a transaction + RetryingTransactionCallback collapseUsages = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Get distinct candidates + Set usageNodeRefs = usageService.getUsageDeltaNodes(); + + for(final NodeRef usageNodeRef : usageNodeRefs) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + QName nodeType = nodeService.getType(usageNodeRef); + + if (nodeType.equals(ContentModel.TYPE_PERSON)) + { + NodeRef personNodeRef = usageNodeRef; + String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); + + long currentUsage = contentUsageImpl.getUserStoredUsage(personNodeRef); + if (currentUsage != -1) + { + // collapse the usage deltas + currentUsage = contentUsageImpl.getUserUsage(userName); + usageService.deleteDeltas(personNodeRef); + contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage); + + if (logger.isDebugEnabled()) + { + logger.debug("Collapsed usage: username=" + userName + ", usage=" + currentUsage); + } + } + else + { + if (logger.isWarnEnabled()) + { + logger.warn("Initial usage for user has not yet been calculated: " + userName); + } + } + } + return null; + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantService.getDomain(usageNodeRef.getStoreRef().getIdentifier()))); + } + return null; + } + }; + + // execute in txn + txnHelper.doInTransaction(collapseUsages, false); } }