From 2e2dbf62f1fe948967120d6861ecb6fcf0acb069 Mon Sep 17 00:00:00 2001 From: Gary Spencer Date: Wed, 22 Nov 2006 11:11:54 +0000 Subject: [PATCH] Checkpoint of file state refactoring. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@4417 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org/alfresco/filesys/avm/AVMContext.java | 68 ++++- .../alfresco/filesys/avm/AVMDiskDriver.java | 36 ++- .../org/alfresco/filesys/avm/AVMPath.java | 238 ++++++++++++++++++ .../alfresco/filesys/avm/AVMShareMapper.java | 7 +- .../alfresco/filesys/ftp/FTPSrvSession.java | 2 +- .../server/auth/CifsAuthenticator.java | 2 +- .../server/config/ServerConfiguration.java | 13 +- .../filesys/server/core/DeviceContext.java | 45 +++- .../filesys/server/core/DeviceInterface.java | 5 +- .../filesys/server/core/SharedDevice.java | 2 +- .../server/filesys/DiskDeviceContext.java | 5 +- .../repo => server/state}/FileState.java | 2 +- .../filesys/server/state/FileStateReaper.java | 215 ++++++++++++++++ .../repo => server/state}/FileStateTable.java | 130 +--------- .../smb/server/repo/ContentContext.java | 40 ++- .../smb/server/repo/ContentDiskDriver.java | 39 ++- .../server/repo}/HomeShareMapper.java | 14 +- .../repo/pseudo/ContentPseudoFileImpl.java | 2 +- 18 files changed, 694 insertions(+), 171 deletions(-) create mode 100644 source/java/org/alfresco/filesys/avm/AVMPath.java rename source/java/org/alfresco/filesys/{smb/server/repo => server/state}/FileState.java (95%) create mode 100644 source/java/org/alfresco/filesys/server/state/FileStateReaper.java rename source/java/org/alfresco/filesys/{smb/server/repo => server/state}/FileStateTable.java (72%) rename source/java/org/alfresco/filesys/{server/filesys => smb/server/repo}/HomeShareMapper.java (92%) diff --git a/source/java/org/alfresco/filesys/avm/AVMContext.java b/source/java/org/alfresco/filesys/avm/AVMContext.java index 33b199e11a..801b6aa46f 100644 --- a/source/java/org/alfresco/filesys/avm/AVMContext.java +++ b/source/java/org/alfresco/filesys/avm/AVMContext.java @@ -20,6 +20,8 @@ package org.alfresco.filesys.avm; import org.alfresco.filesys.server.filesys.DiskDeviceContext; import org.alfresco.filesys.server.filesys.FileSystem; import org.alfresco.filesys.server.filesys.SrvDiskInfo; +import org.alfresco.filesys.server.state.FileStateReaper; +import org.alfresco.filesys.server.state.FileStateTable; /** * AVM Filesystem Context Class @@ -41,15 +43,21 @@ public class AVMContext extends DiskDeviceContext { private String m_storePath; private int m_version = VERSION_HEAD; + // File state table and associated file state reaper + + private FileStateTable m_stateTable; + private FileStateReaper m_stateReaper; + /** * Class constructor * + * @param filesysName String * @param storePath String * @param version int */ - public AVMContext( String storePath, int version) + public AVMContext( String filesysName, String storePath, int version) { - super( storePath + "(" + version + ")"); + super( filesysName, storePath + "(" + version + ")"); // Set the store root path, remove any trailing slash as relative paths will be appended to this value @@ -106,8 +114,64 @@ public class AVMContext extends DiskDeviceContext { */ public void CloseContext() { + // Deregister the file state table from the reaper + + if ( m_stateTable != null) + enableStateTable( false, m_stateReaper); + // Call the base class super.CloseContext(); } + + /** + * Determine if the file state table is enabled + * + * @return boolean + */ + public final boolean hasStateTable() + { + return m_stateTable != null ? true : false; + } + + /** + * Return the file state table + * + * @return FileStateTable + */ + public final FileStateTable getStateTable() + { + return m_stateTable; + } + + /** + * Enable/disable the file state table + * + * @param ena boolean + * @param stateReaper FileStateReaper + */ + public final void enableStateTable(boolean ena, FileStateReaper stateReaper) + { + if ( ena == false) + { + // Remove the state table from the reaper + + stateReaper.removeStateTable( getFilesystemName()); + m_stateTable = null; + } + else if ( m_stateTable == null) + { + // Create the file state table + + m_stateTable = new FileStateTable(); + + // Register with the file state reaper + + stateReaper.addStateTable( getFilesystemName(), m_stateTable); + } + + // Save the reaper, for deregistering when the filesystem is closed + + m_stateReaper = stateReaper; + } } diff --git a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java index 26b6db8f00..77ab5e1187 100644 --- a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java +++ b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java @@ -28,6 +28,7 @@ import org.alfresco.config.ConfigElement; import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.core.DeviceContext; import org.alfresco.filesys.server.core.DeviceContextException; +import org.alfresco.filesys.server.core.DeviceInterface; import org.alfresco.filesys.server.filesys.AccessDeniedException; import org.alfresco.filesys.server.filesys.DirectoryNotEmptyException; import org.alfresco.filesys.server.filesys.DiskInterface; @@ -40,6 +41,7 @@ import org.alfresco.filesys.server.filesys.FileStatus; import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.server.filesys.SearchContext; import org.alfresco.filesys.server.filesys.TreeConnection; +import org.alfresco.filesys.server.state.FileStateReaper; import org.alfresco.filesys.util.StringList; import org.alfresco.filesys.util.WildCard; import org.alfresco.repo.security.authentication.AuthenticationComponent; @@ -93,6 +95,10 @@ public class AVMDiskDriver implements DiskInterface { private ServiceRegistry m_serviceRegistry; + // File state reaper + + private FileStateReaper m_stateReaper; + /** * Default constructor */ @@ -140,6 +146,16 @@ public class AVMDiskDriver implements DiskInterface { return m_serviceRegistry; } + /** + * Return the file state reaper + * + * @return FileStateReaper + */ + public final FileStateReaper getStateReaper() + { + return m_stateReaper; + } + /** * Set the AVM service * @@ -200,15 +216,27 @@ public class AVMDiskDriver implements DiskInterface { m_mimetypeService = mimetypeService; } + /** + * Set the file state reaper + * + * @param stateReaper FileStateReaper + */ + public final void setStateReaper(FileStateReaper stateReaper) + { + m_stateReaper = stateReaper; + } + /** * Parse and validate the parameter string and create a device context object for this instance * of the shared device. * + * @param devIface DeviceInterface + * @param name String * @param cfg ConfigElement * @return DeviceContext * @exception DeviceContextException */ - public DeviceContext createContext(ConfigElement cfg) + public DeviceContext createContext(DeviceInterface devIface, String name, ConfigElement cfg) throws DeviceContextException { // Use the system user as the authenticated context for the filesystem initialization @@ -315,7 +343,7 @@ public class AVMDiskDriver implements DiskInterface { // Create the context - context = new AVMContext(storePath, version); + context = new AVMContext( name, storePath, version); } catch (Exception ex) { @@ -342,6 +370,10 @@ public class AVMDiskDriver implements DiskInterface { } } + // Enable file state caching + + context.enableStateTable( true, getStateReaper()); + // Return the context for this shared filesystem return context; diff --git a/source/java/org/alfresco/filesys/avm/AVMPath.java b/source/java/org/alfresco/filesys/avm/AVMPath.java new file mode 100644 index 0000000000..f79e003449 --- /dev/null +++ b/source/java/org/alfresco/filesys/avm/AVMPath.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ + +package org.alfresco.filesys.avm; + +import org.alfresco.filesys.server.filesys.FileName; + +/** + * AVM Path Class + * + *

Parses a share relative path into store, version and remaining path values. + * + * @author gkspencer + */ +public class AVMPath { + + // Constants + // + // Invalid version id value + + private static final int InvalidVersionId = -2; + + // Version id string for the head version + + private static final String VersionNameHead = "Head"; + + // AVM path seperator + + public static final char AVM_SEPERATOR = '/'; + + // Store name + + private String m_storeName; + + // Version id + + private int m_version = InvalidVersionId; + + // Remaining path + + private String m_path; + + // AVM style path + + private String m_avmPath; + + /** + * Class constructor + * + * @param shrPath String + */ + public AVMPath(String shrPath) + { + // Parse the path + + parsePath( shrPath); + } + + /** + * Return the store name + * + * @return String + */ + public final String getStoreName() + { + return m_storeName; + } + + /** + * Check if the version id was specified in the path + * + * @return boolean + */ + public final boolean hasVersion() + { + return m_version != InvalidVersionId ? true : false; + } + + /** + * Return the version id + * + * @return int + */ + public final int getVersion() + { + return m_version; + } + + /** + * Return the share relative path + * + * @return String + */ + public final String getRelativePath() + { + return m_path; + } + + /** + * Return the AVM style path, in :/ format + * + * @return String + */ + public final String getAVMPath() + { + return m_avmPath; + } + + /** + * Check if the path is valid + * + * @return boolean + */ + public final boolean isValid() + { + return m_storeName == null ? false : true; + } + + /** + * Parse the path + * + * @param path String + */ + private final void parsePath( String path) + { + // Split the path + + String[] paths = FileName.splitAllPaths(path); + + if ( paths == null || paths.length == 0) + return; + + // Set the store name + + m_storeName = paths[0]; + + if ( paths.length > 1) + { + // Validate the version id + + String verStr = paths[1]; + if ( verStr.equalsIgnoreCase( VersionNameHead)) + m_version = -1; + else + { + try + { + // Parse the version id + + m_version = Integer.parseInt( verStr); + + // Validate the version id + + if ( m_version < 0) + { + // Invalid version id + + m_storeName = null; + return; + } + } + catch ( NumberFormatException ex) + { + m_storeName = null; + return; + } + } + + // If there additional path elements build the share and AVM relative paths + + if ( paths.length > 2) + { + // Build the share relative path + + StringBuilder pathStr = new StringBuilder(); + + for ( int i = 2; i < paths.length; i++) + { + pathStr.append( FileName.DOS_SEPERATOR); + pathStr.append( paths[i]); + } + + m_path = pathStr.toString(); + + // Build the AVM path, in :/ format + + pathStr.setLength( 0); + + pathStr.append( m_storeName); + pathStr.append( ":"); + pathStr.append( m_path.replace( FileName.DOS_SEPERATOR, AVM_SEPERATOR)); + + m_avmPath = pathStr.toString(); + } + } + } + + /** + * Return the AVM path details as a string + * + * @return String + */ + public String toString() + { + StringBuilder str = new StringBuilder(); + + str.append("["); + str.append(getStoreName()); + str.append(","); + + if ( hasVersion()) + str.append(getVersion()); + else + str.append("NoVersion"); + + str.append(","); + str.append(getRelativePath()); + str.append(":"); + str.append(getAVMPath()); + str.append("]"); + + return str.toString(); + } +} diff --git a/source/java/org/alfresco/filesys/avm/AVMShareMapper.java b/source/java/org/alfresco/filesys/avm/AVMShareMapper.java index 0f1592b7d0..dca26255f6 100644 --- a/source/java/org/alfresco/filesys/avm/AVMShareMapper.java +++ b/source/java/org/alfresco/filesys/avm/AVMShareMapper.java @@ -249,12 +249,9 @@ public class AVMShareMapper implements ShareMapper { // Create a dynamic share mapped to the AVM store/version - DiskDeviceContext avmCtx = new AVMContext( storePath, storeVersion); + AVMContext avmCtx = new AVMContext( name, storePath, storeVersion); + avmCtx.enableStateTable( true, avmDrv.getStateReaper()); - // Default the filesystem to look like an 80Gb sized disk with 90% free space - - avmCtx.setDiskInformation(new SrvDiskInfo(2560, 64, 512, 2304)); - // Create a dynamic shared device for the store version DiskSharedDevice diskShare = new DiskSharedDevice( name, avmDrv, avmCtx, SharedDevice.Temporary); diff --git a/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java b/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java index afd55c32a8..db56f00801 100644 --- a/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java +++ b/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java @@ -3352,7 +3352,7 @@ public class FTPSrvSession extends SrvSession implements Runnable // Create the disk driver and context DiskInterface diskDrv = getServer().getConfiguration().getDiskInterface(); - DiskDeviceContext diskCtx = new ContentContext("", "", client.getHomeFolder()); + DiskDeviceContext diskCtx = new ContentContext( client.getUserName(), "", "", client.getHomeFolder()); // Default the filesystem to look like an 80Gb sized disk with 90% free space diff --git a/source/java/org/alfresco/filesys/server/auth/CifsAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/CifsAuthenticator.java index 3effae1145..dd932b0694 100644 --- a/source/java/org/alfresco/filesys/server/auth/CifsAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/CifsAuthenticator.java @@ -836,7 +836,7 @@ public abstract class CifsAuthenticator // Create the disk driver and context DiskInterface diskDrv = m_config.getDiskInterface(); - DiskDeviceContext diskCtx = new ContentContext("", "", client.getHomeFolder()); + DiskDeviceContext diskCtx = new ContentContext(client.getUserName(), "", "", client.getHomeFolder()); // Default the filesystem to look like an 80Gb sized disk with 90% free space diff --git a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java index 73e78464c0..0bb0ce20a7 100644 --- a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java +++ b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java @@ -69,13 +69,13 @@ import org.alfresco.filesys.server.core.SharedDeviceList; import org.alfresco.filesys.server.filesys.DefaultShareMapper; import org.alfresco.filesys.server.filesys.DiskInterface; import org.alfresco.filesys.server.filesys.DiskSharedDevice; -import org.alfresco.filesys.server.filesys.HomeShareMapper; import org.alfresco.filesys.smb.ServerType; import org.alfresco.filesys.smb.TcpipSMB; import org.alfresco.filesys.smb.server.repo.ContentContext; import org.alfresco.filesys.smb.server.repo.DesktopAction; import org.alfresco.filesys.smb.server.repo.DesktopActionException; import org.alfresco.filesys.smb.server.repo.DesktopActionTable; +import org.alfresco.filesys.smb.server.repo.HomeShareMapper; import org.alfresco.filesys.util.IPAddress; import org.alfresco.filesys.util.StringList; import org.alfresco.filesys.util.X64; @@ -1692,11 +1692,16 @@ public class ServerConfiguration extends AbstractLifecycleBean // the new filesystem DiskInterface filesysDriver = this.avmDiskInterface; - AVMContext filesysContext = (AVMContext) filesysDriver.createContext(elem); + AVMContext filesysContext = (AVMContext) filesysDriver.createContext( filesysDriver, filesysName, elem); + filesysContext.setFilesystemName(filesysName); // Create the shared filesystem filesys = new DiskSharedDevice(filesysName, filesysDriver, filesysContext); + + // Start the filesystem + + filesysContext.startFilesystem(filesys); } else { @@ -1704,7 +1709,7 @@ public class ServerConfiguration extends AbstractLifecycleBean // the new filesystem DiskInterface filesysDriver = this.diskInterface; - ContentContext filesysContext = (ContentContext) filesysDriver.createContext(elem); + ContentContext filesysContext = (ContentContext) filesysDriver.createContext( filesysDriver, filesysName, elem); // Check if an access control list has been specified @@ -1804,7 +1809,7 @@ public class ServerConfiguration extends AbstractLifecycleBean { // Create the new share for the store - AVMContext avmContext = new AVMContext( storeName + ":/", AVMContext.VERSION_HEAD); + AVMContext avmContext = new AVMContext( storeName, storeName + ":/", AVMContext.VERSION_HEAD); // Create the shared filesystem diff --git a/source/java/org/alfresco/filesys/server/core/DeviceContext.java b/source/java/org/alfresco/filesys/server/core/DeviceContext.java index 422eeb826d..5d379b9743 100644 --- a/source/java/org/alfresco/filesys/server/core/DeviceContext.java +++ b/source/java/org/alfresco/filesys/server/core/DeviceContext.java @@ -29,14 +29,17 @@ public class DeviceContext private String m_devName; + // Filesystem name + + private String m_filesysName; + // Flag to indicate if the device is available. Unavailable devices will not be listed by the - // various - // protocol servers. + // various protocol servers. private boolean m_available = true; /** - * DeviceContext constructor. + * Default constructor */ public DeviceContext() { @@ -44,23 +47,37 @@ public class DeviceContext } /** - * DeviceContext constructor. + * Class constructor + * + * @param filesysName String + * @param devName String */ - public DeviceContext(String devName) + public DeviceContext(String filesysName, String devName) { - m_devName = devName; + m_filesysName = filesysName; + m_devName = devName; } /** * Return the device name. * - * @return java.lang.String + * @return String */ public final String getDeviceName() { return m_devName; } + /** + * Return the filesystem name + * + * @return String + */ + public final String getFilesystemName() + { + return m_filesysName; + } + /** * Determine if the filesystem is available * @@ -84,13 +101,23 @@ public class DeviceContext /** * Set the device name. * - * @param name java.lang.String + * @param name String */ public final void setDeviceName(String name) { m_devName = name; } + /** + * Set the filesystem name + * + * @param filesysName String + */ + public final void setFilesystemName( String filesysName) + { + m_filesysName = filesysName; + } + /** * Close the device context, free any resources allocated by the context */ @@ -108,6 +135,8 @@ public class DeviceContext StringBuffer str = new StringBuffer(); str.append("["); + str.append(getFilesystemName()); + str.append(","); str.append(getDeviceName()); str.append("]"); diff --git a/source/java/org/alfresco/filesys/server/core/DeviceInterface.java b/source/java/org/alfresco/filesys/server/core/DeviceInterface.java index 54af951dc2..47462000b1 100644 --- a/source/java/org/alfresco/filesys/server/core/DeviceInterface.java +++ b/source/java/org/alfresco/filesys/server/core/DeviceInterface.java @@ -32,11 +32,14 @@ public interface DeviceInterface * of the shared device. The same DeviceInterface implementation may be used for multiple * shares. * + * @param devIface DeviceInterface + * @param name String * @param args ConfigElement * @return DeviceContext * @exception DeviceContextException */ - public DeviceContext createContext(ConfigElement args) throws DeviceContextException; + public DeviceContext createContext(DeviceInterface devIface, String name, ConfigElement args) + throws DeviceContextException; /** * Connection opened to this disk device diff --git a/source/java/org/alfresco/filesys/server/core/SharedDevice.java b/source/java/org/alfresco/filesys/server/core/SharedDevice.java index 61df75a120..203fb51bfc 100644 --- a/source/java/org/alfresco/filesys/server/core/SharedDevice.java +++ b/source/java/org/alfresco/filesys/server/core/SharedDevice.java @@ -383,7 +383,7 @@ public class SharedDevice implements Comparable */ public DeviceContext createContext(String[] args) { - return new DeviceContext(args[0]); + return new DeviceContext("", args[0]); } /** diff --git a/source/java/org/alfresco/filesys/server/filesys/DiskDeviceContext.java b/source/java/org/alfresco/filesys/server/filesys/DiskDeviceContext.java index a4a108db7b..369f4fc4fe 100644 --- a/source/java/org/alfresco/filesys/server/filesys/DiskDeviceContext.java +++ b/source/java/org/alfresco/filesys/server/filesys/DiskDeviceContext.java @@ -59,11 +59,12 @@ public class DiskDeviceContext extends DeviceContext /** * Class constructor * + * @param filesysName String * @param devName String */ - public DiskDeviceContext(String devName) + public DiskDeviceContext(String filesysName, String devName) { - super(devName); + super(filesysName, devName); } /** diff --git a/source/java/org/alfresco/filesys/smb/server/repo/FileState.java b/source/java/org/alfresco/filesys/server/state/FileState.java similarity index 95% rename from source/java/org/alfresco/filesys/smb/server/repo/FileState.java rename to source/java/org/alfresco/filesys/server/state/FileState.java index dc87680438..1ebc55434b 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/FileState.java +++ b/source/java/org/alfresco/filesys/server/state/FileState.java @@ -14,7 +14,7 @@ * language governing permissions and limitations under the * License. */ -package org.alfresco.filesys.smb.server.repo; +package org.alfresco.filesys.server.state; import org.alfresco.filesys.locking.FileLock; import org.alfresco.filesys.locking.FileLockList; diff --git a/source/java/org/alfresco/filesys/server/state/FileStateReaper.java b/source/java/org/alfresco/filesys/server/state/FileStateReaper.java new file mode 100644 index 0000000000..ce11004379 --- /dev/null +++ b/source/java/org/alfresco/filesys/server/state/FileStateReaper.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ + +package org.alfresco.filesys.server.state; + +import java.util.Enumeration; +import java.util.Hashtable; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * File State Reaper Class + * + *

FileStateTable objects register with the file state reaper to periodically check for expired file states. + * + * @author gkspencer + */ +public class FileStateReaper implements Runnable { + + // Logging + + private static final Log logger = LogFactory.getLog(FileStateReaper.class); + + // Default expire check thread interval + + private static final long DEFAULT_EXPIRECHECK = 15000; + + // Wakeup interval for the expire file state checker thread + + private long m_expireInterval = DEFAULT_EXPIRECHECK; + + // File state checker thread + + private Thread m_thread; + + // Shutdown request flag + + private boolean m_shutdown; + + // List of file state tables to be scanned for expired file states + + private Hashtable m_stateTables; + + /** + * Default constructor + */ + public FileStateReaper() + { + // Create the reaper thread + + m_thread = new Thread(this); + m_thread.setDaemon(true); + m_thread.setName("FileStateReaper"); + m_thread.start(); + + // Create the file state table list + + m_stateTables = new Hashtable(); + } + + /** + * Return the expired file state checker interval, in milliseconds + * + * @return long + */ + public final long getCheckInterval() + { + return m_expireInterval; + } + + /** + * Set the expired file state checker interval, in milliseconds + * + * @param chkIntval long + */ + public final void setCheckInterval(long chkIntval) + { + m_expireInterval = chkIntval; + } + + /** + * Add a file state table to the reaper list + * + * @param filesysName String + * @param stateTable FileStateTable + */ + public final void addStateTable( String filesysName, FileStateTable stateTable) + { + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "Added file state table for " + filesysName); + + m_stateTables.put( filesysName, stateTable); + } + + /** + * Remove a state table from the reaper list + * + * @param filesysName String + */ + public final void removeStateTable( String filesysName) + { + m_stateTables.remove( filesysName); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "Removed file state table for " + filesysName); + + } + + /** + * Expired file state checker thread + */ + public void run() + { + // Loop forever + + m_shutdown = false; + + while ( m_shutdown == false) + { + + // Sleep for the required interval + + try + { + Thread.sleep(getCheckInterval()); + } + catch (InterruptedException ex) + { + } + + // Check for shutdown + + if ( m_shutdown == true) + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("FileStateReaper thread closing"); + + return; + } + + // Check if there are any state tables registered + + if ( m_stateTables != null && m_stateTables.size() > 0) + { + try + { + // Loop through the registered file state tables and remove expired file states + + Enumeration filesysNames = m_stateTables.keys(); + + while ( filesysNames.hasMoreElements()) + { + // Get the current filesystem name and associated state table + + String filesysName = filesysNames.nextElement(); + FileStateTable stateTable = m_stateTables.get( filesysName); + + // Check for expired file states + + int cnt = stateTable.removeExpiredFileStates(); + + // Debug + + if (logger.isDebugEnabled() && cnt > 0) + logger.debug("Expired " + cnt + " file states for " + filesysName + ", cache=" + stateTable.numberOfStates()); + } + } + catch (Exception ex) + { + // Log errors if not shutting down + + if ( m_shutdown == false) + logger.debug(ex); + } + } + } + } + + /** + * Request the file state checker thread to shutdown + */ + public final void shutdownRequest() { + m_shutdown = true; + + if ( m_thread != null) + { + try { + m_thread.interrupt(); + } + catch (Exception ex) { + } + } + } +} diff --git a/source/java/org/alfresco/filesys/smb/server/repo/FileStateTable.java b/source/java/org/alfresco/filesys/server/state/FileStateTable.java similarity index 72% rename from source/java/org/alfresco/filesys/smb/server/repo/FileStateTable.java rename to source/java/org/alfresco/filesys/server/state/FileStateTable.java index 2eb2797979..e52593591c 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/FileStateTable.java +++ b/source/java/org/alfresco/filesys/server/state/FileStateTable.java @@ -14,19 +14,20 @@ * language governing permissions and limitations under the * License. */ -package org.alfresco.filesys.smb.server.repo; +package org.alfresco.filesys.server.state; import java.util.*; -import java.io.*; import org.apache.commons.logging.*; /** * File State Table Class - *

- * Contains an indexed list of the currently open files/folders. + * + *

Contains an indexed list of the currently open files/folders. + * + * @author gkspencer */ -public class FileStateTable implements Runnable +public class FileStateTable { private static final Log logger = LogFactory.getLog(FileStateTable.class); @@ -34,30 +35,14 @@ public class FileStateTable implements Runnable private static final int INITIAL_SIZE = 100; - // Default expire check thread interval - - private static final long DEFAULT_EXPIRECHECK = 15000; - // File state table, keyed by file path private Hashtable m_stateTable; - // Wakeup interval for the expire file state checker thread - - private long m_expireInterval = DEFAULT_EXPIRECHECK; - // File state expiry time in seconds private long m_cacheTimer = 2 * 60000L; // 2 minutes default - // File state checker thread - - private Thread m_thread; - - // Shutdown request flag - - private boolean m_shutdown; - /** * Class constructor */ @@ -67,20 +52,6 @@ public class FileStateTable implements Runnable // Start the expired file state checker thread - m_thread = new Thread(this); - m_thread.setDaemon(true); - m_thread.setName("FileStateExpire"); - m_thread.start(); - } - - /** - * Return the expired file state checker interval, in milliseconds - * - * @return long - */ - public final long getCheckInterval() - { - return m_expireInterval; } /** @@ -113,16 +84,6 @@ public class FileStateTable implements Runnable m_cacheTimer = tmo; } - /** - * Set the expired file state checker interval, in milliseconds - * - * @param chkIntval long - */ - public final void setCheckInterval(long chkIntval) - { - m_expireInterval = chkIntval; - } - /** * Add a new file state * @@ -351,7 +312,7 @@ public class FileStateTable implements Runnable // DEBUG if (logger.isDebugEnabled()) - logger.debug("++ Expired file state: " + state); + logger.debug("Expired file state: " + state); // Update the expired count @@ -366,79 +327,6 @@ public class FileStateTable implements Runnable return expiredCnt; } - /** - * Expired file state checker thread - */ - public void run() - { - - // Loop forever - - m_shutdown = false; - - while ( m_shutdown == false) - { - - // Sleep for the required interval - - try - { - Thread.sleep(getCheckInterval()); - } - catch (InterruptedException ex) - { - } - - // Check for shutdown - - if ( m_shutdown == true) - { - // Debug - - if ( logger.isDebugEnabled()) - logger.debug("FileStateExpire thread closing"); - - return; - } - - try - { - - // Check for expired file states - - int cnt = removeExpiredFileStates(); - - // Debug - - if (logger.isDebugEnabled() && cnt > 0) - { - logger.debug("++ Expired " + cnt + " file states, cache=" + m_stateTable.size()); - Dump(); - } - } - catch (Exception ex) - { - logger.debug(ex); - } - } - } - - /** - * Request the file state checker thread to shutdown - */ - public final void shutdownRequest() { - m_shutdown = true; - - if ( m_thread != null) - { - try { - m_thread.interrupt(); - } - catch (Exception ex) { - } - } - } - /** * Dump the state cache entries to the specified stream */ @@ -448,7 +336,7 @@ public class FileStateTable implements Runnable // Dump the file state cache entries to the specified stream if (m_stateTable.size() > 0) - logger.debug("++ FileStateCache Entries:"); + logger.debug("FileStateCache Entries:"); Enumeration enm = m_stateTable.keys(); long curTime = System.currentTimeMillis(); @@ -458,7 +346,7 @@ public class FileStateTable implements Runnable String fname = (String) enm.nextElement(); FileState state = m_stateTable.get(fname); - logger.debug(" ++ " + fname + "(" + state.getSecondsToExpire(curTime) + ") : " + state); + logger.debug(" " + fname + "(" + state.getSecondsToExpire(curTime) + ") : " + state); } } } \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentContext.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentContext.java index 211bcd684b..ac6b372855 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentContext.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentContext.java @@ -19,6 +19,8 @@ package org.alfresco.filesys.smb.server.repo; import java.util.Enumeration; import org.alfresco.filesys.server.filesys.*; +import org.alfresco.filesys.server.state.FileStateReaper; +import org.alfresco.filesys.server.state.FileStateTable; import org.alfresco.filesys.smb.server.repo.pseudo.ContentPseudoFileImpl; import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFileInterface; import org.alfresco.service.cmr.repository.*; @@ -41,9 +43,10 @@ public class ContentContext extends DiskDeviceContext private NodeRef m_rootNodeRef; - // File state table + // File state table and associated file state reaper private FileStateTable m_stateTable; + private FileStateReaper m_stateReaper; // URL pseudo file web path prefix (server/port/webapp) and link file name @@ -65,13 +68,14 @@ public class ContentContext extends DiskDeviceContext /** * Class constructor * + *@param filesysName String * @param storeName String * @param rootPath String * @param rootNodeRef NodeRef */ - public ContentContext(String storeName, String rootPath, NodeRef rootNodeRef) + public ContentContext(String filesysName, String storeName, String rootPath, NodeRef rootNodeRef) { - super(rootNodeRef.toString()); + super(filesysName, rootNodeRef.toString()); m_storeName = storeName; m_rootPath = rootPath; @@ -147,13 +151,31 @@ public class ContentContext extends DiskDeviceContext * Enable/disable the file state table * * @param ena boolean + * @param stateReaper FileStateReaper */ - public final void enableStateTable(boolean ena) + public final void enableStateTable(boolean ena, FileStateReaper stateReaper) { if ( ena == false) + { + // Remove the state table from the reaper + + stateReaper.removeStateTable( getFilesystemName()); m_stateTable = null; + } else if ( m_stateTable == null) + { + // Create the file state table + m_stateTable = new FileStateTable(); + + // Register with the file state reaper + + stateReaper.addStateTable( getFilesystemName(), m_stateTable); + } + + // Save the reaper, for deregistering when the filesystem is closed + + m_stateReaper = stateReaper; } /** @@ -354,14 +376,10 @@ public class ContentContext extends DiskDeviceContext */ public void CloseContext() { - // Check if file states are enabled + // Deregister the file state table from the reaper - if ( hasStateTable()) - { - // Shutdown the file state checker thread - - getStateTable().shutdownRequest(); - } + if ( m_stateTable != null) + enableStateTable( false, m_stateReaper); // Call the base class diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java index 7ce6b8705f..983cba5a49 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java @@ -28,6 +28,7 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.core.DeviceContext; import org.alfresco.filesys.server.core.DeviceContextException; +import org.alfresco.filesys.server.core.DeviceInterface; import org.alfresco.filesys.server.filesys.AccessDeniedException; import org.alfresco.filesys.server.filesys.AccessMode; import org.alfresco.filesys.server.filesys.DiskInterface; @@ -44,11 +45,13 @@ import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.server.filesys.SearchContext; import org.alfresco.filesys.server.filesys.SrvDiskInfo; import org.alfresco.filesys.server.filesys.TreeConnection; +import org.alfresco.filesys.server.state.FileState; +import org.alfresco.filesys.server.state.FileStateReaper; +import org.alfresco.filesys.server.state.FileState.FileStateStatus; import org.alfresco.filesys.smb.SMBException; import org.alfresco.filesys.smb.SMBStatus; import org.alfresco.filesys.smb.SharingMode; import org.alfresco.filesys.smb.server.SMBSrvSession; -import org.alfresco.filesys.smb.server.repo.FileState.FileStateStatus; import org.alfresco.filesys.smb.server.repo.pseudo.MemoryNetworkFile; import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFile; import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFileInterface; @@ -113,6 +116,10 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface private ServiceRegistry serviceRegistry; + // File state reaper + + private FileStateReaper m_stateReaper; + /** * Class constructor * @@ -202,6 +209,16 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface return this.serviceRegistry; } + /** + * Return the file state reaper + * + * @return FileStateReaper + */ + public final FileStateReaper getStateReaper() + { + return m_stateReaper; + } + /** * @param contentService the content service */ @@ -282,16 +299,28 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface this.authService = authService; } + /** + * Set the file state reaper + * + * @param stateReaper FileStateReaper + */ + public final void setStateReaper(FileStateReaper stateReaper) + { + m_stateReaper = stateReaper; + } + /** * Parse and validate the parameter string and create a device context object for this instance * of the shared device. The same DeviceInterface implementation may be used for multiple * shares. * + * @param devIface DeviceInterface + * @param name String * @param args ConfigElement * @return DeviceContext * @exception DeviceContextException */ - public DeviceContext createContext(ConfigElement cfg) throws DeviceContextException + public DeviceContext createContext(DeviceInterface devIface, String name, ConfigElement cfg) throws DeviceContextException { // Use the system user as the authenticated context for the filesystem initialization @@ -389,7 +418,7 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface // Create the context - context = new ContentContext(storeValue, rootPath, rootNodeRef); + context = new ContentContext(name, storeValue, rootPath, rootNodeRef); // Default the filesystem to look like an 80Gb sized disk with 90% free space @@ -497,6 +526,10 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface logger.info("Locked files will be marked as offline"); } + // Enable file state caching + + context.enableStateTable( true, getStateReaper()); + // Return the context for this shared filesystem return context; diff --git a/source/java/org/alfresco/filesys/server/filesys/HomeShareMapper.java b/source/java/org/alfresco/filesys/smb/server/repo/HomeShareMapper.java similarity index 92% rename from source/java/org/alfresco/filesys/server/filesys/HomeShareMapper.java rename to source/java/org/alfresco/filesys/smb/server/repo/HomeShareMapper.java index 4e1289d3c4..58209e07eb 100644 --- a/source/java/org/alfresco/filesys/server/filesys/HomeShareMapper.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/HomeShareMapper.java @@ -15,7 +15,7 @@ * License. */ -package org.alfresco.filesys.server.filesys; +package org.alfresco.filesys.smb.server.repo; import java.util.Enumeration; @@ -29,7 +29,9 @@ import org.alfresco.filesys.server.core.ShareMapper; import org.alfresco.filesys.server.core.ShareType; import org.alfresco.filesys.server.core.SharedDevice; import org.alfresco.filesys.server.core.SharedDeviceList; +import org.alfresco.filesys.server.filesys.DiskSharedDevice; import org.alfresco.filesys.smb.server.repo.ContentContext; +import org.alfresco.filesys.smb.server.repo.ContentDiskDriver; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -317,13 +319,11 @@ public class HomeShareMapper implements ShareMapper { // Create the disk driver and context - DiskInterface diskDrv = m_config.getDiskInterface(); - DiskDeviceContext diskCtx = new ContentContext("", "", client.getHomeFolder()); - - // Default the filesystem to look like an 80Gb sized disk with 90% free space - - diskCtx.setDiskInformation(new SrvDiskInfo(2560, 64, 512, 2304)); + ContentDiskDriver diskDrv = ( ContentDiskDriver) m_config.getDiskInterface(); + ContentContext diskCtx = new ContentContext( getHomeFolderName(), "", "", client.getHomeFolder()); + diskCtx.enableStateTable( true, diskDrv.getStateReaper()); + // Create a temporary shared device for the users home directory return new DiskSharedDevice(getHomeFolderName(), diskDrv, diskCtx, SharedDevice.Temporary); diff --git a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java index ab38534a88..e2e6f8c4da 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java @@ -21,11 +21,11 @@ import java.util.Enumeration; import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.filesys.FileName; import org.alfresco.filesys.server.filesys.TreeConnection; +import org.alfresco.filesys.server.state.FileState; import org.alfresco.filesys.smb.server.SMBSrvSession; import org.alfresco.filesys.smb.server.repo.ContentContext; import org.alfresco.filesys.smb.server.repo.DesktopAction; import org.alfresco.filesys.smb.server.repo.DesktopActionTable; -import org.alfresco.filesys.smb.server.repo.FileState; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;