User content usages & quotas - initial check-in

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@7453 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jan Vonka
2007-11-27 18:44:24 +00:00
parent 0b19ebb616
commit 7f8a4f0d5b
30 changed files with 1894 additions and 10 deletions

View File

@@ -1482,8 +1482,15 @@ public class NTProtocolHandler extends CoreProtocolHandler
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
return;
}
catch (DiskFullException ex)
{
m_sess.sendErrorResponseSMB(SMBStatus.NTDiskFull, SMBStatus.HRDWriteFault, SMBStatus.ErrHrd);
return;
}
catch (java.io.IOException ex)
{
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_FILE))
logger.debug("IOException ignore: "+ex);
}
// Remove the file from the connections list of open files

View File

@@ -35,6 +35,7 @@ import java.nio.charset.Charset;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.server.filesys.AccessDeniedException;
import org.alfresco.filesys.server.filesys.DiskFullException;
import org.alfresco.filesys.server.filesys.FileAttribute;
import org.alfresco.filesys.server.filesys.FileInfo;
import org.alfresco.filesys.server.filesys.FileOpenParams;
@@ -52,6 +53,7 @@ import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.usage.ContentQuotaException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -382,7 +384,14 @@ public class ContentNetworkFile extends NodeRefNetworkFile
// Update node properties
ContentData contentData = content.getContentData();
nodeService.setProperty( getNodeRef(), ContentModel.PROP_CONTENT, contentData);
try
{
nodeService.setProperty( getNodeRef(), ContentModel.PROP_CONTENT, contentData);
}
catch (ContentQuotaException qe)
{
throw new DiskFullException(qe.getMessage());
}
}
else
{

View File

@@ -168,6 +168,9 @@ public interface ContentModel
static final QName PROP_PRESENCEPROVIDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "presenceProvider");
static final QName PROP_PRESENCEUSERNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "presenceUsername");
static final QName PROP_SIZE_CURRENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "sizeCurrent");
static final QName PROP_SIZE_QUOTA = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "sizeQuota");
// Ownable aspect
static final QName ASPECT_OWNABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "ownable");
static final QName PROP_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "owner");

View File

@@ -59,6 +59,7 @@ import org.alfresco.service.cmr.repository.NoTransformerException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.usage.ContentQuotaException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
@@ -567,6 +568,10 @@ public class RoutingContentService implements ContentService
" value: " + contentData);
}
}
catch (ContentQuotaException qe)
{
throw qe;
}
catch (Throwable e)
{
throw new ContentIOException("Failed to set content property on stream closure: \n" +

View File

@@ -500,6 +500,19 @@
where
node.store.key.protocol = :protocol and
node.store.key.identifier = :identifier
</query>
</query>
<query name="node.GetNodesWithPropertyStringValueForStore">
select
node
from
org.alfresco.repo.domain.hibernate.NodeImpl as node
join node.properties prop
where
index(prop) = :propQName and
prop.stringValue = :propStringValue and
node.store.key.protocol = :protocol and
node.store.key.identifier = :identifier
</query>
</hibernate-mapping>

View File

@@ -0,0 +1,79 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
'-//Hibernate/Hibernate Mapping DTD 3.0//EN'
'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<!-- Hibernate mapping for storing usage delta information -->
<!-- -->
<!-- This generates a schema for content usage deltas -->
<!-- which can be used to keep track of current usages until a -->
<!-- background job has rolled up and stored against -->
<!-- the actual entity (e.g. person, folder). -->
<!-- -->
<hibernate-mapping>
<!-- The Usage Delta -->
<class name="org.alfresco.repo.usage.hibernate.UsageDeltaImpl"
proxy="org.alfresco.repo.usage.UsageDelta"
table="alf_usage_delta"
dynamic-update="false"
dynamic-insert="false"
select-before-update="false"
lazy="true"
optimistic-lock="version">
<!-- An auto-generated id -->
<id name="id" column="id" type="long">
<generator class="native"/>
</id>
<!-- Optimistic locking -->
<version column="version" name="version" type="long" />
<!-- forward assoc to node -->
<many-to-one
name="node"
not-null="true"
lazy="proxy"
class="org.alfresco.repo.domain.hibernate.NodeImpl"
fetch="select" >
<column name="node_id" />
</many-to-one>
<property name="deltaSize" column="delta_size" type="long" not-null="true"/>
</class>
<!-- Get total usage delta for a node -->
<query name="usage.GetTotalDeltaSize">
select
sum(deltaSize)
from
org.alfresco.repo.usage.hibernate.UsageDeltaImpl as usage_delta
where
usage_delta.node = :node
</query>
<!-- Get nodes with usage deltas -->
<query name="usage.GetUsageDeltaNodes">
select
distinct usage_delta.node
from
org.alfresco.repo.usage.hibernate.UsageDeltaImpl as usage_delta
</query>
<!-- Get usage deltas for a node -->
<query name="usage.GetDeltas">
select
usage_delta
from
org.alfresco.repo.usage.hibernate.UsageDeltaImpl as usage_delta
where
usage_delta.node = :node
</query>
</hibernate-mapping>

View File

@@ -293,6 +293,8 @@ public interface NodeDaoService
*/
public int getNodeCount(final StoreRef storeRef);
public Collection<Node> getNodesWithPropertyStringValueForStore(final StoreRef storeRef, final QName propQName, final String propStringValue);
public Transaction getTxnById(long txnId);
/**
* Get all transactions in a given time range. Since time-based retrieval doesn't guarantee uniqueness

View File

@@ -60,6 +60,7 @@ import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionAwareSingleton;
import org.alfresco.repo.transaction.TransactionalDao;
import org.alfresco.repo.usage.UsageDeltaDAO;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
import org.alfresco.service.cmr.repository.AssociationExistsException;
@@ -113,6 +114,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
private static final String QUERY_GET_CHILD_ASSOCS_FOR_STORE = "node.GetChildAssocsForStore";
private static final String QUERY_GET_NODES_EXCEPT_ROOT_FOR_STORE = "node.GetNodesExceptRootForStore";
private static final String QUERY_NODES_WITH_PROPERTY_STRING_VALUE_FOR_STORE = "node.GetNodesWithPropertyStringValueForStore";
private static Log logger = LogFactory.getLog(HibernateNodeDaoServiceImpl.class);
/** Log to trace parent association caching: <b>classname + .ParentAssocsCache</b> */
private static Log loggerParentAssocsCache = LogFactory.getLog(HibernateNodeDaoServiceImpl.class.getName() + ".ParentAssocsCache");
@@ -131,7 +134,14 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
/** used for debugging */
private Set<String> changeTxnIdSet;
TenantService tenantService;
private UsageDeltaDAO usageDeltaDao;
private TenantService tenantService;
public void setUsageDeltaDao(UsageDeltaDAO usageDeltaDao)
{
this.usageDeltaDao = usageDeltaDao;
}
public void setTenantService(TenantService tenantService)
{
@@ -616,6 +626,13 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
{
getHibernateTemplate().delete(assoc);
}
if (isDebugEnabled)
{
logger.debug("Deleting usage deltas of node (if any)" + node.getId());
}
usageDeltaDao.deleteDeltas(node);
// update the node status
NodeRef nodeRef = node.getNodeRef();
NodeStatus nodeStatus = getNodeStatus(nodeRef, true);
@@ -1427,6 +1444,29 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
// done
return count.intValue();
}
@SuppressWarnings("unchecked")
public Collection<Node> getNodesWithPropertyStringValueForStore(final StoreRef storeRef, final QName propQName, final String propStringValue)
{
HibernateCallback callback = new HibernateCallback()
{
public Object doInHibernate(Session session)
{
Query query = session.getNamedQuery(QUERY_NODES_WITH_PROPERTY_STRING_VALUE_FOR_STORE);
query.setString("protocol", storeRef.getProtocol())
.setString("identifier", storeRef.getIdentifier())
.setParameter("propQName", propQName)
.setString("propStringValue", propStringValue)
.setReadOnly(true);
return query.list();
}
};
List<Node> queryResults = (List<Node>) getHibernateTemplate().execute(callback);
return queryResults;
}
/*
* Queries for transactions

View File

@@ -607,6 +607,10 @@ public class PersonServiceImpl implements PersonService,
properties.put(ContentModel.PROP_EMAIL, "");
properties.put(ContentModel.PROP_ORGID, "");
properties.put(ContentModel.PROP_HOME_FOLDER_PROVIDER, defaultHomeFolderProvider);
properties.put(ContentModel.PROP_SIZE_CURRENT, 0L);
properties.put(ContentModel.PROP_SIZE_QUOTA, -1L); // no quota
return properties;
}
@@ -615,6 +619,9 @@ public class PersonServiceImpl implements PersonService,
String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties
.get(ContentModel.PROP_USERNAME));
properties.put(ContentModel.PROP_USERNAME, userName);
properties.put(ContentModel.PROP_SIZE_CURRENT, 0L);
return nodeService.createNode(getPeopleContainer(), ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_PERSON,
ContentModel.TYPE_PERSON, properties).getChildRef();
}

View File

@@ -0,0 +1,412 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage;
import java.io.Serializable;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.usage.ContentUsageService;
import org.alfresco.service.cmr.usage.UsageService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Implements Content Usage service and policies/behaviour.
*
*/
public class ContentUsageImpl implements ContentUsageService,
NodeServicePolicies.OnCreateNodePolicy,
NodeServicePolicies.OnUpdatePropertiesPolicy,
NodeServicePolicies.BeforeDeleteNodePolicy
{
// Logger
private static Log logger = LogFactory.getLog(ContentUsageImpl.class);
private NodeService nodeService;
private PersonService personService;
private PolicyComponent policyComponent;
private UsageService usageService;
private boolean enabled = true;
public static final StoreRef SPACES_STOREREF = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
public void setUsageService(UsageService usageService)
{
this.usageService = usageService;
}
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
/**
* The initialise method
*/
public void init()
{
if (enabled)
{
// Register interest in the onCreateNode policy
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
ContentModel.TYPE_CONTENT,
new JavaBehaviour(this, "onCreateNode"));
// Register interest in the onUpdateProperties policy
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
ContentModel.TYPE_CONTENT,
new JavaBehaviour(this, "onUpdateProperties"));
// Register interest in the beforeDeleteNode policy
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
ContentModel.TYPE_CONTENT,
new JavaBehaviour(this, "beforeDeleteNode"));
}
}
/**
* Called when a new node has been created.
*
* @param childAssocRef the created child association reference
*/
public void onCreateNode(ChildAssociationRef childAssocRef)
{
NodeRef nodeRef = childAssocRef.getChildRef();
if (nodeRef.getStoreRef().equals(SPACES_STOREREF))
{
// Get content size
// TODO use data dictionary to get content property
ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
Long contentSize = (contentData == null ? null : contentData.getSize());
// Get owner/creator
String owner = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_OWNER);
if (owner == null)
{
owner = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_CREATOR);
}
if (contentSize != null && contentSize != 0 && owner != null)
{
// new node with non-empty content size
if (logger.isDebugEnabled()) logger.debug("onCreateNode: contentSize="+contentSize+", nodeRef="+nodeRef+", ownerAfter="+owner);
incrementUserUsage(owner, contentSize, nodeRef);
}
}
}
/**
* Called after a node's properties have been changed.
*
* @param nodeRef reference to the updated node
* @param before the node's properties before the change
* @param after the node's properties after the change
*/
public void onUpdateProperties(
NodeRef nodeRef,
Map<QName, Serializable> before,
Map<QName, Serializable> after)
{
if (nodeRef.getStoreRef().equals(SPACES_STOREREF))
{
// Check for change in content size
// TODO use data dictionary to get content property
ContentData contentDataBefore = (ContentData)before.get(ContentModel.PROP_CONTENT);
Long contentSizeBefore = (contentDataBefore == null ? null : contentDataBefore.getSize());
ContentData contentDataAfter = (ContentData)after.get(ContentModel.PROP_CONTENT);
Long contentSizeAfter = (contentDataAfter == null ? null : contentDataAfter.getSize());
// Check for change in owner/creator
String ownerBefore = (String)before.get(ContentModel.PROP_OWNER);
if (ownerBefore == null)
{
ownerBefore = (String)before.get(ContentModel.PROP_CREATOR);
}
String ownerAfter = (String)after.get(ContentModel.PROP_OWNER);
if (ownerAfter == null)
{
ownerAfter = (String)after.get(ContentModel.PROP_CREATOR);
}
// check change in size (and possibly owner)
if (contentSizeBefore == null && contentSizeAfter != null && contentSizeAfter != 0 && ownerAfter != null)
{
// new size has been added - note: ownerBefore does not matter since the contentSizeBefore is null
if (logger.isDebugEnabled()) logger.debug("onUpdateProperties: updateSize (null -> "+contentSizeAfter+"): nodeRef="+nodeRef+", ownerAfter="+ownerAfter);
incrementUserUsage(ownerAfter, contentSizeAfter, nodeRef);
}
else if (contentSizeAfter == null && contentSizeBefore != null && contentSizeBefore != 0 && ownerBefore != null)
{
// old size has been removed - note: ownerAfter does not matter since contentSizeAfter is null
if (logger.isDebugEnabled()) logger.debug("onUpdateProperties: updateSize ("+contentSizeBefore+" -> null): nodeRef="+nodeRef+", ownerBefore="+ownerBefore);
decrementUserUsage(ownerBefore, contentSizeBefore, nodeRef);
}
else if (contentSizeBefore != null && contentSizeAfter != null)
{
if (contentSizeBefore.equals(contentSizeAfter) == false)
{
// size has changed (and possibly owner)
if (logger.isDebugEnabled()) logger.debug("onUpdateProperties: updateSize ("+contentSizeBefore+" -> "+contentSizeAfter+"): nodeRef="+nodeRef+", ownerBefore="+ownerBefore+", ownerAfter="+ownerAfter);
if (contentSizeBefore != 0 && ownerBefore != null)
{
decrementUserUsage(ownerBefore, contentSizeBefore, nodeRef);
}
if (contentSizeAfter != 0 && ownerAfter != null)
{
incrementUserUsage(ownerAfter, contentSizeAfter, nodeRef);
}
}
else
{
// same size - check change in owner only
if (ownerBefore == null && ownerAfter != null && contentSizeAfter != 0 && ownerAfter != null)
{
// new owner has been added
if (logger.isDebugEnabled()) logger.debug("onUpdateProperties: updateOwner (null -> "+ownerAfter+"): nodeRef="+nodeRef+", contentSize="+contentSizeAfter);
incrementUserUsage(ownerAfter, contentSizeAfter, nodeRef);
}
else if (ownerAfter == null && ownerBefore != null && contentSizeBefore != 0)
{
// old owner has been removed
if (logger.isDebugEnabled()) logger.debug("onUpdateProperties: updateOwner ("+ownerBefore+" -> null): nodeRef="+nodeRef+", contentSize="+contentSizeBefore);
decrementUserUsage(ownerBefore, contentSizeBefore, nodeRef);
}
else if (ownerBefore != null && ownerAfter != null && ownerBefore.equals(ownerAfter) == false)
{
// owner has changed (size has not)
if (logger.isDebugEnabled()) logger.debug("onUpdateProperties: updateOwner ("+ownerBefore+" -> "+ownerAfter+"): nodeRef="+nodeRef+", contentSize="+contentSizeBefore);
if (contentSizeBefore != 0)
{
decrementUserUsage(ownerBefore, contentSizeBefore, nodeRef);
}
if (contentSizeAfter != 0)
{
incrementUserUsage(ownerAfter, contentSizeAfter, nodeRef);
}
}
}
}
}
}
/**
* Called before a node is deleted.
*
* @param nodeRef the node reference
*/
public void beforeDeleteNode(NodeRef nodeRef)
{
if (nodeRef.getStoreRef().equals(SPACES_STOREREF))
{
// TODO use data dictionary to get content property
ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
if (contentData != null)
{
long contentSize = contentData.getSize();
String owner = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_OWNER);
if (contentSize != 0 && owner != null)
{
// decrement usage if node is being deleted
if (logger.isDebugEnabled()) logger.debug("beforeDeleteNode: nodeRef="+nodeRef+", owner="+owner+", contentSize="+contentSize);
decrementUserUsage(owner, contentSize, nodeRef);
}
}
}
}
private void incrementUserUsage(String userName, long contentSize, NodeRef contentNodeRef)
{
// increment usage - add positive delta
if (logger.isDebugEnabled()) logger.debug("incrementUserUsage: username="+userName+", contentSize="+contentSize+", contentNodeRef="+contentNodeRef);
long currentSize = getUserUsage(userName);
long quotaSize = getUserQuota(userName);
long newSize = currentSize + contentSize;
// check whether user's quota exceeded
if ((quotaSize != -1) && (newSize > quotaSize))
{
if (logger.isWarnEnabled())
{
logger.warn("User (" + userName + ") quota exceeded: content=" + contentSize +
", usage=" + currentSize +
", quota=" + quotaSize);
}
}
NodeRef personNodeRef = personService.getPerson(userName);
usageService.insertDelta(personNodeRef, contentSize);
}
private void decrementUserUsage(String userName, long contentSize, NodeRef contentNodeRef)
{
// decrement usage - add negative delta
if (logger.isDebugEnabled()) logger.debug("decrementUserUsage: username="+userName+", contentSize="+contentSize+", contentNodeRef="+contentNodeRef);
long currentSize = getUserUsage(userName);
long newSize = currentSize + contentSize;
if (newSize < 0)
{
if (logger.isDebugEnabled())
{
logger.debug("User (" + userName + ") has negative usage (" + newSize + ") - reset to 0");
}
}
NodeRef personNodeRef = personService.getPerson(userName);
usageService.insertDelta(personNodeRef, (-contentSize));
}
/**
* Set user's usage. Should only be called by background (collapse) job !
*
* @param userName
* @param currentUsage
*/
public void setUserStoredUsage(NodeRef personNodeRef, long currentUsage)
{
if (personNodeRef != null)
{
nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT, new Long(currentUsage));
}
}
public long getUserStoredUsage(NodeRef personNodeRef)
{
Long currentUsage = null;
if (personNodeRef != null)
{
currentUsage = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT);
}
return (currentUsage == null ? -1 : currentUsage);
}
public long getUserUsage(String userName)
{
long currentUsage = -1;
NodeRef personNodeRef = personService.getPerson(userName);
if (personNodeRef != null)
{
currentUsage = getUserStoredUsage(personNodeRef);
}
if (currentUsage != -1)
{
// add any deltas
currentUsage = currentUsage + usageService.getTotalDeltaSize(personNodeRef);
if (currentUsage < 0)
{
if (logger.isWarnEnabled())
{
logger.warn("User usage ("+ userName+") is negative ("+currentUsage+") overriding to 0");
}
currentUsage = 0;
}
}
return currentUsage;
}
/**
* Set user's current quota.
* Usually called by Web Client (Admin Console) if admin is changing/setting a user's quota.
*
* @param userName
* @param currentQuota
*/
public void setUserQuota(String userName, long currentQuota)
{
NodeRef personNodeRef = personService.getPerson(userName);
if (personNodeRef != null)
{
nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_QUOTA, new Long(currentQuota));
}
}
public long getUserQuota(String userName)
{
Long currentQuota = null;
NodeRef personNodeRef = personService.getPerson(userName);
if (personNodeRef != null)
{
currentQuota = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_QUOTA);
}
return (currentQuota == null ? -1 : currentQuota);
}
public boolean getEnabled()
{
return enabled;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage;
import org.alfresco.repo.domain.Node;
/**
* Interface for persistent <b>usage delta</b> objects.
*
*/
public interface UsageDelta
{
public Node getNode();
public void setNode(Node node);
public long getDeltaSize();
public void setDeltaSize(long usageDeltaSize);
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage;
import java.util.Set;
import org.alfresco.repo.domain.Node;
/**
* The interface to persist usage delta information.
*
*/
public interface UsageDeltaDAO
{
/**
* Create a usage delta entry.
*
* @param deltaInfo
*/
public void insertDelta(UsageDelta deltaInfo);
/**
* Get the total delta size for a node.
*
* @param node
* @return sum of delta sizes (in bytes) - can be +ve or -ve
*/
public long getTotalDeltaSize(Node node);
public Set<Node> getUsageDeltaNodes();
public int deleteDeltas(Node node);
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.repo.domain.Node;
import org.alfresco.repo.node.db.NodeDaoService;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.usage.hibernate.UsageDeltaImpl;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.usage.UsageService;
import org.alfresco.util.ParameterCheck;
/**
* The implementation of the UsageService for tracking usages.
*
*/
public class UsageServiceImpl implements UsageService
{
private UsageDeltaDAO usageDeltaDao;
private NodeDaoService nodeDaoService;
private TenantService tenantService;
//private static Log logger = LogFactory.getLog(UsageServiceImpl.class);
public void setUsageDeltaDao(UsageDeltaDAO usageDeltaDao)
{
this.usageDeltaDao = usageDeltaDao;
}
public void setNodeDaoService(NodeDaoService nodeDaoService)
{
this.nodeDaoService = nodeDaoService;
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public void insertDelta(NodeRef usageNodeRef, long deltaSize)
{
UsageDelta delta = new UsageDeltaImpl();
// delta properties
delta.setNode(getNodeNotNull(usageNodeRef));
delta.setDeltaSize(deltaSize);
usageDeltaDao.insertDelta(delta);
}
public long getTotalDeltaSize(NodeRef usageNodeRef)
{
return usageDeltaDao.getTotalDeltaSize(getNodeNotNull(usageNodeRef));
}
public Set<NodeRef> getUsageDeltaNodes()
{
Set<Node> nodes = usageDeltaDao.getUsageDeltaNodes();
// convert nodes to nodeRefs
Set<NodeRef> results = new HashSet<NodeRef>(nodes.size());
for (Node node : nodes)
{
results.add(tenantService.getBaseName(node.getNodeRef()));
}
return results;
}
public int deleteDeltas(NodeRef usageNodeRef)
{
return usageDeltaDao.deleteDeltas(getNodeNotNull(usageNodeRef));
}
private Node getNodeNotNull(NodeRef nodeRef) throws InvalidNodeRefException
{
ParameterCheck.mandatory("nodeRef", nodeRef);
Node unchecked = nodeDaoService.getNode(tenantService.getName(nodeRef));
if (unchecked == null)
{
throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef);
}
return unchecked;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Bootstraps user's content usage. This job is performed once at startup.
*
* If usages are enabled (as specified by 'system.usages.enabled=true' repository property) then will calculate
* usages for all users that have no current usage.
*
* If usages are disabled (as specified by 'system.usages.enabled=false' repository property) then will clear
* current usages for all users.
*/
public class UserUsageBootstrapJob implements Job
{
private static final String KEY_COMPONENT = "userUsageBootstrapComponent";
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap jobData = context.getJobDetail().getJobDataMap();
UserUsageTrackingComponent usageComponent = (UserUsageTrackingComponent) jobData.get(KEY_COMPONENT);
if (usageComponent == null)
{
throw new JobExecutionException("Missing job data: " + KEY_COMPONENT);
}
// perform the content usage calculations
usageComponent.execute();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Collapses user's content usage delta. This is performed as a regular background job.
*/
public class UserUsageCollapseJob implements Job
{
private static final String KEY_COMPONENT = "userUsageCollapseComponent";
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap jobData = context.getJobDetail().getJobDataMap();
UserUsageTrackingComponent usageComponent = (UserUsageTrackingComponent) jobData.get(KEY_COMPONENT);
if (usageComponent == null)
{
throw new JobExecutionException("Missing job data: " + KEY_COMPONENT);
}
// perform the content usage calculations
usageComponent.execute();
}
}

View File

@@ -0,0 +1,328 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.Node;
import org.alfresco.repo.node.db.NodeDaoService;
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;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.usage.UsageService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* User Usage Tracking Component - to allow user usages to be collapsed or re-calculated
*
* - used by UserUsageCollapseJob to collapse usage deltas.
* - used by UserUsageBootstrapJob to either clear all usages or (re-)calculate all missing usages.
*/
public class UserUsageTrackingComponent
{
private static Log logger = LogFactory.getLog(UserUsageTrackingComponent.class);
private static boolean busy = false;
private boolean bootstrap = false;
private NodeDaoService nodeDaoService;
private TransactionServiceImpl transactionService;
private ContentUsageImpl contentUsageImpl;
private PersonService personService;
private NodeService nodeService;
private UsageService usageService;
private boolean enabled = true;
public void setNodeDaoService(NodeDaoService nodeDaoService)
{
this.nodeDaoService = nodeDaoService;
}
public void setTransactionService(TransactionServiceImpl transactionService)
{
this.transactionService = transactionService;
}
public void setContentUsageImpl(ContentUsageImpl contentUsageImpl)
{
this.contentUsageImpl = contentUsageImpl;
}
public void setBootstrap(boolean bootstrap)
{
this.bootstrap = bootstrap;
}
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setUsageService(UsageService usageService)
{
this.usageService = usageService;
}
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
}
public void execute()
{
try
{
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<Integer> clearAllUsages = new RetryingTransactionCallback<Integer>()
{
public Integer execute() throws Throwable
{
Set<NodeRef> 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 usage for " + count + " users");
}
}
}
else if (! busy && enabled)
{
busy = true;
if (bootstrap == true)
{
if (logger.isDebugEnabled())
{
logger.debug("Enabled - calculate usages for all users (without usage) ...");
}
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
// wrap to make the request in a transaction
RetryingTransactionCallback<Set<String>> getAllPeople = new RetryingTransactionCallback<Set<String>>()
{
public Set<String> execute() throws Throwable
{
Set<NodeRef> allPeople = personService.getAllPeople();
Set<String> userNames = new HashSet<String>();
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<String> userNames = txnHelper.doInTransaction(getAllPeople, true);
for (String userName : userNames)
{
recalculateUsage(userName);
}
if (logger.isDebugEnabled())
{
logger.debug("... calculated usage for " + userNames.size() + " users");
}
}
else
{
// Collapse usage deltas (if a person has initial usage set)
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
// wrap to make the request in a transaction
RetryingTransactionCallback<Object> collapseUsages = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
// Get distinct candidates
Set<NodeRef> 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;
}
};
txnHelper.doInTransaction(collapseUsages, false);
}
}
}
finally
{
busy = false;
}
}
/**
* Recalculate content usage for given user. Required if upgrading an existing Alfresco, for users that
* have not had their initial usage calculated. In a future release, could also be called explicitly by
* a SysAdmin, eg. via a JMX operation.
*
* @param userName
*/
public void recalculateUsage(final String userName)
{
final StoreRef storeRef = ContentUsageImpl.SPACES_STOREREF;
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
// wrap to make the request in a transaction
RetryingTransactionCallback<Long> calculatePersonCurrentUsage = new RetryingTransactionCallback<Long>()
{
public Long execute() throws Throwable
{
// get nodes for which user is owner
Collection<Node> ownerNodes = nodeDaoService.getNodesWithPropertyStringValueForStore(storeRef, ContentModel.PROP_OWNER, userName);
long totalUsage = 0;
for (Node ownerNode : ownerNodes)
{
if (ownerNode.getTypeQName().equals(ContentModel.TYPE_CONTENT))
{
ContentData contentData = ContentData.createContentProperty(ownerNode.getProperties().get(ContentModel.PROP_CONTENT).getStringValue());
totalUsage = totalUsage + contentData.getSize();
}
}
// get nodes for which user is creator, and then filter out those that have an owner
Collection<Node> creatorNodes = nodeDaoService.getNodesWithPropertyStringValueForStore(storeRef, ContentModel.PROP_CREATOR, userName);
for (Node creatorNode : creatorNodes)
{
if (creatorNode.getTypeQName().equals(ContentModel.TYPE_CONTENT) &&
creatorNode.getProperties().get(ContentModel.PROP_OWNER) == null)
{
ContentData contentData = ContentData.createContentProperty(creatorNode.getProperties().get(ContentModel.PROP_CONTENT).getStringValue());
totalUsage = totalUsage + contentData.getSize();
}
}
if (logger.isDebugEnabled())
{
long quotaSize = contentUsageImpl.getUserQuota(userName);
logger.debug("Recalc usage ("+ userName+") totalUsage="+totalUsage+", quota="+quotaSize);
}
return totalUsage;
}
};
// execute in READ-ONLY txn
final Long currentUsage = txnHelper.doInTransaction(calculatePersonCurrentUsage, true);
// wrap to make the request in a transaction
RetryingTransactionCallback<Object> setUserCurrentUsage = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
NodeRef personNodeRef = personService.getPerson(userName);
contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage);
usageService.deleteDeltas(personNodeRef);
return null;
}
};
txnHelper.doInTransaction(setUserCurrentUsage, false);
}
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage.hibernate;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.domain.Node;
import org.alfresco.repo.transaction.TransactionalDao;
import org.alfresco.repo.usage.UsageDelta;
import org.alfresco.repo.usage.UsageDeltaDAO;
import org.alfresco.util.GUID;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
/**
* Hibernate-specific implementation of the persistence-independent <b>Usage Delta</b> DAO interface
*
*/
public class HibernateUsageDeltaDAO extends HibernateDaoSupport implements UsageDeltaDAO, TransactionalDao
{
private static final String QUERY_GET_DELTAS = "usage.GetDeltas";
private static final String QUERY_GET_TOTAL_DELTA_SIZE = "usage.GetTotalDeltaSize";
private static final String QUERY_GET_USAGE_DELTA_NODES = "usage.GetUsageDeltaNodes";
/** a uuid identifying this unique instance */
private final String uuid;
/**
*
*/
public HibernateUsageDeltaDAO()
{
this.uuid = GUID.generate();
}
/**
* Checks equality by type and uuid
*/
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
else if (!(obj instanceof HibernateUsageDeltaDAO))
{
return false;
}
HibernateUsageDeltaDAO that = (HibernateUsageDeltaDAO) obj;
return this.uuid.equals(that.uuid);
}
/**
* @see #uuid
*/
public int hashCode()
{
return uuid.hashCode();
}
/**
* NO-OP
*/
public void beforeCommit()
{
}
/**
* Does this <tt>Session</tt> contain any changes which must be
* synchronized with the store?
*
* @return true => changes are pending
*/
public boolean isDirty()
{
// create a callback for the task
HibernateCallback callback = new HibernateCallback()
{
public Object doInHibernate(Session session)
{
return session.isDirty();
}
};
// execute the callback
return ((Boolean)getHibernateTemplate().execute(callback)).booleanValue();
}
/**
* Just flushes the session
*/
public void flush()
{
getSession().flush();
}
@SuppressWarnings("unchecked")
public int deleteDeltas(final Node node)
{
HibernateCallback callback = new HibernateCallback()
{
public Object doInHibernate(Session session)
{
Query query = session.getNamedQuery(QUERY_GET_DELTAS);
query.setParameter("node", node);
return query.list();
}
};
// execute
List<UsageDelta> queryResults = (List<UsageDelta>)getHibernateTemplate().execute(callback);
for (UsageDelta usageDelta : queryResults)
{
getHibernateTemplate().delete(usageDelta);
}
return queryResults.size();
}
@SuppressWarnings("unchecked")
public long getTotalDeltaSize(final Node node)
{
HibernateCallback callback = new HibernateCallback()
{
public Object doInHibernate(Session session)
{
Query query = session.getNamedQuery(QUERY_GET_TOTAL_DELTA_SIZE);
query.setParameter("node", node);
query.setReadOnly(true);
return query.uniqueResult();
}
};
// execute read-only tx
Long queryResult = (Long)getHibernateTemplate().execute(callback);
return (queryResult == null ? 0 : queryResult);
}
public void insertDelta(UsageDelta deltaInfo)
{
// Save
getSession().save(deltaInfo);
}
@SuppressWarnings("unchecked")
public Set<Node> getUsageDeltaNodes()
{
HibernateCallback callback = new HibernateCallback()
{
public Object doInHibernate(Session session)
{
Query query = session.getNamedQuery(QUERY_GET_USAGE_DELTA_NODES);
query.setReadOnly(true);
return query.list();
}
};
// execute read-only tx
List<Node> queryResults = (List<Node>)getHibernateTemplate().execute(callback);
Set<Node> results = new HashSet<Node>(queryResults.size());
for (Node node : queryResults)
{
results.add(node);
}
return results;
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.usage.hibernate;
import org.alfresco.repo.domain.Node;
import org.alfresco.repo.usage.UsageDelta;
/**
* Usage Delta Implementation
*
*/
public class UsageDeltaImpl implements UsageDelta
{
private Long id;
private Long version;
private Node node;
private long deltaSize; // +ve or -ve or 0 (in bytes)
public Long getId()
{
return id;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setId(Long id)
{
this.id = id;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public Node getNode()
{
return node;
}
public void setNode(Node node)
{
this.node = node;
}
public long getDeltaSize()
{
return deltaSize;
}
public void setDeltaSize(long deltaSize)
{
this.deltaSize = deltaSize;
}
}

View File

@@ -116,7 +116,7 @@ public class WorkflowInterpreter extends BaseInterpreter
if (!transactionService.isReadOnly())
{
interpretCommand("var bpm:package package 1");
//interpretCommand("var bpm:package package 1"); // only used for testing workflows, causes increment usage/delta
interpretCommand("var bpm:assignee person admin");
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.usage;
import org.alfresco.error.AlfrescoRuntimeException;
/**
*
*/
public class ContentQuotaException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = 1346806021547860709L;
public ContentQuotaException(String msg)
{
super(msg);
}
public ContentQuotaException(String msg, Throwable cause)
{
super(msg, cause);
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.usage;
import org.alfresco.service.Auditable;
import org.alfresco.service.PublicService;
@PublicService
public interface ContentUsageService
{
/**
* Gets user usage
*
* @return Return user's current calculated usage (in bytes)
*/
@Auditable
public long getUserUsage(String userName);
/**
* Gets user quota
*
* Note: -1 means no quota limit set
*
* @return Return user's quota (in bytes).
*/
@Auditable
public long getUserQuota(String userName);
/**
* Set user quota.
*
* Note: It is possible to set a quota that is below the current usage. At this point
* the user will be over quota until their usage is decreased.
*
* Note: -1 means no quota limit set
*
* @param User's new quota (in bytes)
*/
@Auditable
public void setUserQuota(String userName, long newQuota);
/**
* Are ContentUsages enabled (refer to 'system.usages.enabled' repository property) ?
*
* @return true if ContentUsages are enabled, otherwise false
*/
@Auditable
public boolean getEnabled();
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.usage;
import java.util.Set;
import org.alfresco.service.NotAuditable;
import org.alfresco.service.PublicService;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* The public API by which applications can create usage delta entries.
*
*/
@PublicService
public interface UsageService
{
/**
* Add a usage delta entry.
*/
@NotAuditable
public void insertDelta(NodeRef usageNodeRef, long deltaSize);
/**
* Get sum of usage delta sizes.
*/
@NotAuditable
public long getTotalDeltaSize(NodeRef usageNodeRef);
/**
* Get distinct set of usage delta nodes
*/
@NotAuditable
public Set<NodeRef> getUsageDeltaNodes();
/**
* Delete the usage delta nodes
*/
@NotAuditable
public int deleteDeltas(NodeRef usageNodeRef);
}