/// <summary> /// Open a new output file. /// </summary> /// <remarks>Any existing file will be closed.</remarks> private void OpenFile() { //clear the existing file pointer to make sure if we fail, it's gone. //we also rely on this to distinguish adding a new file to an existing stream. CloseFile(false); //increment our session file counter since we're going to open a new file m_CurrentSessionFile++; //Calculate our candidate file name (with path) based on what we know. string fileNamePath = Path.Combine(m_RepositoryFolder, MakeFileName()); //now double check that the candidate path is unique fileNamePath = FileSystemTools.MakeFileNamePathUnique(fileNamePath); //we now have a unique file name, create the file. FileSystemTools.EnsurePathExists(fileNamePath); m_CurrentFile = new FileStream(fileNamePath, FileMode.CreateNew, FileAccess.Write); //and open a serializer on it m_CurrentSerializer = new GLFWriter(m_CurrentFile, Publisher.SessionSummary, m_CurrentSessionFile, DateTimeOffset.Now); //write out every header packet to the stream ICachedMessengerPacket[] headerPackets = Publisher.HeaderPackets; if (headerPackets != null) { foreach (ICachedMessengerPacket packet in headerPackets) { m_CurrentSerializer.Write(packet); } } //and set a time for us to do our next index update. m_FileExpiration = DateTime.Now.AddSeconds(m_MaxLogDurationSeconds); }
/// <summary> /// Shared logic for creating all streams /// </summary> public static StreamWriter CreateStream(string fileNamePath) { FileSystemTools.EnsurePathExists(fileNamePath); // Create the stream Log.Write(LogMessageSeverity.Verbose, LogCategory, "Creating file to export session data", fileNamePath); return(new StreamWriter(File.Create(fileNamePath))); }
/// <summary> /// Save the package to the specified file name and path, overwriting any existing data /// </summary> /// <param name="progressMonitors"></param> /// <param name="fileNamePath"></param> public void Save(ProgressMonitorStack progressMonitors, string fileNamePath) { if (string.IsNullOrEmpty(fileNamePath)) { throw new ArgumentNullException(nameof(fileNamePath), "Unable to save the current package because no path has been set to save to."); } //normalize the destination fileNamePath = Path.GetFullPath(fileNamePath); //make sure the path exists so we can save into it. FileSystemTools.EnsurePathExists(fileNamePath); lock (m_Lock) { int steps = 2; int completedSteps = 0; using (ProgressMonitor ourMonitor = progressMonitors.NewMonitor(this, "Saving Package", steps)) { //Do the save (if this fails, we failed) ourMonitor.Update("Saving Package File to Disk", completedSteps++); m_Archive.Dispose(); // ...So we need to dispose and reopen the archive. //check to see if we're saving to the same file we *are*... if (fileNamePath.Equals(m_FileNamePath, StringComparison.OrdinalIgnoreCase) == false) { File.Copy(m_FileNamePath, fileNamePath, true); try { File.Delete(m_FileNamePath); } catch (Exception ex) { if (!Log.SilentMode) { Log.Write(LogMessageSeverity.Error, LogWriteMode.Queued, ex, LogCategory, "Unable to Delete Package Temporary File due to " + ex.GetType(), "Unable to delete the temporary working file '{0}'. This means we'll use more disk space than we should but otherwise should not impact the application.", m_FileNamePath); } } m_FileNamePath = fileNamePath; } ourMonitor.Update("Confirming package contents", completedSteps++); //Since we were successful at saving, this is our new path and we are no longer dirty. m_FileNamePath = fileNamePath; m_Archive = ZipFile.Open(m_FileNamePath, ZipArchiveMode.Update); // Now we should again be able to read any entry. LoadIndex(); IsDirty = false; } } }
/// <summary> /// Inheritors should override this method to implement custom initialize functionality. /// </summary> /// <remarks>This method will be called exactly once before any call to OnFlush or OnWrite is made. /// Code in this method is protected by a Thread Lock. /// This method is called with the Message Dispatch thread exclusively.</remarks> protected override void OnInitialize(IMessengerConfiguration configuration) { //do our first time initialization Caption = "Standard File Messenger"; Description = "Messenger implementation that writes messages to files through a buffer. Supports synchronous and asynchronous messaging."; //try to up cast the configuration to our specific configuration type var fileConfiguration = (SessionFileConfiguration)configuration; //If the max file size is unbounded (zero or less) then we want 1GB. m_MaxFileSizeBytes = fileConfiguration.MaxFileSize < 1 ? 1024 : fileConfiguration.MaxFileSize; m_MaxFileSizeBytes = m_MaxFileSizeBytes * 1048576; //the configured value is in MB, we use bytes for faster comparisons m_MaxLogDurationSeconds = fileConfiguration.MaxFileDuration * 60; //the configured value is in minutes, we use seconds for consistency m_RepositoryMaintenanceEnabled = fileConfiguration.EnableFilePruning; m_MaxLocalDiskUsage = fileConfiguration.MaxLocalDiskUsage; m_MaxLocalFileAge = fileConfiguration.MaxLocalFileAge; //what are the very best folders for us to use? m_RepositoryFolder = LocalRepository.CalculateRepositoryPath(Publisher.SessionSummary.Product, fileConfiguration.Folder); m_SessionLockFolder = Path.Combine(m_RepositoryFolder, SessionLockFolderName); //we also have to be sure the path exists now. FileSystemTools.EnsurePathExists(m_RepositoryFolder); FileSystemTools.EnsurePathExists(m_SessionLockFolder); //Since we update the index during a flush, and the index update is about as bad as a flush we look at both together. AutoFlush = true; AutoFlushInterval = Math.Min(fileConfiguration.AutoFlushInterval, fileConfiguration.IndexUpdateInterval); //If we aren't able to initialize our log folder, throw an exception if (string.IsNullOrEmpty(m_RepositoryFolder)) { throw new DirectoryNotFoundException("No log folder could be determined, so the file messenger can't log."); } ScheduleRepositoryMaintenance(0, 0); GetSessionFileLock(); }