@Override public void closeFile(SrvSession sess, TreeConnection tree, NetworkFile param) throws IOException { ContentContext tctx = (ContentContext) tree.getContext(); try { diskInterface.closeFile(sess, tree, param); if (tctx.hasStateCache()) { FileStateCache cache = tctx.getStateCache(); FileState fstate = cache.findFileState(param.getFullName(), true); if (fstate != null && param.getAccessToken() != null) { FileAccessToken token = param.getAccessToken(); if (logger.isDebugEnabled() && token != null) { logger.debug("close file release lock token:" + token); } cache.releaseFileAccess(fstate, token); } } } catch (IOException ie) { if (logger.isDebugEnabled()) { logger.debug("close file exception caught", ie); } throw ie; } }
/** * 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, 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; }
/** * Delete the specified file data * * @param fname String * @param fid int * @param stid int * @exception IOException */ public void deleteFile(String fname, int fid, int stid) throws IOException { // Delete the file data from the database try { // Find the associated file state FileState fstate = m_stateCache.findFileState(fname, false); if (fstate != null) { // Get the file segment details FileSegmentInfo fileSegInfo = (FileSegmentInfo) fstate.removeAttribute(DBFileSegmentInfo); if (fileSegInfo != null) { try { // Change the file segment status fileSegInfo.setQueued(false); fileSegInfo.setStatus(FileSegmentInfo.Initial); // Delete the temporary file fileSegInfo.deleteTemporaryFile(); } catch (Exception ex) { // DEBUG if (Debug.EnableInfo && hasDebug()) Debug.println( "## ObjIdLoader failed to delete temp file " + fileSegInfo.getTemporaryFile()); } } } } catch (Exception ex) { // DEBUG if (Debug.EnableInfo && hasDebug()) Debug.println("## ObjIdLoader deleteFile() error, " + ex.toString()); } }
/** * Find the file segment for the specified virtual path * * @param virtPath String * @return FileSegment */ protected final FileSegment findFileSegmentForPath(String virtPath) { // Get the file state for the virtual path FileState fstate = m_stateCache.findFileState(virtPath, false); if (fstate == null) return null; // Get the file segment FileSegmentInfo segInfo = null; FileSegment fileSeg = null; synchronized (fstate) { // Get the associated file segment segInfo = (FileSegmentInfo) fstate.findAttribute(DBFileSegmentInfo); fileSeg = new FileSegment(segInfo, true); } // Return the file segment return fileSeg; }
@Override public NetworkFile createFile(SrvSession sess, TreeConnection tree, FileOpenParams params) throws IOException { ContentContext tctx = (ContentContext) tree.getContext(); FileAccessToken token = null; if (tctx.hasStateCache()) { FileStateCache cache = tctx.getStateCache(); FileState fstate = tctx.getStateCache().findFileState(params.getPath(), true); token = cache.grantFileAccess(params, fstate, FileStatus.NotExist); if (logger.isDebugEnabled()) { logger.debug("create file created lock token:" + token); } } try { NetworkFile newFile = diskInterface.createFile(sess, tree, params); if (tctx.hasStateCache()) { FileState fstate = tctx.getStateCache().findFileState(params.getPath(), true); fstate.setProcessId(params.getProcessId()); fstate.setSharedAccess(params.getSharedAccess()); // Indicate that the file is open fstate.setFileStatus( newFile.isDirectory() ? FileStatus.DirectoryExists : FileStatus.FileExists); fstate.setAllocationSize(params.getAllocationSize()); if (newFile instanceof NodeRefNetworkFile) { NodeRefNetworkFile x = (NodeRefNetworkFile) newFile; x.setFileState(fstate); } if (newFile instanceof TempNetworkFile) { TempNetworkFile x = (TempNetworkFile) newFile; x.setFileState(fstate); } } if (newFile instanceof NodeRefNetworkFile) { NodeRefNetworkFile x = (NodeRefNetworkFile) newFile; x.setProcessId(params.getProcessId()); x.setAccessToken(token); } if (newFile instanceof TempNetworkFile) { TempNetworkFile x = (TempNetworkFile) newFile; x.setAccessToken(token); } return newFile; } catch (IOException ie) { if (logger.isDebugEnabled()) { logger.debug("create file exception caught", ie); } if (tctx.hasStateCache() && token != null) { FileStateCache cache = tctx.getStateCache(); FileState fstate = tctx.getStateCache().findFileState(params.getPath(), false); if (fstate != null && token != null) { if (logger.isDebugEnabled()) { logger.debug("create file release lock token:" + token); } cache.releaseFileAccess(fstate, token); } } throw ie; } }
@Override public NetworkFile openFile(SrvSession sess, TreeConnection tree, FileOpenParams params) throws IOException { ContentContext tctx = (ContentContext) tree.getContext(); String path = params.getPath(); FileAccessToken token = null; if (tctx.hasStateCache()) { if (!params.isDirectory()) { FileStateCache cache = tctx.getStateCache(); FileState fstate = tctx.getStateCache().findFileState(params.getPath(), true); token = cache.grantFileAccess(params, fstate, FileStatus.Unknown); if (logger.isDebugEnabled()) { logger.debug("open file created lock token:" + token); } } } try { NetworkFile openFile = diskInterface.openFile(sess, tree, params); if (openFile instanceof ContentNetworkFile) { ContentNetworkFile x = (ContentNetworkFile) openFile; x.setProcessId(params.getProcessId()); x.setAccessToken(token); if (tctx.hasStateCache()) { FileState fstate = tctx.getStateCache().findFileState(path, true); x.setFileState(fstate); fstate.setProcessId(params.getProcessId()); fstate.setSharedAccess(params.getSharedAccess()); fstate.setFileStatus(FileStatus.FileExists); } } if (openFile instanceof TempNetworkFile) { TempNetworkFile x = (TempNetworkFile) openFile; x.setAccessToken(token); // x.setProcessId( params.getProcessId()); if (tctx.hasStateCache()) { FileState fstate = tctx.getStateCache().findFileState(path, true); x.setFileState(fstate); fstate.setFileStatus(FileStatus.FileExists); fstate.setProcessId(params.getProcessId()); fstate.setSharedAccess(params.getSharedAccess()); } } if (openFile instanceof AlfrescoFolder) { AlfrescoFolder x = (AlfrescoFolder) openFile; // x.setProcessId( param.getProcessId()); if (tctx.hasStateCache()) { FileState fstate = tctx.getStateCache().findFileState(path, true); x.setFileState(fstate); fstate.setFileStatus(FileStatus.DirectoryExists); fstate.setProcessId(params.getProcessId()); fstate.setSharedAccess(params.getSharedAccess()); } } return openFile; } catch (IOException ie) { if (logger.isDebugEnabled()) { logger.debug("open file exception caught", ie); } if (tctx.hasStateCache() && token != null) { FileStateCache cache = tctx.getStateCache(); FileState fstate = tctx.getStateCache().findFileState(params.getPath(), false); if (fstate != null) { if (logger.isDebugEnabled()) { logger.debug("open file release lock token:" + token); } cache.releaseFileAccess(fstate, token); } } throw ie; } }
/** * Close the network file * * @param sess SrvSession * @param netFile NetworkFile * @exception IOException */ public void closeFile(SrvSession sess, NetworkFile netFile) throws IOException { // Close the cached network file if (netFile instanceof CachedNetworkFile) { // Get the cached network file CachedNetworkFile cacheFile = (CachedNetworkFile) netFile; cacheFile.closeFile(); // Get the file segment details FileSegment fileSeg = cacheFile.getFileSegment(); // Check if the file data has been updated, if so then queue a file save if (fileSeg.isUpdated() && netFile.hasDeleteOnClose() == false) { // Set the modified date/time and file size for the file File tempFile = new File(fileSeg.getTemporaryFile()); netFile.setModifyDate(tempFile.lastModified()); netFile.setFileSize(tempFile.length()); // Queue a file save request to save the data back to the repository, if not already queued if (fileSeg.isSaveQueued() == false) { // Create a file save request for the updated file segment SingleFileRequest fileReq = new SingleFileRequest( FileRequest.SAVE, cacheFile.getFileId(), cacheFile.getStreamId(), fileSeg.getInfo(), netFile.getFullName(), cacheFile.getFileState()); // Check if there are any attributes to be added to the file request if (hasRequiredAttributes() && sess != null) { // Check if the user name is required if (m_reqAttributes.containsString(FileRequest.AttrUserName)) { // Add the user name attribute ClientInfo cInfo = sess.getClientInformation(); String userName = ""; if (cInfo != null && cInfo.getUserName() != null) userName = cInfo.getUserName(); fileReq.addAttribute(new NameValue(FileRequest.AttrUserName, userName)); } // Check if the protocol type is required if (m_reqAttributes.containsString(FileRequest.AttrProtocol)) { // Add the protocol type attribute fileReq.addAttribute(new NameValue(FileRequest.AttrProtocol, sess.getProtocolName())); } } // Set the file segment status fileSeg.setStatus(FileSegmentInfo.SaveWait, true); // Queue the file save request queueFileRequest(fileReq); } else if (Debug.EnableInfo && hasDebug()) { // DEBUG Debug.println("## FileLoader Save already queued for " + fileSeg); } } // Update the cache timeout for the temporary file if there are no references to the file. If // the file was // opened for sequential access only it will be expired quicker. else if (cacheFile.getFileState().getOpenCount() == 0) { // If the file was opened for sequential access only then we can delete it from the // temporary area // sooner long tmo = System.currentTimeMillis(); if (cacheFile.isSequentialOnly()) tmo += SequentialFileExpire; else tmo += m_stateCache.getFileStateExpireInterval(); // Set the file state expiry, the local file data will be deleted when the file state // expires (if there // are still no references to the file). cacheFile.getFileState().setExpiryTime(tmo); } // If the database is not online and the file is marked for delete then queue a delete file // request to do // the // delete when the database is back online if (m_dbCtx.isAvailable() == false && netFile.hasDeleteOnClose()) { // Queue an offline delete request for the file DeleteFileRequest deleteReq = new DeleteFileRequest( cacheFile.getFileId(), cacheFile.getStreamId(), fileSeg.getTemporaryFile(), cacheFile.getFullNameStream(), cacheFile.getFileState()); m_dbCtx.addOfflineFileDelete(deleteReq); // DEBUG if (Debug.EnableInfo && hasDebug()) Debug.println("## FileLoader queued offline delete, " + deleteReq); } } }
/** * Create a network file for the specified file * * @param params FileOpenParams * @param fid int * @param stid int * @param did int * @param create boolean * @param dir boolean * @exception IOException * @exception FileNotFoundException */ public NetworkFile openFile( FileOpenParams params, int fid, int stid, int did, boolean create, boolean dir) throws IOException, FileNotFoundException { // Split the file name to get the name only String[] paths = FileName.splitPath(params.getPath()); String name = paths[1]; if (params.isStream()) name = name + params.getStreamName(); // Find, or create, the file state for the file/directory FileState fstate = m_stateCache.findFileState(params.getFullPath(), true); fstate.setExpiryTime(System.currentTimeMillis() + m_stateCache.getFileStateExpireInterval()); // Check if the file is a directory DBNetworkFile netFile = null; if (dir == false) { // Create the network file and associated file segment CachedNetworkFile cacheFile = createNetworkFile(fstate, params, name, fid, stid, did); netFile = cacheFile; // Check if the file is being opened for sequential access and the data has not yet been // loaded FileSegment fileSeg = cacheFile.getFileSegment(); if (create == true || params.isOverwrite() == true) { // Indicate that the file data is available, this is a new file or the existing file is // being // overwritten so there is no data to load. fileSeg.setStatus(FileSegmentInfo.Available); } else if (params.isSequentialAccessOnly() && fileSeg.isDataLoading() == false) { synchronized (cacheFile.getFileState()) { // Create the temporary file cacheFile.openFile(create); cacheFile.closeFile(); // Queue a file data load request if (fileSeg.isDataLoading() == false) queueFileRequest( new SingleFileRequest( FileRequest.LOAD, cacheFile.getFileId(), cacheFile.getStreamId(), fileSeg.getInfo(), cacheFile.getFullName(), fstate)); } // DEBUG if (Debug.EnableInfo && hasDebug()) Debug.println( "ObjIdLoader Queued file load, SEQUENTIAL access, file=" + cacheFile.getFullName()); } } else { // Create a placeholder network file for the directory netFile = new DirectoryNetworkFile(name, fid, did, m_stateCache.getFileStateProxy(fstate)); // Debug if (Debug.EnableInfo && hasDebug()) Debug.println("ObjIdLoader.openFile() DIR name=" + name + ", state=" + fstate); } // Return the network file return netFile; }
/** * Create a file segment to load/save the file data * * @param state FileState * @param params FileOpenParams * @param fname String * @param fid int * @param stid int * @param did int * @return CachedNetworkFile * @exception IOException */ private final CachedNetworkFile createNetworkFile( FileState state, FileOpenParams params, String fname, int fid, int stid, int did) throws IOException { // The file state is used to synchronize the creation of the file segment as there may be other // sessions opening the file at the same time. We have to be careful that only one thread // creates the // file segment. FileSegment fileSeg = null; CachedNetworkFile netFile = null; synchronized (state) { // Check if the file segment has been attached to the file state FileSegmentInfo fileSegInfo = (FileSegmentInfo) state.findAttribute(DBFileSegmentInfo); if (fileSegInfo == null) { // Check if we need to create a new temporary sub-drectory if (m_tempCount++ >= m_tempMax) createNewTempDirectory(); // Create a unique temporary file name StringBuffer tempName = new StringBuffer(); tempName.append(getTempFilePrefix()); tempName.append(fid); if (stid > 0) { tempName.append("_"); tempName.append(stid); // DEBUG if (Debug.EnableInfo && hasDebug()) Debug.println("## Temp file for stream ##"); } tempName.append(".tmp"); // Create a new file segment fileSegInfo = new FileSegmentInfo(); fileSeg = FileSegment.createSegment( fileSegInfo, tempName.toString(), m_curTempDir, params.isReadOnlyAccess() == false); // Add the segment to the file state cache state.addAttribute(DBFileSegmentInfo, fileSegInfo); // Check if the file is zero length, if so then set the file segment state to indicate it is // available DBFileInfo finfo = (DBFileInfo) state.findAttribute(FileState.FileInformation); if (finfo != null && finfo.getSize() == 0) fileSeg.setStatus(FileSegmentInfo.Available); } else { // Create the file segment to map to the existing temporary file fileSeg = new FileSegment(fileSegInfo, params.isReadOnlyAccess() == false); // Check if the temporary file exists, if not then create it File tempFile = new File(fileSeg.getTemporaryFile()); if (tempFile.exists() == false) { // Create the temporary file tempFile.createNewFile(); // Reset the file segment state to indicate a file load is required fileSeg.setStatus(FileSegmentInfo.Initial); } } // Create the new network file netFile = new CachedNetworkFile( fname, fid, stid, did, m_stateCache.getFileStateProxy(state), fileSeg, this); netFile.setGrantedAccess( params.isReadOnlyAccess() ? NetworkFile.READONLY : NetworkFile.READWRITE); netFile.setSequentialOnly(params.isSequentialAccessOnly()); netFile.setAttributes(params.getAttributes()); netFile.setFullName(params.getPath()); if (stid != 0) netFile.setStreamName(params.getStreamName()); } // Return the network file return netFile; }
/** * File state has expired. The listener can control whether the file state is removed from the * cache, or not. * * @param state FileState * @return true to remove the file state from the cache, or false to leave the file state in the * cache */ public boolean fileStateExpired(FileState state) { // Check if the file state has an associated file segment FileSegmentInfo segInfo = (FileSegmentInfo) state.findAttribute(DBFileSegmentInfo); boolean expire = true; if (segInfo != null) { // Check if the file has a request queued if (segInfo.isQueued() == false) { try { // Delete the temporary file and reset the segment status so that the data may be loaded // again // if required. if (segInfo.hasStatus() != FileSegmentInfo.Initial) { // Delete the temporary file try { segInfo.deleteTemporaryFile(); } catch (IOException ex) { // DEBUG if (Debug.EnableError) { Debug.println("Delete temp file error: " + ex.toString()); File tempFile = new File(segInfo.getTemporaryFile()); Debug.println( " TempFile file=" + tempFile.getAbsolutePath() + ", exists=" + tempFile.exists()); Debug.println(" FileState state=" + state); Debug.println(" FileSegmentInfo segInfo=" + segInfo); Debug.println(" StateCache size=" + m_stateCache.numberOfStates()); } } // Remove the file segment, reset the file segment back to the initial state state.removeAttribute(DBFileSegmentInfo); segInfo.setStatus(FileSegmentInfo.Initial); // Reset the file state to indicate file data load required state.setDataStatus(FileState.FILE_LOADWAIT); // Check if the temporary file sub-directory is now empty, and it is not the current // temporary // sub-directory if (segInfo.getTemporaryFile().startsWith(m_curTempName) == false) { // Check if the sub-directory is empty File tempFile = new File(segInfo.getTemporaryFile()); File subDir = tempFile.getParentFile(); String[] files = subDir.list(); if (files == null || files.length == 0) { // Delete the sub-directory subDir.delete(); // DEBUG if (Debug.EnableInfo && hasDebug()) Debug.println( "$$ Deleted temporary directory " + subDir.getPath() + ", curTempName=" + m_curTempName + ", tempFile=" + segInfo.getTemporaryFile()); } } // Indicate that the file state should not be deleted expire = false; // Debug if (Debug.EnableInfo && hasDebug()) Debug.println( "$$ Deleted temporary file " + segInfo.getTemporaryFile() + " [EXPIRED] $$"); } // If the file state is not to be deleted reset the file state expiration timer if (expire == false) state.setExpiryTime( System.currentTimeMillis() + m_stateCache.getFileStateExpireInterval()); } catch (Exception ex) { // DEBUG if (Debug.EnableError) { Debug.println("$$ " + ex.toString()); Debug.println(" state=" + state); } } } else { // DEBUG if (hasDebug()) { File tempFile = new File(segInfo.getTemporaryFile()); if (tempFile.exists() == false) { Debug.println("== Skipped file state, queued " + state); Debug.println(" File seg=" + segInfo); } } // File state is queued, do not expire expire = false; } } else if (state.isDirectory()) { // Nothing to do when it's a directory, just allow it to expire expire = true; } else if (Debug.EnableInfo && hasDebug()) Debug.println("$$ Expiring state=" + state); // Return true if the file state can be expired return expire; }
/** * Start the file loader * * @param ctx DeviceContext */ public void startLoader(DeviceContext ctx) { // Get the file state cache from the context m_stateCache = getContext().getStateCache(); // Add the file loader as a file state listener so that we can cleanup temporary data files m_stateCache.addStateListener(this); // Get the database interface DBQueueInterface dbQueue = null; if (getContext().getDBInterface() instanceof DBQueueInterface) dbQueue = (DBQueueInterface) getContext().getDBInterface(); else throw new RuntimeException("Database interface does not implement queue interface"); // Perform a queue cleanup before starting the thread pool. This will check the temporary cache // area and delete // files that are not part of a queued save/transaction save request. FileRequestQueue recoveredQueue = null; try { // Cleanup the temporary cache area and queue recoveredQueue = dbQueue.performQueueCleanup(m_tempDir, TempDirPrefix, TempFilePrefix, JarFilePrefix); // DEBUG if (recoveredQueue != null && Debug.EnableInfo && hasDebug()) Debug.println( "[DBLoader] Cleanup recovered " + recoveredQueue.numberOfRequests() + " pending save files"); } catch (DBException ex) { // DEBUG if (Debug.EnableError && hasDebug()) Debug.println(ex); } // Check if there are any file save requests pending in the queue database FileRequestQueue saveQueue = new FileRequestQueue(); try { dbQueue.loadFileRequests(1, FileRequest.SAVE, saveQueue, 1); } catch (DBException ex) { } // Create the background load/save thread pool m_backgroundLoader = new BackgroundLoadSave("DBLdr", dbQueue, m_stateCache, this); m_backgroundLoader.setReadWorkers(m_readWorkers); m_backgroundLoader.setWriteWorkers(m_writeWorkers); m_backgroundLoader.setDebug(m_threadDebug); // Start the file loader threads, start the request loading if there are pending file save // requests m_backgroundLoader.startThreads(saveQueue.numberOfRequests()); // Queue the recovered file save requests if (recoveredQueue != null) { // Queue the file save requests while (recoveredQueue.numberOfRequests() > 0) queueFileRequest(recoveredQueue.removeRequestNoWait()); } }