diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml
index efaf413c97..0103efcd5a 100644
--- a/config/alfresco/bootstrap-context.xml
+++ b/config/alfresco/bootstrap-context.xml
@@ -509,6 +509,12 @@
+
+
+
+
+
+
@@ -567,12 +573,6 @@
/${spaces.company_home.childname}
-
-
-
-
-
-
diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
index bb722d7f59..363649c94e 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
+++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
@@ -669,6 +669,20 @@
p.string_value = :propStringValue
+
+ select
+ node.id,
+ node.store.protocol,
+ node.store.identifier,
+ node.uuid
+ from
+ org.alfresco.repo.domain.hibernate.NodeImpl as node
+ where
+ node.store.protocol = :storeProtocol and
+ node.store.identifier = :storeIdentifier and
+ node.auditableProperties.auditCreator = :userName
+
+
select
node
diff --git a/source/java/org/alfresco/repo/node/db/NodeDaoService.java b/source/java/org/alfresco/repo/node/db/NodeDaoService.java
index 6cd22c97f4..534a26f62c 100644
--- a/source/java/org/alfresco/repo/node/db/NodeDaoService.java
+++ b/source/java/org/alfresco/repo/node/db/NodeDaoService.java
@@ -393,6 +393,20 @@ public interface NodeDaoService
String value,
NodePropertyHandler handler);
+ /**
+ * Iterate over all nodes that have a given creator
+ *
+ * @param storeRef the store to search in
+ * @param userName the user to match
+ * @param handler the callback to use while iterating over the URLs
+ * @return Returns the values for the given owner
+ */
+ @DirtySessionAnnotation(markDirty=true)
+ public void getNodesWithCreatorAndStore(
+ StoreRef storeRef,
+ String userName,
+ NodeRefQueryCallback resultsCallback);
+
/**
* Iterate over all property values for the given type definition. This will also dig out values that
* were persisted as type d:any.
diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
index 9f393d2394..bb7aef8f0d 100644
--- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
@@ -142,6 +142,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
private static final String QUERY_GET_TARGET_ASSOCS = "node.GetTargetAssocs";
private static final String QUERY_GET_SOURCE_ASSOCS = "node.GetSourceAssocs";
private static final String QUERY_GET_NODES_WITH_PROPERTY_VALUES_BY_STRING_AND_STORE = "node.GetNodesWithPropertyValuesByStringAndStore";
+ private static final String QUERY_GET_NODES_WITH_CREATOR_AND_STORE = "node.GetNodesWithCreatorAndStore";
private static final String QUERY_GET_NODES_WITH_PROPERTY_VALUES_BY_ACTUAL_TYPE = "node.GetNodesWithPropertyValuesByActualType";
private static final String QUERY_GET_SERVER_BY_IPADDRESS = "server.getServerByIpAddress";
@@ -2709,6 +2710,42 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
}
}
+ public void getNodesWithCreatorAndStore(
+ final StoreRef storeRef,
+ final String userName,
+ final NodeRefQueryCallback resultsCallback)
+ {
+ HibernateCallback callback = new HibernateCallback()
+ {
+ public Object doInHibernate(Session session)
+ {
+ Query query = session
+ .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODES_WITH_CREATOR_AND_STORE)
+ .setString("storeProtocol", storeRef.getProtocol())
+ .setString("storeIdentifier", storeRef.getIdentifier())
+ .setString("userName", userName)
+ ;
+ DirtySessionMethodInterceptor.setQueryFlushMode(session, query);
+ return query.scroll(ScrollMode.FORWARD_ONLY);
+ }
+ };
+ ScrollableResults queryResults = null;
+ try
+ {
+ queryResults = (ScrollableResults) getHibernateTemplate().execute(callback);
+ processNodeResults(queryResults, resultsCallback);
+ }
+ finally
+ {
+ if (queryResults != null)
+ {
+ queryResults.close();
+ }
+ }
+
+ // Done
+ }
+
public void getPropertyValuesByActualType(DataTypeDefinition actualDataTypeDefinition, NodePropertyHandler handler)
{
// get the in-database string representation of the actual type
diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java
index 34e3b4108b..86b012ce75 100644
--- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java
+++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java
@@ -25,6 +25,7 @@
package org.alfresco.repo.usage;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -32,11 +33,11 @@ import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.node.db.NodeDaoService;
import org.alfresco.repo.node.db.NodeDaoService.NodePropertyHandler;
+import org.alfresco.repo.node.db.NodeDaoService.NodeRefQueryCallback;
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.TenantAdminService;
-import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.TransactionServiceImpl;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ContentData;
@@ -47,6 +48,7 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.usage.UsageService;
import org.alfresco.service.namespace.QName;
+import org.alfresco.util.Pair;
import org.apache.commons.lang.mutable.MutableLong;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -184,63 +186,102 @@ public class UserUsageTrackingComponent
}
}
- public void clearAllUsages()
+ private void clearAllUsages()
{
- if (logger.isDebugEnabled())
+ if (logger.isInfoEnabled())
{
- logger.debug("Disabled - clear usages for all users ...");
+ logger.info("Disabled - clear non-missing user usages ...");
}
- RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
-
- // wrap to make the request in a transaction
- RetryingTransactionCallback clearAllUsages = new RetryingTransactionCallback()
+ RetryingTransactionCallback> getPersonRefs = new RetryingTransactionCallback>()
{
- public Integer execute() throws Throwable
+ public List execute() throws Throwable
{
Set allPeople = personService.getAllPeople();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Found " + allPeople.size() + " people");
+ }
+
+ List personRefsToClear = new ArrayList();
+
for (NodeRef personNodeRef : allPeople)
{
- nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT, null);
- usageService.deleteDeltas(personNodeRef);
+ Long currentUsage = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT);
+ if (currentUsage != null)
+ {
+ personRefsToClear.add(personNodeRef);
+ }
}
- return allPeople.size();
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Found " + personRefsToClear.size() + " users to clear");
+ }
+
+ return personRefsToClear;
}
};
- // execute in txn
- int count = txnHelper.doInTransaction(clearAllUsages, false);
-
- if (logger.isDebugEnabled())
+
+ // execute in READ-ONLY txn
+ List personRefsToClear = transactionService.getRetryingTransactionHelper().doInTransaction(getPersonRefs, true);
+
+ for (NodeRef personNodeRef : personRefsToClear)
{
- logger.debug("... cleared usages for " + count + " users");
+ clearUsage(personNodeRef);
+ }
+
+ if (logger.isInfoEnabled())
+ {
+ logger.info("... cleared non-missing usages for " + personRefsToClear.size() + " users");
}
}
- public void calculateMissingUsages()
+ private void clearUsage(final NodeRef personNodeRef)
{
- if (logger.isDebugEnabled())
+ RetryingTransactionCallback clearPersonUsage = new RetryingTransactionCallback()
{
- logger.debug("Enabled - calculate missing usages ...");
- }
-
- RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
+ public Integer execute() throws Throwable
+ {
+ nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT, null);
+ usageService.deleteDeltas(personNodeRef);
+
+ if (logger.isTraceEnabled())
+ {
+ logger.trace("Cleared usage for person ("+ personNodeRef+")");
+ }
+
+ return null;
+ }
+ };
+
+ // execute in READ-WRITE txn
+ transactionService.getRetryingTransactionHelper().doInTransaction(clearPersonUsage, false);
+ }
+
+ private void calculateMissingUsages()
+ {
+ if (logger.isInfoEnabled())
+ {
+ logger.info("Enabled - calculate missing user usages ...");
+ }
- // wrap to make the request in a transaction
RetryingTransactionCallback> getAllPeople = new RetryingTransactionCallback>()
{
public Set execute() throws Throwable
{
Set allPeople = personService.getAllPeople();
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Found " + allPeople.size() + " people");
+ }
+
Set userNames = new HashSet();
for (NodeRef personNodeRef : allPeople)
{
- // Cater for Lucene indexes being stale
- if (!nodeService.exists(personNodeRef))
- {
- continue;
- }
Long currentUsage = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT);
if (currentUsage == null)
{
@@ -248,11 +289,18 @@ public class UserUsageTrackingComponent
userNames.add(userName);
}
}
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Found " + userNames.size() + " users to recalculate");
+ }
+
return userNames;
}
};
+
// execute in READ-ONLY txn
- final Set userNames = txnHelper.doInTransaction(getAllPeople, true);
+ Set userNames = transactionService.getRetryingTransactionHelper().doInTransaction(getAllPeople, true);
for (final String userName : userNames)
{
@@ -266,9 +314,9 @@ public class UserUsageTrackingComponent
}, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantAdminService.getUserDomain(userName)));
}
- if (logger.isDebugEnabled())
+ if (logger.isInfoEnabled())
{
- logger.debug("... calculated missing usages for " + userNames.size() + " users");
+ logger.info("... calculated missing usages for " + userNames.size() + " users");
}
}
@@ -279,18 +327,12 @@ public class UserUsageTrackingComponent
*
* @param username the username to for which calcualte usages
*/
- public void recalculateUsage(final String username)
- {
- final RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
-
- // wrap to make the request in a transaction
+ private void recalculateUsage(final String username)
+ {
RetryingTransactionCallback calculatePersonCurrentUsage = new RetryingTransactionCallback()
{
public Long execute() throws Throwable
{
-// QNameEntity ownerQnameEntity = qnameDAO.getQNameEntity(ContentModel.PROP_OWNER);
-// QNameEntity contentQnameEntity = qnameDAO.getQNameEntity(ContentModel.PROP_CONTENT);
-//
List stores = contentUsageImpl.getStores();
final MutableLong totalUsage = new MutableLong(0L);
@@ -298,11 +340,11 @@ public class UserUsageTrackingComponent
{
final StoreRef storeRef = new StoreRef(store);
- if (logger.isDebugEnabled())
+ if (logger.isTraceEnabled())
{
- logger.debug("Recalc usage (" + username + ") store=" + storeRef);
+ logger.trace("Recalc usage (" + username + ") store=" + storeRef);
}
-
+
NodePropertyHandler propOwnerHandler = new NodePropertyHandler()
{
public void handle(NodeRef nodeRef, QName nodeTypeQName, QName propertyQName, Serializable value)
@@ -323,25 +365,19 @@ public class UserUsageTrackingComponent
};
nodeDaoService.getPropertyValuesByPropertyAndValue(storeRef, ContentModel.PROP_OWNER, username, propOwnerHandler);
- if (logger.isDebugEnabled())
+ // get nodes for which user is creator (ignore those with owner)
+ NodeRefQueryCallback nodeCreatorHandler = new NodeRefQueryCallback()
{
- logger.debug("Recalc usage (" + username + ") store=" + storeRef);
- }
-
- NodePropertyHandler propCreatorHandler = new NodePropertyHandler()
- {
- public void handle(NodeRef nodeRef, QName nodeTypeQName, QName propertyQName, Serializable value)
+ public boolean handle(Pair nodePair)
{
- if (!nodeTypeQName.equals(ContentModel.TYPE_CONTENT))
- {
- // It is not content
- return;
- }
+ NodeRef nodeRef = nodePair.getSecond();
+
if (nodeService.getProperty(nodeRef, ContentModel.PROP_OWNER) != null)
{
- // There is an owner property so we will have process this already
- return;
+ // There is an owner property so we will have processed this already
+ return true; // continue to next node (more required)
}
+
ContentData contentData = DefaultTypeConverter.INSTANCE.convert(
ContentData.class,
nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT));
@@ -350,42 +386,38 @@ public class UserUsageTrackingComponent
long currentTotalUsage = totalUsage.longValue();
totalUsage.setValue(currentTotalUsage + contentData.getSize());
}
+ return true; // continue to next node (more required)
}
};
- nodeDaoService.getPropertyValuesByPropertyAndValue(storeRef, ContentModel.PROP_OWNER, username, propCreatorHandler);
+ nodeDaoService.getNodesWithCreatorAndStore(storeRef, username, nodeCreatorHandler);
}
- if (logger.isDebugEnabled())
+ if (logger.isTraceEnabled())
{
long quotaSize = contentUsageImpl.getUserQuota(username);
- logger.debug("Recalc usage ("+ username+") totalUsage="+totalUsage+", quota="+quotaSize);
+ logger.trace("Recalc usage ("+ username+") totalUsage="+totalUsage+", quota="+quotaSize);
}
return totalUsage.longValue();
}
};
- // execute in READ-ONLY txn
- final Long currentUsage = txnHelper.doInTransaction(calculatePersonCurrentUsage, true);
- // wrap to make the request in a transaction
+ // execute in READ-ONLY txn
+ final Long currentUsage = transactionService.getRetryingTransactionHelper().doInTransaction(calculatePersonCurrentUsage, true);
+
RetryingTransactionCallback