@@ -324,6 +331,11 @@
true
+
+
+
+
+
diff --git a/source/java/org/alfresco/filesys/repo/ContentContext.java b/source/java/org/alfresco/filesys/repo/ContentContext.java
index 7e8d9e0339..a946802437 100644
--- a/source/java/org/alfresco/filesys/repo/ContentContext.java
+++ b/source/java/org/alfresco/filesys/repo/ContentContext.java
@@ -28,6 +28,7 @@ import org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.FileSystem;
+import org.alfresco.jlan.server.filesys.quota.QuotaManagerException;
import org.alfresco.service.cmr.repository.NodeRef;
/**
@@ -260,6 +261,16 @@ public class ContentContext extends AlfrescoContext
if ( m_nodeMonitor != null)
m_nodeMonitor.shutdownRequest();
+ // Stop the quota manager, if enabled
+
+ if ( hasQuotaManager()) {
+ try {
+ getQuotaManager().stopManager(null, this);
+ }
+ catch ( QuotaManagerException ex) {
+ }
+ }
+
// Call the base class
super.CloseContext();
diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
index 1a790c0b72..475c4cde33 100644
--- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
+++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
@@ -53,11 +53,14 @@ import org.alfresco.jlan.server.filesys.FileStatus;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.server.filesys.SearchContext;
import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.server.filesys.db.DBFileInfo;
import org.alfresco.jlan.server.filesys.pseudo.MemoryNetworkFile;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFile;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFileInterface;
import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList;
import org.alfresco.jlan.server.filesys.pseudo.PseudoNetworkFile;
+import org.alfresco.jlan.server.filesys.quota.QuotaManager;
+import org.alfresco.jlan.server.filesys.quota.QuotaManagerException;
import org.alfresco.jlan.server.locking.FileLockingInterface;
import org.alfresco.jlan.server.locking.LockManager;
import org.alfresco.jlan.server.locking.OpLockInterface;
@@ -650,6 +653,22 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
}
else
logger.warn("Oplock support disabled for filesystem " + ctx.getDeviceName());
+
+ // Start the quota manager, if enabled
+
+ if ( context.hasQuotaManager()) {
+
+ try {
+
+ // Start the quota manager
+
+ context.getQuotaManager().startManager( this, context);
+ logger.info("Quota manager enabled for filesystem");
+ }
+ catch ( QuotaManagerException ex) {
+ logger.error("Failed to start quota manager", ex);
+ }
+ }
}
/**
@@ -2261,6 +2280,25 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
}
}
+ // Check if there is a quota manager enabled
+
+ long fileSize = 0L;
+
+ if ( ctx.hasQuotaManager() && file.hasDeleteOnClose()) {
+
+ // Make sure the content stream has been opened, to get the current file size
+
+ if ( file instanceof ContentNetworkFile) {
+ ContentNetworkFile contentFile = (ContentNetworkFile) file;
+ if ( contentFile.hasContent() == false)
+ contentFile.openContent( false, false);
+
+ // Save the current file size
+
+ fileSize = contentFile.getFileSize();
+ }
+ }
+
// Defer to the network file to close the stream and remove the content
file.closeFile();
@@ -2289,6 +2327,11 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
// Delete the file
fileFolderService.delete(nodeRef);
+
+ // Check if there is a quota manager enabled, release space back to the user quota
+
+ if ( ctx.hasQuotaManager())
+ ctx.getQuotaManager().releaseSpace(sess, tree, file.getFileId(), file.getFullName(), fileSize);
}
catch ( Exception ex)
{
@@ -2376,6 +2419,18 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
try
{
+ // Check if there is a quota manager enabled, if so then we need to save the current file size
+
+ QuotaManager quotaMgr = ctx.getQuotaManager();
+ FileInfo fInfo = null;
+
+ if ( quotaMgr != null) {
+
+ // Get the size of the file being deleted
+
+ fInfo = getFileInformation( sess, tree, name);
+ }
+
// Get the node
NodeRef nodeRef = getNodeForPath(tree, name);
@@ -2427,6 +2482,11 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
parentState.updateModifyDateTime();
}
}
+
+ // Release the space back to the users quota
+
+ if ( quotaMgr != null)
+ quotaMgr.releaseSpace( sess, tree, fInfo.getFileId(), name, fInfo.getSize());
}
// Debug
@@ -2951,14 +3011,75 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
*/
public void truncateFile(SrvSession sess, TreeConnection tree, NetworkFile file, long size) throws IOException
{
- // Truncate or extend the file to the required size
+ // Keep track of the allocation/release size in case the file resize fails
- file.truncateFile(size);
-
- // Debug
-
ContentContext ctx = (ContentContext) tree.getContext();
+ long allocSize = 0L;
+ long releaseSize = 0L;
+
+ // Check if there is a quota manager
+
+ QuotaManager quotaMgr = ctx.getQuotaManager();
+
+ if ( ctx.hasQuotaManager()) {
+
+ // Check if the file content has been opened, we need the content to be opened to get the
+ // current file size
+
+ if ( file instanceof ContentNetworkFile) {
+ ContentNetworkFile contentFile = (ContentNetworkFile) file;
+ if ( contentFile.hasContent() == false)
+ contentFile.openContent( false, false);
+ }
+ else
+ throw new IOException("Invalid file class type, " + file.getClass().getName());
+
+ // Determine if the new file size will release space or require space allocating
+
+ if ( size > file.getFileSize()) {
+
+ // Calculate the space to be allocated
+
+ allocSize = size - file.getFileSize();
+
+ // Allocate space to extend the file
+
+ quotaMgr.allocateSpace(sess, tree, file, allocSize);
+ }
+ else {
+
+ // Calculate the space to be released as the file is to be truncated, release the space if
+ // the file truncation is successful
+
+ releaseSize = file.getFileSize() - size;
+ }
+ }
+
+ // Set the file length
+
+ try {
+ file.truncateFile(size);
+ }
+ catch (IOException ex) {
+
+ // Check if we allocated space to the file
+
+ if ( allocSize > 0 && quotaMgr != null)
+ quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, allocSize);
+
+ // Rethrow the exception
+
+ throw ex;
+ }
+
+ // Check if space has been released by the file resizing
+
+ if ( releaseSize > 0 && quotaMgr != null)
+ quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, releaseSize);
+
+ // Debug
+
if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILEIO))
logger.debug("Truncated file: network file=" + file + " size=" + size);
}
@@ -3071,15 +3192,52 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
if ( contentFile.hasContent() == false)
beginReadTransaction( sess);
}
+
+ // Check if there is a quota manager
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+ QuotaManager quotaMgr = ctx.getQuotaManager();
+ long curSize = file.getFileSize();
+
+ if ( quotaMgr != null) {
+
+ // Check if the file requires extending
+
+ long extendSize = 0L;
+ long endOfWrite = fileOffset + size;
+
+ if ( endOfWrite > curSize) {
+
+ // Calculate the amount the file must be extended
+
+ extendSize = endOfWrite - file.getFileSize();
+
+ // Allocate space for the file extend
+
+ quotaMgr.allocateSpace(sess, tree, file, extendSize);
+ }
+ }
// Write to the file
file.writeFile(buffer, size, bufferOffset, fileOffset);
+
+ // Check if the file size was reduced by the write, may have been extended previously
+
+ if ( quotaMgr != null) {
+
+ // Check if the file size reduced
+
+ if ( file.getFileSize() < curSize) {
+
+ // Release space that was freed by the write
+
+ quotaMgr.releaseSpace( sess, tree, file.getFileId(), file.getFullName(), curSize - file.getFileSize());
+ }
+ }
// Debug
- ContentContext ctx = (ContentContext) tree.getContext();
-
if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILEIO))
logger.debug("Wrote bytes to file: network file=" + file + " buffer size=" + buffer.length + " size=" + size + " file offset=" + fileOffset);
diff --git a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
index d48c88f083..8af6d735d5 100644
--- a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
+++ b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
@@ -273,7 +273,7 @@ public class ContentNetworkFile extends NodeRefNetworkFile
* @see NetworkFile#WRITEONLY
* @see NetworkFile#READWRITE
*/
- private void openContent(boolean write, boolean trunc)
+ protected void openContent(boolean write, boolean trunc)
throws AccessDeniedException, AlfrescoRuntimeException
{
// Check if the file is a directory
@@ -359,6 +359,17 @@ public class ContentNetworkFile extends NodeRefNetworkFile
channel = ((ContentReader) content).getFileChannel();
}
+
+ // Update the current file size
+
+ if ( channel != null) {
+ try {
+ setFileSize(channel.size());
+ }
+ catch (IOException ex) {
+ logger.error( ex);
+ }
+ }
}
/**
diff --git a/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java b/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java
new file mode 100644
index 0000000000..8dbfedbdd8
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.filesys.repo;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.alfresco.filesys.state.FileStateTable;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.filesys.DiskDeviceContext;
+import org.alfresco.jlan.server.filesys.DiskFullException;
+import org.alfresco.jlan.server.filesys.DiskInterface;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.server.filesys.quota.QuotaManager;
+import org.alfresco.jlan.server.filesys.quota.QuotaManagerException;
+import org.alfresco.jlan.util.MemorySize;
+import org.alfresco.service.cmr.usage.ContentUsageService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Content Quota Manager Class
+ *
+ * Quota manager implementation for the Alfresco repository.
+ *
+ * @author gkspencer
+ *
+ */
+public class ContentQuotaManager implements QuotaManager, Runnable {
+
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog(ContentQuotaManager.class);
+
+ // User details idle check interval
+
+ private static final long UserQuotaCheckInterval = 1 * 60 * 1000; // 1 minute
+ private static final long UserQuotaExpireInterval = 5 * 60 * 1000; // 5 minutes
+
+ // Associated filesystem driver
+
+ private ContentDiskDriver m_filesys;
+
+ // Content usage service
+
+ private ContentUsageService m_usageService;
+
+ // Track live usage of users that are writing files
+
+ private HashMap m_liveUsage;
+ private Object m_addDetailsLock = new Object();
+
+ // User details inactivity checker thread
+
+ private Thread m_thread;
+ private boolean m_shutdown;
+
+ /**
+ * Get the usage service
+ *
+ * @return ContentUsageService
+ */
+ public final ContentUsageService getUsageService() {
+ return m_usageService;
+ }
+
+ /**
+ * Set the usage service
+ *
+ * @param usageService ContentUsageService
+ */
+ public final void setUsageService(ContentUsageService usageService) {
+ m_usageService = usageService;
+ }
+
+ /**
+ * Return the free space available in bytes
+ *
+ * @return long
+ */
+ public long getAvailableFreeSpace() {
+
+ // Return a dummy value for now
+ //
+ // Need to find the content store size and return the live available space value if possible
+
+ return 100 * MemorySize.GIGABYTE;
+ }
+
+ /**
+ * Return the free space available to the specified user/session
+ *
+ * @param sess SrvSession
+ * @param tree TreeConnection
+ * @return long
+ */
+ public long getUserFreeSpace(SrvSession sess, TreeConnection tree) {
+
+ // Check if there is a live usage record for the user
+
+ UserQuotaDetails userQuota = getQuotaDetails(sess, true);
+ if ( userQuota != null)
+ return userQuota.getAvailableSpace();
+
+ // No quota details available
+
+ return 0L;
+ }
+
+ /**
+ * Allocate space on the filesystem.
+ *
+ * @param sess SrvSession
+ * @param tree TreeConnection
+ * @param file NetworkFile
+ * @param alloc long
+ * @return long
+ * @exception IOException
+ */
+ public long allocateSpace(SrvSession sess, TreeConnection tree, NetworkFile file, long alloc)
+ throws IOException {
+
+ // Check if there is a live usage record for the user
+
+ UserQuotaDetails userQuota = getQuotaDetails(sess, true);
+ long allowedAlloc = 0L;
+
+ if ( userQuota != null) {
+
+ // Check if the user has a usage quota
+
+ if ( userQuota.hasUserQuota()) {
+
+ synchronized ( userQuota) {
+
+ // Check if the user has enough free space allocation
+
+ if ( alloc > 0 && userQuota.getAvailableSpace() >= alloc) {
+ userQuota.addToCurrentUsage( alloc);
+ allowedAlloc = alloc;
+ }
+ }
+ }
+ else {
+
+ // Update the live usage
+
+ synchronized ( userQuota) {
+ userQuota.addToCurrentUsage( alloc);
+ allowedAlloc = alloc;
+ }
+ }
+ }
+ else if ( logger.isDebugEnabled())
+ logger.debug("Failed to allocate " + alloc + " bytes for sess " + sess.getUniqueId());
+
+ // Check if the allocation was allowed
+
+ if ( allowedAlloc < alloc) {
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Allocation failed userQuota=" + userQuota);
+
+ throw new DiskFullException();
+ }
+ else if ( logger.isDebugEnabled())
+ logger.debug("Allocated " + alloc + " bytes, userQuota=" + userQuota);
+
+ // Return the allocation size
+
+ return allowedAlloc;
+ }
+
+ /**
+ * Release space to the free space for the filesystem.
+ *
+ * @param sess SrvSession
+ * @param tree TreeConnection
+ * @param fid int
+ * @param path String
+ * @param alloc long
+ * @exception IOException
+ */
+ public void releaseSpace(SrvSession sess, TreeConnection tree, int fid, String path, long alloc)
+ throws IOException {
+
+ // Check if there is a live usage record for the user
+
+ UserQuotaDetails userQuota = getQuotaDetails(sess, true);
+
+ if ( userQuota != null) {
+
+ synchronized ( userQuota) {
+
+ // Release the space from the live usage value
+
+ userQuota.subtractFromCurrentUsage( alloc);
+ }
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Released " + alloc + " bytes, userQuota=" + userQuota);
+ }
+ else if ( logger.isDebugEnabled())
+ logger.debug("Failed to release " + alloc + " bytes for sess " + sess.getUniqueId());
+ }
+
+ /**
+ * Start the quota manager.
+ *
+ * @param disk DiskInterface
+ * @param ctx DiskDeviceContext
+ * @exception QuotaManagerException
+ */
+ public void startManager(DiskInterface disk, DiskDeviceContext ctx)
+ throws QuotaManagerException {
+
+ // Save the filesystem driver details
+
+ if ( disk instanceof ContentDiskDriver)
+ m_filesys = (ContentDiskDriver) disk;
+ else
+ throw new QuotaManagerException("Invalid filesystem type, " + disk.getClass().getName());
+
+ // Allocate the live usage table
+
+ m_liveUsage = new HashMap();
+
+ // Create the inactivity checker thread
+
+ m_thread = new Thread(this);
+ m_thread.setDaemon(true);
+ m_thread.setName("ContentQuotaManagerChecker");
+ m_thread.start();
+ }
+
+ /**
+ * Stop the quota manager
+ *
+ * @param disk DiskInterface
+ * @param ctx DiskDeviceContext
+ * @exception QuotaManagerException
+ */
+ public void stopManager(DiskInterface disk, DiskDeviceContext ctx)
+ throws QuotaManagerException {
+
+ // Clear out the live usage details
+
+ m_liveUsage.clear();
+
+ // Shutdown the checker thread
+
+ m_shutdown = true;
+ m_thread.interrupt();
+ }
+
+ /**
+ * Get the usage details for the session/user
+ *
+ * @param sess SrvSession
+ * @param loadDetails boolean
+ * @return UserQuotaDetails
+ */
+ private UserQuotaDetails getQuotaDetails(SrvSession sess, boolean loadDetails) {
+
+ UserQuotaDetails userQuota = null;
+ if ( sess != null && sess.hasClientInformation()) {
+
+ // Get the live usage values
+
+ userQuota = m_liveUsage.get( sess.getClientInformation().getUserName());
+ if ( userQuota == null && loadDetails == true) {
+
+ // User is not in the live tracking table, load details for the user
+
+ try {
+ userQuota = loadUsageDetails( sess);
+ }
+ catch ( QuotaManagerException ex) {
+ if ( logger.isDebugEnabled())
+ logger.debug( ex);
+ }
+ }
+ }
+
+ // Return the user quota details
+
+ return userQuota;
+ }
+
+ /**
+ * Load the user quota details
+ *
+ * @param sess SrvSession
+ * @return UserQuotaDetails
+ * @throws QuotaManagerException
+ */
+ private UserQuotaDetails loadUsageDetails(SrvSession sess)
+ throws QuotaManagerException {
+
+ // Check if the user name is available
+
+ if ( sess == null || sess.hasClientInformation() == false)
+ throw new QuotaManagerException("No session/client information");
+
+ UserQuotaDetails quotaDetails = null;
+ String userName = null;
+
+ try {
+
+ // Get the user name
+
+ userName = sess.getClientInformation().getUserName();
+ if ( userName == null || userName.length() == 0)
+ throw new QuotaManagerException("No user name for client");
+
+ // Start a transaction
+
+ m_filesys.beginReadTransaction(sess);
+
+ // Get the usage quota and current usage values for the user
+
+ long userQuota = m_usageService.getUserQuota( userName);
+ long userUsage = m_usageService.getUserUsage( userName);
+
+ // Create the user quota details for live tracking
+
+ quotaDetails = new UserQuotaDetails( userName, userQuota);
+ if ( userUsage > 0L)
+ quotaDetails.setCurrentUsage( userUsage);
+
+ // Add the details to the live tracking table
+
+ synchronized ( m_addDetailsLock) {
+
+ // Check if another thread has added the details
+
+ UserQuotaDetails details = m_liveUsage.get( userName);
+ if ( details != null)
+ quotaDetails = details;
+ else
+ m_liveUsage.put( userName, quotaDetails);
+ }
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug( "Added live usage tracking " + quotaDetails);
+ }
+ catch ( Exception ex) {
+
+ // Log the error
+
+ if ( logger.isErrorEnabled())
+ logger.error( ex);
+
+ // Failed to load usage details
+
+ throw new QuotaManagerException("Failed to load usage for " + userName + ", " + ex);
+ }
+
+ // Return the user usage details
+
+ return quotaDetails;
+ }
+
+ /**
+ * Inactivity checker, run in a seperate thread
+ */
+ public void run() {
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Content quota manager checker thread starting");
+
+ // Loop forever
+
+ m_shutdown = false;
+
+ while ( m_shutdown == false)
+ {
+
+ // Sleep for the required interval
+
+ try
+ {
+ Thread.sleep( UserQuotaCheckInterval);
+ }
+ catch (InterruptedException ex)
+ {
+ }
+
+ // Check for shutdown
+
+ if ( m_shutdown == true)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Content quota manager checker thread closing");
+
+ return;
+ }
+
+ // Check if there are any user quota details to check
+
+ if ( m_liveUsage != null && m_liveUsage.size() > 0)
+ {
+ try
+ {
+ // Timestamp to check if the quota details is inactive
+
+ long checkTime = System.currentTimeMillis() - UserQuotaExpireInterval;
+
+ // Loop through the user quota details
+
+ Iterator userNames = m_liveUsage.keySet().iterator();
+
+ while ( userNames.hasNext()) {
+
+ // Get the user quota details and check if it has been inactive in the last check interval
+
+ String userName = userNames.next();
+ UserQuotaDetails quotaDetails = m_liveUsage.get( userName);
+
+ if ( quotaDetails.getLastUpdated() < checkTime) {
+
+ // Remove the live usage tracking details, inactive
+
+ m_liveUsage.remove( userName);
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Removed inactive usage tracking, " + quotaDetails);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log errors if not shutting down
+
+ if ( m_shutdown == false)
+ logger.debug(ex);
+ }
+ }
+ }
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/UserQuotaDetails.java b/source/java/org/alfresco/filesys/repo/UserQuotaDetails.java
new file mode 100644
index 0000000000..eeeafeec3d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/UserQuotaDetails.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.filesys.repo;
+
+import org.alfresco.jlan.util.MemorySize;
+
+/**
+ * User Quota Details Class
+ *
+ * Used to track the live usage of a user as files are being written.
+ *
+ * @author gkspencer
+ */
+public class UserQuotaDetails {
+
+ // User name and allowed quota, -1 indicates unlimited quota
+
+ private String m_userName;
+ private long m_quota;
+
+ // Current live usage
+
+ private long m_curUsage;
+
+ // Timestamp of the last allocation/release
+
+ private long m_lastUpdate;
+
+ /**
+ * Class constructor
+ *
+ * @param userName String
+ * @param quota long
+ */
+ public UserQuotaDetails(String userName, long quota) {
+ m_userName = userName;
+ m_quota = quota;
+ }
+
+ /**
+ * Return the user name
+ *
+ * @return String
+ */
+ public final String getUserName() {
+ return m_userName;
+ }
+
+ /**
+ * Check if the user has a usage quota
+ *
+ * @return boolean
+ */
+ public final boolean hasUserQuota() {
+ return m_quota == -1L ? false : true;
+ }
+
+ /**
+ * Return the user quota, in bytes
+ *
+ * @return long
+ */
+ public final long getUserQuota() {
+ return m_quota;
+ }
+
+ /**
+ * Return the current live usage, in bytes
+ *
+ * @return long
+ */
+ public final long getCurrentUsage() {
+ return m_curUsage;
+ }
+
+ /**
+ * Return the time the live usage value was last updated
+ *
+ * @return long
+ */
+ public final long getLastUpdated() {
+ return m_lastUpdate;
+ }
+
+ /**
+ * Return the available space for this user, -1 is unlimited
+ *
+ * @return long
+ */
+ public final long getAvailableSpace() {
+ if ( getUserQuota() == 0)
+ return -1L;
+ long availSpace = getUserQuota() - getCurrentUsage();
+ if ( availSpace < 0L)
+ availSpace = 0L;
+ return availSpace;
+ }
+
+ /**
+ * Set the user quota, in bytes
+ *
+ * @param quota long
+ */
+ public final void setUserQuota(long quota) {
+ m_quota = quota;
+ }
+
+ /**
+ * Update the current live usage
+ *
+ * @param usage long
+ */
+ public final void setCurrentUsage(long usage) {
+ m_curUsage = usage;
+ m_lastUpdate = System.currentTimeMillis();
+ }
+
+ /**
+ * Add to the current live usage
+ *
+ * @param usage long
+ * @return long
+ */
+ public final long addToCurrentUsage(long usage) {
+ m_curUsage += usage;
+ m_lastUpdate = System.currentTimeMillis();
+
+ return m_curUsage;
+ }
+
+ /**
+ * Subtract from the current live usage
+ *
+ * @param usage long
+ * @return long
+ */
+ public final long subtractFromCurrentUsage(long usage) {
+ m_curUsage -= usage;
+ m_lastUpdate = System.currentTimeMillis();
+
+ return m_curUsage;
+ }
+
+ /**
+ * Return the user quota details as a string
+ *
+ * @return String
+ */
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+
+ str.append("[");
+ str.append(getUserName());
+ str.append(",quota=");
+ str.append(MemorySize.asScaledString(getUserQuota()));
+ str.append(",current=");
+ str.append(getCurrentUsage());
+ str.append(",available=");
+ str.append(getAvailableSpace());
+ str.append("/");
+ str.append(MemorySize.asScaledString(getAvailableSpace()));
+ str.append("]");
+
+ return str.toString();
+ }
+}