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
This commit is contained in:
Gary Spencer
2006-11-22 11:11:54 +00:00
parent e5babd2675
commit 2e2dbf62f1
18 changed files with 694 additions and 171 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -1,632 +0,0 @@
/*
* Copyright (C) 2005 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.smb.server.repo;
import org.alfresco.filesys.locking.FileLock;
import org.alfresco.filesys.locking.FileLockList;
import org.alfresco.filesys.locking.LockConflictException;
import org.alfresco.filesys.locking.NotLockedException;
import org.alfresco.filesys.server.filesys.FileOpenParams;
import org.alfresco.filesys.server.filesys.FileStatus;
import org.alfresco.filesys.smb.SharingMode;
import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFile;
import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFileList;
import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* File State Class
*
* <p>Keeps track of file state across all sessions on the server, to keep track of file sharing modes,
* file locks and also for synchronizing access to files/folders.
*
* @author gkspencer
*/
public class FileState
{
private static final Log logger = LogFactory.getLog(FileState.class);
// File state constants
public final static long NoTimeout = -1L;
public final static long DefTimeout = 2 * 60000L; // 2 minutes
public final static long RenameTimeout = 1 * 60000L; // 1 minute
// File status
public enum FileStateStatus { NotExist, FileExists, FolderExists, Renamed };
// File name/path
private String m_path;
// File state timeout, -1 indicates no timeout
private long m_tmo;
// File status, indicates if the file/folder exists and if it is a file or folder.
private FileStateStatus m_fileStatus = FileStateStatus.NotExist;
// Open file count
private int m_openCount;
// Sharing mode
private int m_sharedAccess = SharingMode.READWRITE;
// File lock list, allocated once there are active locks on this file
private FileLockList m_lockList;
// Node for this file
private NodeRef m_nodeRef;
// Link to the new file state when a file is renamed
private FileState m_newNameState;
// Pseudo file list
private PseudoFileList m_pseudoFiles;
/**
* Class constructor
*
* @param fname String
* @param isdir boolean
*/
public FileState(String fname, boolean isdir)
{
// Normalize the file path
setPath(fname);
setExpiryTime(System.currentTimeMillis() + DefTimeout);
// Set the file/folder status
setFileStatus( isdir ? FileStateStatus.FolderExists : FileStateStatus.FileExists);
}
/**
* Return the file name/path
*
* @return String
*/
public final String getPath()
{
return m_path;
}
/**
* Return the file status
*
* @return FileStateStatus
*/
public final FileStateStatus getFileStatus()
{
return m_fileStatus;
}
/**
* Determine if the file/folder exists
*
* @return boolen
*/
public final boolean exists()
{
if ( m_fileStatus == FileStateStatus.FileExists ||
m_fileStatus == FileStateStatus.FolderExists)
return true;
return false;
}
/**
* Return the directory state
*
* @return boolean
*/
public final boolean isDirectory()
{
return m_fileStatus == FileStateStatus.FolderExists ? true : false;
}
/**
* Determine if the associated node has been set
*
* @return boolean
*/
public final boolean hasNodeRef()
{
return m_nodeRef != null ? true : false;
}
/**
* Return the associated node
*
* @return NodeRef
*/
public final NodeRef getNodeRef()
{
return m_nodeRef;
}
/**
* Return the file open count
*
* @return int
*/
public final int getOpenCount()
{
return m_openCount;
}
/**
* Return the shared access mode
*
* @return int
*/
public final int getSharedAccess()
{
return m_sharedAccess;
}
/**
* Check if there are active locks on this file
*
* @return boolean
*/
public final boolean hasActiveLocks()
{
if (m_lockList != null && m_lockList.numberOfLocks() > 0)
return true;
return false;
}
/**
* Check if this file state does not expire
*
* @return boolean
*/
public final boolean hasNoTimeout()
{
return m_tmo == NoTimeout ? true : false;
}
/**
* Check if the file can be opened depending on any current file opens and the sharing mode of
* the first file open
*
* @param params FileOpenParams
* @return boolean
*/
public final boolean allowsOpen(FileOpenParams params)
{
// If the file is not currently open then allow the file open
if (getOpenCount() == 0)
return true;
// Check the shared access mode
if (getSharedAccess() == SharingMode.READWRITE && params.getSharedAccess() == SharingMode.READWRITE)
return true;
else if ((getSharedAccess() & SharingMode.READ) != 0 && params.isReadOnlyAccess())
return true;
else if ((getSharedAccess() & SharingMode.WRITE) != 0 && params.isWriteOnlyAccess())
return true;
// Sharing violation, do not allow the file open
return false;
}
/**
* Increment the file open count
*
* @return int
*/
public final synchronized int incrementOpenCount()
{
return m_openCount++;
}
/**
* Decrement the file open count
*
* @return int
*/
public final synchronized int decrementOpenCount()
{
// Debug
if (m_openCount <= 0)
logger.debug("@@@@@ File close name=" + getPath() + ", count=" + m_openCount + " <<ERROR>>");
else
m_openCount--;
return m_openCount;
}
/**
* Check if the file state has expired
*
* @param curTime long
* @return boolean
*/
public final boolean hasExpired(long curTime)
{
if (m_tmo == NoTimeout)
return false;
if (curTime > m_tmo)
return true;
return false;
}
/**
* Return the number of seconds left before the file state expires
*
* @param curTime long
* @return long
*/
public final long getSecondsToExpire(long curTime)
{
if (m_tmo == NoTimeout)
return -1;
return (m_tmo - curTime) / 1000L;
}
/**
* Determine if the file state has an associated rename state
*
* @return boolean
*/
public final boolean hasRenameState()
{
return m_newNameState != null ? true : false;
}
/**
* Return the associated rename state
*
* @return FileState
*/
public final FileState getRenameState()
{
return m_newNameState;
}
/**
* Determine if a folder has pseudo files associated with it
*
* @return boolean
*/
public final boolean hasPseudoFiles()
{
if ( m_pseudoFiles != null)
return m_pseudoFiles.numberOfFiles() > 0;
return false;
}
/**
* Return the pseudo file list
*
* @return PseudoFileList
*/
public final PseudoFileList getPseudoFileList()
{
return m_pseudoFiles;
}
/**
* Add a pseudo file to this folder
*
* @param pfile PseudoFile
*/
public final void addPseudoFile(PseudoFile pfile)
{
if ( m_pseudoFiles == null)
m_pseudoFiles = new PseudoFileList();
m_pseudoFiles.addFile( pfile);
}
/**
* Set the file status
*
* @param status FileStateStatus
*/
public final void setFileStatus(FileStateStatus status)
{
m_fileStatus = status;
}
/**
* Set the file status
*
* @param fsts int
*/
public final void setFileStatus(int fsts)
{
if ( fsts == FileStatus.FileExists)
m_fileStatus = FileStateStatus.FileExists;
else if ( fsts == FileStatus.DirectoryExists)
m_fileStatus = FileStateStatus.FolderExists;
else if ( fsts == FileStatus.NotExist)
m_fileStatus = FileStateStatus.NotExist;
}
/**
* Set the file state expiry time
*
* @param expire long
*/
public final void setExpiryTime(long expire)
{
m_tmo = expire;
}
/**
* Set the node ref for the file/folder
*
* @param nodeRef NodeRef
*/
public final void setNodeRef(NodeRef nodeRef)
{
m_nodeRef = nodeRef;
}
/**
* Set the associated file state when a file is renamed, this is the link to the new file state
*
* @param fstate FileState
*/
public final void setRenameState(FileState fstate)
{
m_newNameState = fstate;
}
/**
* Set the shared access mode, from the first file open
*
* @param mode int
*/
public final void setSharedAccess(int mode)
{
if (getOpenCount() == 0)
m_sharedAccess = mode;
}
/**
* Set the file path
*
* @param path String
*/
public final void setPath(String path)
{
// Split the path into directories and file name, only uppercase the directories to
// normalize the path.
m_path = normalizePath(path);
}
/**
* Return the count of active locks on this file
*
* @return int
*/
public final int numberOfLocks()
{
if (m_lockList != null)
return m_lockList.numberOfLocks();
return 0;
}
/**
* Add a lock to this file
*
* @param lock FileLock
* @exception LockConflictException
*/
public final void addLock(FileLock lock) throws LockConflictException
{
// Check if the lock list has been allocated
if (m_lockList == null)
{
synchronized (this)
{
// Allocate the lock list, check if the lock list has been allocated elsewhere
// as we may have been waiting for the lock
if (m_lockList == null)
m_lockList = new FileLockList();
}
}
// Add the lock to the list, check if there are any lock conflicts
synchronized (m_lockList)
{
// Check if the new lock overlaps with any existing locks
if (m_lockList.allowsLock(lock))
{
// Add the new lock to the list
m_lockList.addLock(lock);
}
else
throw new LockConflictException();
}
}
/**
* Remove a lock on this file
*
* @param lock FileLock
* @exception NotLockedException
*/
public final void removeLock(FileLock lock) throws NotLockedException
{
// Check if the lock list has been allocated
if (m_lockList == null)
throw new NotLockedException();
// Remove the lock from the active list
synchronized (m_lockList)
{
// Remove the lock, check if we found the matching lock
if (m_lockList.removeLock(lock) == null)
throw new NotLockedException();
}
}
/**
* Check if the file is readable for the specified section of the file and process id
*
* @param offset long
* @param len long
* @param pid int
* @return boolean
*/
public final boolean canReadFile(long offset, long len, int pid)
{
// Check if the lock list is valid
if (m_lockList == null)
return true;
// Check if the file section is readable by the specified process
boolean readOK = false;
synchronized (m_lockList)
{
// Check if the file section is readable
readOK = m_lockList.canReadFile(offset, len, pid);
}
// Return the read status
return readOK;
}
/**
* Check if the file is writeable for the specified section of the file and process id
*
* @param offset long
* @param len long
* @param pid int
* @return boolean
*/
public final boolean canWriteFile(long offset, long len, int pid)
{
// Check if the lock list is valid
if (m_lockList == null)
return true;
// Check if the file section is writeable by the specified process
boolean writeOK = false;
synchronized (m_lockList)
{
// Check if the file section is writeable
writeOK = m_lockList.canWriteFile(offset, len, pid);
}
// Return the write status
return writeOK;
}
/**
* Normalize the path to uppercase the directory names and keep the case of the file name.
*
* @param path String
* @return String
*/
public final static String normalizePath(String path)
{
return path.toUpperCase();
}
/**
* Return the file state as a string
*
* @return String
*/
public String toString()
{
StringBuffer str = new StringBuffer();
str.append("[");
str.append(getPath());
str.append(",");
str.append(getFileStatus());
str.append(":Opn=");
str.append(getOpenCount());
str.append(",Expire=");
str.append(getSecondsToExpire(System.currentTimeMillis()));
str.append(",Locks=");
str.append(numberOfLocks());
str.append(",Ref=");
if ( hasNodeRef())
str.append(getNodeRef().getId());
else
str.append("Null");
if ( isDirectory())
{
str.append(",Pseudo=");
if ( hasPseudoFiles())
str.append(getPseudoFileList().numberOfFiles());
else
str.append(0);
}
str.append("]");
return str.toString();
}
}

View File

@@ -1,464 +0,0 @@
/*
* Copyright (C) 2005 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.smb.server.repo;
import java.util.*;
import java.io.*;
import org.apache.commons.logging.*;
/**
* File State Table Class
* <p>
* Contains an indexed list of the currently open files/folders.
*/
public class FileStateTable implements Runnable
{
private static final Log logger = LogFactory.getLog(FileStateTable.class);
// Initial allocation size for the state cache
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<String, FileState> 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
*/
public FileStateTable()
{
m_stateTable = new Hashtable<String, FileState>(INITIAL_SIZE);
// 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;
}
/**
* Get the file state cache timer, in milliseconds
*
* @return long
*/
public final long getCacheTimer()
{
return m_cacheTimer;
}
/**
* Return the number of states in the cache
*
* @return int
*/
public final int numberOfStates()
{
return m_stateTable.size();
}
/**
* Set the default file state cache timer, in milliseconds
*
* @param tmo long
*/
public final void setCacheTimer(long tmo)
{
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
*
* @param fstate FileState
*/
public final synchronized void addFileState(FileState fstate)
{
// Check if the file state already exists in the cache
if (logger.isDebugEnabled() && m_stateTable.get(fstate.getPath()) != null)
logger.debug("***** addFileState() state=" + fstate.toString() + " - ALREADY IN CACHE *****");
// DEBUG
if (logger.isDebugEnabled() && fstate == null)
{
logger.debug("addFileState() NULL FileState");
return;
}
// Set the file state timeout and add to the cache
fstate.setExpiryTime(System.currentTimeMillis() + getCacheTimer());
m_stateTable.put(fstate.getPath(), fstate);
}
/**
* Find the file state for the specified path
*
* @param path String
* @return FileState
*/
public final synchronized FileState findFileState(String path)
{
return m_stateTable.get(FileState.normalizePath(path));
}
/**
* Find the file state for the specified path, and optionally create a new file state if not
* found
*
* @param path String
* @param isdir boolean
* @param create boolean
* @return FileState
*/
public final synchronized FileState findFileState(String path, boolean isdir, boolean create)
{
// Find the required file state, if it exists
FileState state = m_stateTable.get(FileState.normalizePath(path));
// Check if we should create a new file state
if (state == null && create == true)
{
// Create a new file state
state = new FileState(path, isdir);
// Set the file state timeout and add to the cache
state.setExpiryTime(System.currentTimeMillis() + getCacheTimer());
m_stateTable.put(state.getPath(), state);
}
// Return the file state
return state;
}
/**
* Update the name that a file state is cached under, and the associated file state
*
* @param oldName String
* @param newName String
* @return FileState
*/
public final synchronized FileState updateFileState(String oldName, String newName)
{
// Find the current file state
FileState state = m_stateTable.remove(FileState.normalizePath(oldName));
// Rename the file state and add it back into the cache using the new name
if (state != null)
{
state.setPath(newName);
addFileState(state);
}
// Return the updated file state
return state;
}
/**
* Enumerate the file state cache
*
* @return Enumeration
*/
public final Enumeration enumerate()
{
return m_stateTable.keys();
}
/**
* Remove the file state for the specified path
*
* @param path String
* @return FileState
*/
public final synchronized FileState removeFileState(String path)
{
// Remove the file state from the cache
FileState state = m_stateTable.remove(FileState.normalizePath(path));
// Return the removed file state
return state;
}
/**
* Rename a file state, remove the existing entry, update the path and add the state back into
* the cache using the new path.
*
* @param newPath String
* @param state FileState
*/
public final synchronized void renameFileState(String newPath, FileState state)
{
// Remove the existing file state from the cache, using the original name
m_stateTable.remove(state.getPath());
// Update the file state path and add it back to the cache using the new name
state.setPath(FileState.normalizePath(newPath));
m_stateTable.put(state.getPath(), state);
}
/**
* Remove all file states from the cache
*/
public final synchronized void removeAllFileStates()
{
// Check if there are any items in the cache
if (m_stateTable == null || m_stateTable.size() == 0)
return;
// Enumerate the file state cache and remove expired file state objects
Enumeration enm = m_stateTable.keys();
while (enm.hasMoreElements())
{
// Get the file state
FileState state = m_stateTable.get(enm.nextElement());
// DEBUG
if (logger.isDebugEnabled())
logger.debug("++ Closed: " + state.getPath());
}
// Remove all the file states
m_stateTable.clear();
}
/**
* Remove expired file states from the cache
*
* @return int
*/
public final int removeExpiredFileStates()
{
// Check if there are any items in the cache
if (m_stateTable == null || m_stateTable.size() == 0)
return 0;
// Enumerate the file state cache and remove expired file state objects
Enumeration enm = m_stateTable.keys();
long curTime = System.currentTimeMillis();
int expiredCnt = 0;
while (enm.hasMoreElements())
{
// Get the file state
FileState state = m_stateTable.get(enm.nextElement());
if (state != null && state.hasNoTimeout() == false)
{
synchronized (state)
{
// Check if the file state has expired and there are no open references to the
// file
if (state.hasExpired(curTime) && state.getOpenCount() == 0)
{
// Remove the expired file state
m_stateTable.remove(state.getPath());
// DEBUG
if (logger.isDebugEnabled())
logger.debug("++ Expired file state: " + state);
// Update the expired count
expiredCnt++;
}
}
}
}
// Return the count of expired file states that were removed
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
*/
public final void Dump()
{
// Dump the file state cache entries to the specified stream
if (m_stateTable.size() > 0)
logger.debug("++ FileStateCache Entries:");
Enumeration enm = m_stateTable.keys();
long curTime = System.currentTimeMillis();
while (enm.hasMoreElements())
{
String fname = (String) enm.nextElement();
FileState state = m_stateTable.get(fname);
logger.debug(" ++ " + fname + "(" + state.getSecondsToExpire(curTime) + ") : " + state);
}
}
}

View File

@@ -0,0 +1,331 @@
/*
* Copyright (C) 2005 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.smb.server.repo;
import java.util.Enumeration;
import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.auth.ClientInfo;
import org.alfresco.filesys.server.auth.InvalidUserException;
import org.alfresco.filesys.server.config.InvalidConfigurationException;
import org.alfresco.filesys.server.config.ServerConfiguration;
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;
/**
* Home Share Mapper Class
*
* <p>Maps disk share lookup requests to the list of shares defined in the server
* configuration and provides a dynamic home share mapped to the users home node.
*
* @author GKSpencer
*/
public class HomeShareMapper implements ShareMapper
{
// Logging
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol");
// Home folder share name
public static final String HOME_FOLDER_SHARE = "HOME";
// Server configuration
private ServerConfiguration m_config;
// Home folder share name
private String m_homeShareName = HOME_FOLDER_SHARE;
// Debug enable flag
private boolean m_debug;
/**
* Default constructor
*/
public HomeShareMapper()
{
}
/**
* Initialize the share mapper
*
* @param config ServerConfiguration
* @param params ConfigElement
* @exception InvalidConfigurationException
*/
public void initializeMapper(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException
{
// Save the server configuration
m_config = config;
// Check if the home share name has been specified
String homeName = params.getAttribute("name");
if ( homeName != null && homeName.length() > 0)
m_homeShareName = homeName;
// Check if debug is enabled
if (params != null && params.getChild("debug") != null)
m_debug = true;
}
/**
* Check if debug output is enabled
*
* @return boolean
*/
public final boolean hasDebug()
{
return m_debug;
}
/**
* Return the home folder share name
*
* @return String
*/
public final String getHomeFolderName()
{
return m_homeShareName;
}
/**
* Return the list of available shares.
*
* @param host String
* @param sess SrvSession
* @param allShares boolean
* @return SharedDeviceList
*/
public SharedDeviceList getShareList(String host, SrvSession sess, boolean allShares)
{
// Check if the user has a home folder, and the session does not currently have any
// dynamic shares defined
if ( sess != null && sess.hasClientInformation() && sess.hasDynamicShares() == false)
{
ClientInfo client = sess.getClientInformation();
if ( client.hasHomeFolder())
{
// Create the home folder share
DiskSharedDevice homeShare = createHomeDiskShare(client);
sess.addDynamicShare(homeShare);
// Debug
if ( logger.isDebugEnabled())
logger.debug("Added " + getHomeFolderName() + " share to list of shares for " + client.getUserName());
}
}
// Make a copy of the global share list and add the per session dynamic shares
SharedDeviceList shrList = new SharedDeviceList(m_config.getShares());
if ( sess != null && sess.hasDynamicShares()) {
// Add the per session dynamic shares
shrList.addShares(sess.getDynamicShareList());
}
// Remove unavailable shares from the list and return the list
if ( allShares == false)
shrList.removeUnavailableShares();
return shrList;
}
/**
* Find a share using the name and type for the specified client.
*
* @param host String
* @param name String
* @param typ int
* @param sess SrvSession
* @param create boolean
* @return SharedDevice
* @exception InvalidUserException
*/
public SharedDevice findShare(String tohost, String name, int typ, SrvSession sess, boolean create)
throws Exception
{
// Check for the special HOME disk share
SharedDevice share = null;
if (( typ == ShareType.DISK || typ == ShareType.UNKNOWN) && name.equalsIgnoreCase(getHomeFolderName()) &&
sess.getClientInformation() != null) {
// Get the client details
ClientInfo client = sess.getClientInformation();
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Map share " + name + ", type=" + ShareType.TypeAsString(typ) + ", client=" + client);
// Check if the user has a home folder node
if ( client != null && client.hasHomeFolder()) {
// Check if the share has already been created for the session
if ( sess.hasDynamicShares()) {
// Check if the required share exists in the sessions dynamic share list
share = sess.getDynamicShareList().findShare(name, typ, false);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug(" Reusing existing dynamic share for " + name);
}
// Check if we found a share, if not then create a new dynamic share for the home directory
if ( share == null && create == true) {
// Create the home share mapped to the users home folder
DiskSharedDevice diskShare = createHomeDiskShare(client);
// Add the new share to the sessions dynamic share list
sess.addDynamicShare(diskShare);
share = diskShare;
// DEBUG
if (logger.isDebugEnabled())
logger.debug(" Mapped share " + name + " to " + client.getHomeFolder());
}
}
else
throw new InvalidUserException("No home directory");
}
else {
// Find the required share by name/type. Use a case sensitive search first, if that fails use a case
// insensitive search.
share = m_config.getShares().findShare(name, typ, false);
if ( share == null) {
// Try a case insensitive search for the required share
share = m_config.getShares().findShare(name, typ, true);
}
}
// Check if the share is available
if ( share != null && share.getContext() != null && share.getContext().isAvailable() == false)
share = null;
// Return the shared device, or null if no matching device was found
return share;
}
/**
* Delete temporary shares for the specified session
*
* @param sess SrvSession
*/
public void deleteShares(SrvSession sess)
{
// Check if the session has any dynamic shares
if ( sess.hasDynamicShares() == false)
return;
// Delete the dynamic shares
SharedDeviceList shares = sess.getDynamicShareList();
Enumeration<SharedDevice> enm = shares.enumerateShares();
while ( enm.hasMoreElements()) {
// Get the current share from the list
SharedDevice shr = (SharedDevice) enm.nextElement();
// Close the shared device
shr.getContext().CloseContext();
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Deleted dynamic share " + shr);
}
// Clear the dynamic share list
shares.removeAllShares();
}
/**
* Close the share mapper, release any resources.
*/
public void closeMapper()
{
// TODO Auto-generated method stub
}
/**
* Create a disk share for the home folder
*
* @param client ClientInfo
* @return DiskSharedDevice
*/
private final DiskSharedDevice createHomeDiskShare(ClientInfo client)
{
// Create the disk driver and context
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);
}
}

View File

@@ -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;