/* * 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.server.state; 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.server.pseudo.PseudoFile; import org.alfresco.filesys.server.pseudo.PseudoFileList; import org.alfresco.filesys.smb.SharingMode; import org.alfresco.service.cmr.repository.NodeRef; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * File State Class * *

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 + " <>"); 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(); } }