/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.jlan.server.filesys.cache;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Hashtable;
import org.alfresco.jlan.debug.Debug;
import org.alfresco.jlan.locking.FileLock;
import org.alfresco.jlan.locking.FileLockList;
import org.alfresco.jlan.locking.LockConflictException;
import org.alfresco.jlan.locking.NotLockedException;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.FileOpenParams;
import org.alfresco.jlan.server.filesys.FileStatus;
import org.alfresco.jlan.smb.SharingMode;
/**
* File State Class
*
* <p>Caches information about a file/directory so that the core server does not need
* to make calls to the shared device driver.
*
* @author gkspencer
*/
public class FileState {
// File state constants
public final static long NoTimeout = -1L;
public final static long DefTimeout = 5 * 60000L; // 5 minutes
public final static int UnknownFileId = -1;
public final static int UnknownStreamCount = -1;
// File status codes
public final static int FILE_LOADWAIT = 0;
public final static int FILE_LOADING = 1;
public final static int FILE_AVAILABLE= 2;
public final static int FILE_UPDATED = 3;
public final static int FILE_SAVEWAIT = 4;
public final static int FILE_SAVING = 5;
public final static int FILE_SAVED = 6;
public final static int FILE_DELETED = 7;
// File state names
private static final String[] _fileStates = { "LoadWait", "Loading", "Available", "Updated", "SaveWait", "Saving", "Saved", "Deleted" };
// Standard file information keys
public static final String FileInformation = "FileInfo";
public static final String StreamsList = "StreamsList";
// File name/path
private String m_path;
// File identifier
private int m_fileId = UnknownFileId;
// 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.
// Constants are defined in the FileStatus class.
private int m_fileStatus;
// File data status
private int m_status = FILE_AVAILABLE;
// Open file count
private int m_openCount;
// Sharing mode
private int m_sharedAccess = SharingMode.READWRITE;
// Cache of various file information
private Hashtable m_cache;
// Count of streams associated with this file, -1 if not known
private int m_streamCount = UnknownStreamCount;
// File lock list, allocated once there are active locks on this file
private FileLockList m_lockList;
// Retention period expiry date/time
private long m_retainUntil = -1L;
/**
* Class constructor
*
* @param fname String
*/
public FileState(String fname) {
// Normalize the file path
setPath(fname);
setExpiryTime(System.currentTimeMillis() + DefTimeout);
// Set the file/folder status
m_fileStatus = FileStatus.Unknown;
}
/**
* Class constructor
*
* @param fname String
* @param status int
*/
public FileState(String fname, int status) {
// Normalize the file path
setPath(fname);
setExpiryTime(System.currentTimeMillis() + DefTimeout);
// Set the file/folder status
m_fileStatus = status;
}
/**
* Return the file name/path
*
* @return String
*/
public final String getPath() {
return m_path;
}
/**
* Return the file exists state
*
* @return boolean
*/
public final boolean fileExists() {
if ( m_fileStatus == FileStatus.FileExists || m_fileStatus == FileStatus.DirectoryExists)
return true;
return false;
}
/**
* Return the file status
*
* @return int
*/
public final int getFileStatus() {
return m_fileStatus;
}
/**
* Return the directory state
*
* @return boolean
*/
public final boolean isDirectory() {
return m_fileStatus == FileStatus.DirectoryExists ? true : false;
}
/**
* Return the file open count
*
* @return int
*/
public final int getOpenCount() {
return m_openCount;
}
/**
* Get the file id
*
* @return int
*/
public final int getFileId() {
return m_fileId;
}
/**
* Return the shared access mode
*
* @return int
*/
public final int getSharedAccess() {
return m_sharedAccess;
}
/**
* Return the file status
*
* @return int
*/
public final int getStatus() {
return m_status;
}
/**
* Return the count of streams associated with this file, or -1 if not known
*
* @return int
*/
public final int getStreamCount() {
return m_streamCount;
}
/**
* 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/folder is under retention
*
* @return boolean
*/
public final boolean hasActiveRetentionPeriod() {
if ( m_retainUntil == -1L)
return false;
return System.currentTimeMillis() < m_retainUntil ? true : false;
}
/**
* Get the retention period expiry date/time for the file/folder
*
* @return long
*/
public final long getRetentionExpiryDateTime() {
return m_retainUntil;
}
/**
* 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() {
m_openCount++;
// Debug
// if ( m_openCount > 1)
// Debug.println("@@@@@ File open name=" + getPath() + ", count=" + m_openCount);
return m_openCount;
}
/**
* Decrement the file open count
*
* @return int
*/
public final synchronized int decrementOpenCount() {
// Debug
if ( m_openCount <= 0)
Debug.println("@@@@@ 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;
}
/**
* Return a file status code as a string
*
* @return String
*/
public final String getStatusAsString() {
if ( m_status >= 0 && m_status < _fileStates.length)
return _fileStates[m_status];
return "Unknown";
}
/**
* Set the file status
*
* @param status int
*/
public final void setFileStatus(int status) {
m_fileStatus = status;
}
/**
* Set the file identifier
*
* @param id int
*/
public final void setFileId(int id) {
m_fileId = id;
}
/**
* Set the file state expiry time
*
* @param expire long
*/
public final void setExpiryTime(long expire) {
m_tmo = expire;
}
/**
* Set the retention preiod expiry date/time
*
* @param expires long
*/
public final void setRetentionExpiryDateTime(long expires) {
m_retainUntil = expires;
}
/**
* 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 status
*
* @param sts int
*/
public final void setStatus(int sts) {
m_status = sts;
}
/**
* Set the associated stream count
*
* @param cnt int
*/
public final synchronized void setStreamCount(int cnt) {
m_streamCount = cnt;
}
/**
* Add an attribute to the file state
*
* @param name String
* @param attr Object
*/
public final synchronized void addAttribute(String name, Object attr) {
if ( m_cache == null)
m_cache = new Hashtable();
m_cache.put(name,attr);
}
/**
* Find an attribute
*
* @param name String
* @return Object
*/
public final Object findAttribute(String name) {
if ( m_cache == null)
return null;
return m_cache.get(name);
}
/**
* Remove an attribute from the file state
*
* @param name String
* @return Object
*/
public final synchronized Object removeAttribute(String name) {
if ( m_cache == null)
return null;
return m_cache.remove(name);
}
/**
* Remove all attributes from the file state
*/
public final synchronized void removeAllAttributes() {
if ( m_cache != null)
m_cache.clear();
m_cache = null;
}
/**
* 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) {
// Split the path into directories and file name, only uppercase the directories to normalize
// the path.
String normPath = path;
if ( path.length() > 3) {
// Split the path to seperate the folders/file name
int pos = path.lastIndexOf(FileName.DOS_SEPERATOR);
if ( pos != -1) {
// Get the path and file name parts, normalize the path
String pathPart = path.substring(0, pos).toUpperCase();
String namePart = path.substring(pos);
// Rebuild the path string
normPath = pathPart + namePart;
}
}
// Return the normalized path
return normPath;
}
/**
* Dump the attributes that are attached to the file state
*
* @param out PrintStream
*/
public final void DumpAttributes(PrintStream out) {
// Check if there are any attributes
if ( m_cache != null) {
// Enumerate the available attribute objects
Enumeration names = m_cache.keys();
while ( names.hasMoreElements()) {
// Get the current attribute name
String name = (String) names.nextElement();
// Get the associated attribute object
Object attrib = m_cache.get(name);
// Output the attribute details
out.println("++ " + name + " : " + attrib);
}
}
else
out.println("++ No Attributes");
}
/**
* 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(FileStatus.asString(getFileStatus()));
str.append(":Opn=");
str.append(getOpenCount());
str.append(",Str=");
str.append(getStreamCount());
str.append(":");
str.append(",Fid=");
str.append(getFileId());
str.append(",Expire=");
str.append(getSecondsToExpire(System.currentTimeMillis()));
str.append(",Sts=");
str.append(_fileStates[getStatus()]);
str.append(",Locks=");
str.append(numberOfLocks());
str.append("]");
return str.toString();
}
}