/* * 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.db; import java.util.ArrayList; import java.util.List; import org.alfresco.jlan.debug.Debug; import org.alfresco.jlan.server.filesys.FileStatus; import org.alfresco.jlan.server.filesys.cache.FileState; import org.alfresco.jlan.server.filesys.cache.FileStateCache; import org.alfresco.jlan.server.filesys.loader.BackgroundFileLoader; import org.alfresco.jlan.server.filesys.loader.CachedFileInfo; import org.alfresco.jlan.server.filesys.loader.FileRequest; import org.alfresco.jlan.server.filesys.loader.FileRequestQueue; import org.alfresco.jlan.server.filesys.loader.FileSegment; import org.alfresco.jlan.server.filesys.loader.FileSegmentInfo; import org.alfresco.jlan.server.filesys.loader.MultipleFileRequest; import org.alfresco.jlan.server.filesys.loader.SingleFileRequest; /** * Background Load Save Class * * <p>Utility class that can be used by FileLoader or DBInterface implementations to provide a worker thread pool * to load/save the file data using a queue of file load/save requests. * * @author gkspencer */ public class BackgroundLoadSave { // Status codes returned from the load/save worker thread processing public final static int StsSuccess = 0; public final static int StsRequeue = 1; public final static int StsError = 2; // Default/minimum/maximum number of worker threads to use public static final int DefaultWorkerThreads = 4; public static final int MinimumWorkerThreads = 1; public static final int MaximumWorkerThreads = 50; // Maximum in-memory request queue size and low water mark public static final int RequestQueueMaxSize = 5000; public static final int RequestQueueMinSize = 50; public static final int RequestQueueDefaultSize = 200; public static final int RequestQueueLowWaterMark = 50; // Minimum number of requests that must be in the in-memory queue when a requeue occurs to continue // without sleeping public static final int RequeueMinSize = 20; public static final long RequeueWaitTime = 500; // milliseconds // Default worker thread prefix private static final String DefaultThreadName = "LoadSave_"; // Attributes attached to the file state public static final String DBFileSegmentInfo = "DBFileSegmentInfo"; // File state timeout values public static final long SequentialFileExpire = 3000L; // milliseconds public static final long RequestProcessedExpire = 3000L; // " public static final long RequestQueuedExpire = 10000L; // " // Transaction timeout default, minimum and maximum values public static final long DefaultTransactionTimeout = 5000L; // milliseconds public static final long MinimumTransactionTimeout = 2000L; // " public static final long MaximumTransactionTimeout = 60000L; // " // Name, used to prefix worker thread names private String m_name; // Queue of file requests private FileRequestQueue m_readQueue; private FileRequestQueue m_writeQueue; // Queue loader threads and lock object private QueueLoader m_writeLoader; private Object m_writeLoaderLock = new Object(); private QueueLoader m_readLoader; private Object m_readLoaderLock = new Object(); private TransactionQueueLoader m_tranLoader; // Maximum in-memory file request size and low water mark private int m_maxQueueSize; private int m_lowQueueSize; // File request worker thread pools private ThreadWorker[] m_readThreads; private ThreadWorker[] m_writeThreads; // Number of worker threads to create for read/write requests private int m_readWorkers; private int m_writeWorkers; // Enable debug output private boolean m_debug; // Database queue interface used to load/save the file requests private DBQueueInterface m_dbQueueInterface; // File state cache and default expiry timeout private FileStateCache m_stateCache; private long m_stateTimeout; // Associated file loader called by the worker threads to do the actual file load/save private BackgroundFileLoader m_fileLoader; /** * Thread Worker Inner Class */ protected class ThreadWorker implements Runnable { // Worker thread private Thread mi_thread; // Worker unique id private int mi_id; // Associated request queue and queue loader private FileRequestQueue mi_queue; private QueueLoader mi_loader; // Shutdown flag private boolean mi_shutdown = false; /** * Class constructor * * @param name String * @param id int * @param queue FileRequestQueue * @param loader QueueLoader */ public ThreadWorker(String name, int id, FileRequestQueue queue, QueueLoader loader) { mi_id = id; mi_queue = queue; mi_loader = loader; mi_thread = new Thread(this); mi_thread.setName(name); mi_thread.setDaemon(true); mi_thread.start(); } /** * Request the worker thread to shutdown */ public final void shutdownRequest() { mi_shutdown = true; try { mi_thread.interrupt(); } catch (Exception ex) { } } /** * Run the thread */ public void run() { // Loop until shutdown FileRequest fileReq = null; while ( mi_shutdown == false) { try { // Wait for a file request to be queued fileReq = mi_queue.removeRequest(); } catch (InterruptedException ex) { // Check for shutdown if ( mi_shutdown == true) break; } // If the file request is valid process it if ( fileReq != null) { // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave loader=" + getName() + ", fileReq=" + fileReq + ", queued=" + mi_queue.numberOfRequests()); // Check if the in-memory file request queue has reached the low water mark, if so then wakeup the queue loader // thread to load more file request records from the database mi_loader.checkRequestQueue(); // Process the file request int reqSts = StsRequeue; try { // Set the thread id of the worker processing the request fileReq.setThreadId(mi_id); // File data load if ( fileReq.isType() == FileRequest.LOAD) { // Load the file reqSts = getFileLoader().loadFile(fileReq); } else if ( fileReq.isType() == FileRequest.SAVE || fileReq.isType() == FileRequest.TRANSSAVE) { // Save the file reqSts = getFileLoader().storeFile(fileReq); } } catch (Exception ex) { // DEBUG if ( Debug.EnableError && hasDebug()) { Debug.println("BackgroundLoadSave exception=" + ex.toString()); Debug.println(ex); } } // Check if the request was processed successfully if ( reqSts == StsSuccess || reqSts == StsError) { try { // Delete the file request from the queue getDBQueueInterface().deleteFileRequest(fileReq); } catch (DBException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println("BackgroundLoadSave Error: " + ex.toString()); } // Reset the associated file state(s) to expire in a short while if ( fileReq instanceof MultipleFileRequest) { // Get the multiple file request details MultipleFileRequest multiReq = (MultipleFileRequest) fileReq; // Calculate the expiry time long expireAt = System.currentTimeMillis() + RequestProcessedExpire; // Reset all the associated file states to expire in a short while for ( int i = 0; i < multiReq.getNumberOfFiles(); i++) { CachedFileInfo finfo = multiReq.getFileInfo(i); if ( finfo.hasFileState()) finfo.getFileState().setExpiryTime(expireAt); } } else { // Reset the associated file state to expire in a short while SingleFileRequest singleReq = (SingleFileRequest) fileReq; if ( singleReq.hasFileState()) singleReq.getFileState().setExpiryTime(System.currentTimeMillis() + RequestProcessedExpire); } // DEBUG if ( Debug.EnableInfo && reqSts == StsError && hasDebug()) Debug.println("BackgroundLoadSave Error request=" + fileReq); } // If the file request was not processed requeue it else if ( reqSts == StsRequeue) { // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave ReQueue request=" + fileReq); // Check if we need to pause, if there are only a few requests in the in-memory queue then we will // sleep for a short while so as not to process the requeued request again too quickly if ( mi_queue.numberOfRequests() < RequeueMinSize) { // Sleep for a while before requeueing the request try { Thread.sleep(RequeueWaitTime); } catch (Exception ex) { } } // Add the request to the end of the in-memory queue mi_queue.addRequest(fileReq); } } } // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave thread=" + mi_thread.getName() + " shutdown"); } }; /** * Queue Loader Thread Inner Class */ protected class QueueLoader implements Runnable { // Queue loader thread private Thread mi_thread; // Request type to load private int mi_loadType; // Shutdown flag private boolean mi_shutdown = false; // Sequence number of the last request loaded private int mi_lastSeqNo; // File request load in progress flag private boolean mi_loading; // Associated queue and lock object private FileRequestQueue mi_queue; private Object mi_lockObj; // Flag to indicate new records have been added to the database since last load private boolean mi_newRecords = true; private int mi_lastQueuedSeq; /** * Class constructor * * @param name String * @param type int * @param queue FileRequestQueue * @param lock Object */ public QueueLoader(String name, int type, FileRequestQueue queue, Object lock) { mi_loadType = type; mi_queue = queue; mi_lockObj = lock; mi_thread = new Thread(this); mi_thread.setName(name); mi_thread.setDaemon(true); mi_thread.start(); } /** * Request the worker thread to shutdown */ public final void shutdownRequest() { mi_shutdown = true; try { mi_thread.interrupt(); } catch (Exception ex) { } } /** * Check if there is a file request record load in progress * * @return boolean */ public final boolean isLoading() { return mi_loading; } /** * Notify the queue loader that a new record has been added to the database * * @param seqNo int */ public final synchronized void notifyNewRecord(int seqNo) { // Update the new record flag mi_newRecords = true; // Update the last queue request sequence number, if higher than the current value if ( seqNo > mi_lastQueuedSeq) mi_lastQueuedSeq = seqNo; // Check if the queue loader thread should be woken up to load new records synchronized (mi_lockObj) { mi_lockObj.notify(); } } /** * Check the request queue status and wakeup the loader thread if required */ public final void checkRequestQueue() { // Check if there is no load active, the queue is below the low water mark and there are // new records to load. int qSize = mi_queue.numberOfRequests(); if ( isLoading() == false && qSize < getLowQueueSize() && (mi_newRecords == true || qSize == 0)) { synchronized (mi_lockObj) { mi_lockObj.notify(); } } } /** * Run the thread */ public void run() { // Create a local queue for loading the file request records before passing to the main queue FileRequestQueue tempQueue = new FileRequestQueue(); // Loop until shutdown while ( mi_shutdown == false) { // Wait for a queue load request try { // Wait for a queue load request by waiting on the lock object if ( mi_queue.numberOfRequests() > 0 || mi_lastQueuedSeq == 0 || mi_lastSeqNo >= mi_lastQueuedSeq) { synchronized ( mi_lockObj) { mi_lockObj.wait(); } } } catch (InterruptedException ex) { // Check for shutdown if ( mi_shutdown == true) break; } // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave Queue load requested, seqNo=" + mi_lastSeqNo); // Load a block of file requests from the queue database int loadCnt = 0; try { // Indicate that there is a load in progress, clear the temporary queue mi_loading = true; tempQueue.removeAllRequests(); // Calculate the number of records to load int recCnt = getMaximumQueueSize() - mi_queue.numberOfRequests(); // Load a block of file request records loadCnt = getDBQueueInterface().loadFileRequests(mi_lastSeqNo, mi_loadType, tempQueue, recCnt); // Check if any records were loaded if ( loadCnt > 0) { // Post process the loaded file request records while ( tempQueue.numberOfRequests() > 0) { // Remove a request from the temporary queue SingleFileRequest fileReq = (SingleFileRequest) tempQueue.removeRequestNoWait(); // Set the last loaded sequence number mi_lastSeqNo = fileReq.getSequenceNumber(); // Determine the initial status for the file state int fsts = fileReq.isType() == FileRequest.LOAD ? FileSegmentInfo.LoadWait : FileSegmentInfo.SaveWait; // Recreate the file state and associated data for the file FileState fstate = null; try { fstate = createFileStateForRequest(fileReq.getFileId(), fileReq.getTemporaryFile(), fileReq.getVirtualPath(), fsts); fileReq.setFileState(fstate); } catch (Exception ex) { Debug.println(ex); } // Add the file request to the main in-memory queue mi_queue.addRequest(fileReq); } // If we loaded less records than expected clear the new record flag if ( loadCnt < recCnt) mi_newRecords = false; // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave Loaded " + loadCnt + " records from queue db, type " + (mi_loadType == FileRequest.LOAD ? "Read" : "Write")); } } catch (DBException ex) { // DEBUG if ( hasDebug()) { Debug.println("BackgroundLoadSave Error " + ex.toString()); Debug.println(" Last SeqNo=" + mi_lastSeqNo); } // Reset the last loaded sequence number mi_lastSeqNo = 0; } // Indicate that records are not being loaded mi_loading = false; } // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave Queue loader shutdown"); } }; /** * Transaction Queue Loader Thread Inner Class */ protected class TransactionQueueLoader implements Runnable { // Transaction queue loader thread private Thread mi_thread; // Shutdown flag private boolean mi_shutdown = false; // File request load in progress flag private boolean mi_loading; // List of transactions that are ready to load from the database private List mi_transList; // Associated request queue private FileRequestQueue mi_queue; /** * Class constructor * * @param name String * @param queue FileRequestQueue */ public TransactionQueueLoader(String name, FileRequestQueue queue) { // Save the associated request queue mi_queue = queue; // Create the transaction name queue mi_transList = new ArrayList(); // Create the thread and start it mi_thread = new Thread(this); mi_thread.setName(name); mi_thread.setDaemon(true); mi_thread.start(); } /** * Request the worker thread to shutdown */ public final void shutdownRequest() { mi_shutdown = true; try { mi_thread.interrupt(); } catch (Exception ex) { } } /** * Add a transaction to the queue, wakeup the transaction loader if required * * @param name String */ public final void addTransaction(String name) { synchronized ( mi_transList) { // Add the transaction to the queue mi_transList.add(name); // Wakeup the loader if this is the only request if ( mi_transList.size() == 1) mi_transList.notify(); } } /** * Check if there is a transaction file request record load in progress * * @return boolean */ public final boolean isLoading() { return mi_loading; } /** * Run the thread */ public void run() { // Loop until shutdown while ( mi_shutdown == false) { // Wait for a queue load request try { // Wait for a transaction load request by waiting on the transaction name queue synchronized ( mi_transList) { if ( mi_transList.size() == 0) { // Debug.println("TransQueueLoader Waiting ..."); mi_transList.wait(); } } } catch (InterruptedException ex) { // Check for shutdown if ( mi_shutdown == true) break; } // Loop until all pending transactions have been loaded while ( mi_transList.size() > 0) { // Get a transaction name from the queue String tranName = null; int tranId = -1; synchronized ( mi_transList) { tranName = (String) mi_transList.remove(0); } if ( Debug.EnableInfo && mi_transList.size() > 0) Debug.println("TransQueueLoader Processing tranName=" + tranName + ", queued=" + mi_transList.size()); // Convert the transaction id try { tranId = Integer.parseInt(tranName); } catch (NumberFormatException ex) { Debug.println(ex); break; } // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave Transaction load requested, tran=" + tranId); try { // Indicate that there is a load in progress mi_loading = true; // Create a multi-file request to hold the details of all the files in the transaction MultipleFileRequest fileReq = new MultipleFileRequest(FileRequest.TRANSSAVE, tranId); // Load the file list for the transaction CachedFileInfo finfo = null; if ( getDBQueueInterface().loadTransactionRequest(fileReq) != null) { // Recreate the file state and associated data for each of the files in the transaction FileState fstate = null; for ( int i = 0; i < fileReq.getNumberOfFiles(); i++) { // Get the current file information from the transaction finfo = fileReq.getFileInfo(i); try { fstate = createFileStateForRequest(finfo.getFileId(), finfo.getTemporaryPath(), finfo.getVirtualPath(), FileSegmentInfo.SaveWait); finfo.setFileState(fstate); } catch (Exception ex) { Debug.println(ex); } } // Add the request to the file request queue mi_queue.addRequest(fileReq); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave Loaded transaction " + fileReq); } } catch (DBException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); } // Indicate request loading completed mi_loading = false; } } // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("BackgroundLoadSave Transaction queue loader shutdown"); } }; /** * Class constructor * * @param dbQueue DBQueueInterface * @param stateCache FileStateCache * @param bgLoader BackgroundFileLoader */ public BackgroundLoadSave(DBQueueInterface dbQueue, FileStateCache stateCache, BackgroundFileLoader bgLoader) { // Save the database queue, state cache and background loader details m_dbQueueInterface = dbQueue; m_stateCache = stateCache; m_fileLoader = bgLoader; // Create the file request queues m_readQueue = new FileRequestQueue(); m_writeQueue = new FileRequestQueue(); // Set the in-memory queue size and low water mark m_maxQueueSize = RequestQueueDefaultSize; m_lowQueueSize = RequestQueueLowWaterMark; // Set the default worker thread prefix setName(DefaultThreadName); } /** * Class constructor * * @param name String * @param dbQueue DBQueueInterface * @param stateCache FileStateCache * @param bgLoader BackgroundFileLoader */ public BackgroundLoadSave(String name, DBQueueInterface dbQueue, FileStateCache stateCache, BackgroundFileLoader bgLoader) { // Save the database queue, state cache and background loader details m_dbQueueInterface = dbQueue; m_stateCache = stateCache; m_fileLoader = bgLoader; // Create the file request queues m_readQueue = new FileRequestQueue(); m_writeQueue = new FileRequestQueue(); // Set the in-memory queue size and low water mark m_maxQueueSize = RequestQueueDefaultSize; m_lowQueueSize = RequestQueueLowWaterMark; // Set the worker thread prefix setName(name); } /** * Start the background load/save thread pool * * @param recCnt int */ public final void startThreads(int recCnt) { // Create the read/write queue loaders m_readLoader = new QueueLoader("ReadQueueLoader", FileRequest.LOAD, m_readQueue, m_readLoaderLock); m_writeLoader = new QueueLoader("WriteQueueLoader", FileRequest.SAVE, m_writeQueue, m_writeLoaderLock); // Create the read thread pool m_readThreads = new ThreadWorker[m_readWorkers]; for ( int i = 0; i < m_readWorkers; i++) m_readThreads[i] = new ThreadWorker(getName() + "_RD_" + (i+1), i, m_readQueue, m_readLoader); // Create the write thread pool m_writeThreads = new ThreadWorker[m_writeWorkers]; for ( int i = 0; i < m_writeWorkers; i++) m_writeThreads[i] = new ThreadWorker(getName() + "_WR_" + (i+1), i, m_writeQueue, m_writeLoader); // DEBUG if ( Debug.EnableInfo && hasDebug()) Debug.println("FileLoader threadPool read=" + m_readWorkers + ", write=" + m_writeWorkers); // If there are recovered files queued for saving then kick the queue loader if ( recCnt > 0) m_writeLoader.notifyNewRecord( 1); } /** * Enable transactions, start the transaction loader thread */ public final void enableTransactions() { // Check if the transaction loader thread is valid if ( m_tranLoader == null) m_tranLoader = new TransactionQueueLoader(getName() + "_TranLdr", m_writeQueue); } /** * Shutdown the background load/save thread pool */ public final void shutdownThreads() { // Shutdown the worker threads if ( m_readThreads != null) { for ( int i = 0; i < m_readThreads.length; i++) m_readThreads[i].shutdownRequest(); } if ( m_writeThreads != null) { for ( int i = 0; i < m_writeThreads.length; i++) m_writeThreads[i].shutdownRequest(); } // Shutdown the queue loaders m_readLoader.shutdownRequest(); m_readLoader = null; m_writeLoader.shutdownRequest(); m_writeLoader = null; // Shutdown the transaction loader, if active if ( m_tranLoader != null) { m_tranLoader.shutdownRequest(); m_tranLoader = null; } } /** * Request file data to be loaded/saved * * @param req FileRequest */ public void queueFileRequest(FileRequest req) { // Make sure the associated file state stays in memory for a short time, if the queue is small // the request may get processed soon. if ( req instanceof SingleFileRequest) { // Get the request details SingleFileRequest fileReq = (SingleFileRequest) req; if ( fileReq.hasFileState()) fileReq.getFileState().setExpiryTime(FileState.NoTimeout); try { // Write a file request record to the queue database getDBQueueInterface().queueFileRequest(fileReq); // Check if the request is part of a transaction, or a standalone request if ( fileReq.isTransaction() == false) { // Check if the in-memory queue is empty, if so then wakeup the queue loader to load the new request if ( fileReq.isType() == FileRequest.LOAD) m_readLoader.notifyNewRecord(fileReq.getSequenceNumber()); else m_writeLoader.notifyNewRecord(fileReq.getSequenceNumber()); } else { // // Check if this request is the last file in the current transaction, if so then the transaction // is ready to be processed if ( fileReq.isLastTransactionFile()) m_tranLoader.addTransaction("" + fileReq.getTransactionId()); } } catch (DBException ex) { // DEBUG if ( Debug.EnableError && hasDebug()) Debug.println(ex); } } } /** * Flush the current pending transaction request * * @param tranId int */ public final void flushTransaction(int tranId) { // Queue the specified transaction for loading/processing m_tranLoader.addTransaction("" + tranId); } /** * Check if debug output is enabled * * @return boolean */ public final boolean hasDebug() { return m_debug; } /** * Return the database queue interface * * @return DBQueueInterface */ public final DBQueueInterface getDBQueueInterface() { return m_dbQueueInterface; } /** * Return the file loader interface * * @return BackgroundFileLoader */ public final BackgroundFileLoader getFileLoader() { return m_fileLoader; } /** * Return the default file state timeout * * @return int */ public final long getFileStateTimeout() { return m_stateTimeout; } /** * Return the maximum in-memory file request queue size * * @return int */ public final int getMaximumQueueSize() { return m_maxQueueSize; } /** * Return the in-memory file request queue low water mark level * * @return int */ public final int getLowQueueSize() { return m_lowQueueSize; } /** * Return the worker thread prefix * * @return String */ public final String getName() { return m_name; } /** * Return the read request queue * * @return FileRequestQueue */ protected final FileRequestQueue getReadQueue() { return m_readQueue; } /** * Return the read loader * * @return QueueLoader */ public final QueueLoader getReadLoader() { return m_readLoader; } /** * Return the write request queue * * @return FileRequestQueue */ protected final FileRequestQueue getWriteQueue() { return m_writeQueue; } /** * Return the write loader * * @return QueueLoader */ public final QueueLoader getWriteLoader() { return m_writeLoader; } /** * Return the number of read worker threads * * @return int */ public final int getReadWorkers( ) { return m_readWorkers; } /** * Return the number of write worker threads * * @return int */ public final int getWriteWorkers( ) { return m_writeWorkers; } /** * Return the file state cache * * @return FileStateCache */ protected final FileStateCache getStateCache() { return m_stateCache; } /** * Set the worker thread name prefix * * @param name String */ public final void setName(String name) { m_name = name; } /** * Enable/disable debug output * * @param dbg boolean */ public final void setDebug(boolean dbg) { m_debug = dbg; } /** * Set the maximum in-memory file request queue size * * @param qsize int */ public final void setMaximumQueueSize(int qsize) { m_maxQueueSize = qsize; } /** * Set the in-memory file request queue low water mark level * * @param lowqSize */ public final void setLowQueueSize(int lowqSize) { m_lowQueueSize = lowqSize; } /** * Set the number of read worker threads * * @param rdWorkers int */ public final void setReadWorkers( int rdWorkers) { m_readWorkers = rdWorkers; } /** * Set the number of write worker threads * * @param wrWorkers int */ public final void setWriteWorkers( int wrWorkers) { m_writeWorkers = wrWorkers; } /** * Re-create, or attach, a file request to the file state. * * @param fid int * @param tempPath String * @param virtPath String * @param sts int * @return FileState */ protected final FileState createFileStateForRequest(int fid, String tempPath, String virtPath, int sts) { // Find, or create, the file state for the file/directory FileState state = m_stateCache.findFileState(virtPath, false); if ( state == null) { // Create a new file state for the temporary file state = m_stateCache.findFileState(virtPath, true); synchronized ( state) { // Prevent the file state from expiring whilst the request is queued against it state.setExpiryTime(FileState.NoTimeout); // Indicate that the file exists, set the unique file id state.setFileStatus( FileStatus.FileExists); state.setFileId(fid); // Check if the file segment has been attached to the file state FileSegmentInfo fileSegInfo = (FileSegmentInfo) state.findAttribute(DBFileSegmentInfo); FileSegment fileSeg = null; if ( fileSegInfo == null) { // Create a new file segment fileSegInfo = new FileSegmentInfo(); fileSegInfo.setTemporaryFile(tempPath); fileSeg = new FileSegment(fileSegInfo, true); fileSeg.setStatus(sts, true); // Add the segment to the file state cache state.addAttribute(DBFileSegmentInfo, fileSegInfo); } else { // Make sure the file segment indicates its part of a queued request fileSeg = new FileSegment(fileSegInfo, true); fileSeg.setStatus(sts, true); } } } // Return the file state return state; } }