/// <summary> /// Create new instance. /// </summary> /// <param name="targetFolder"> /// Target repository folder. /// </param> /// <param name="backwards"> /// Whether to iterate backwards. /// </param> /// <exception cref="ObjectDisposedException"> /// The target repository instance is disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// The <paramref name="targetFolder"/> is detached. /// </exception> public DataFileIterator(IRepositoryFolder targetFolder, bool backwards) { RepositoryFolder.CheckNotDetached(targetFolder); _targetFolder = RepositoryFolder.CastFolder(targetFolder); _backwards = backwards; Current = NextForward = NextBackwards = Next = Previous = null; }
/// <summary> /// Add folder to read and optionally prepare it for reading /// </summary> /// <param name="folder"> /// Folder to add /// </param> /// <param name="position"> /// <see langword="null"/> means do not prepare it for reading /// </param> /// <returns> /// <see langword="false"/> - the folder is already being read /// <see langword="true"/> otherwise /// </returns> /// <remarks> /// If reader has data (<see cref="HasData"/>) <paramref name="position"/> must have value /// </remarks> private bool AddFolderImpl(IRepositoryFolder folder, IFolderReadingPosition position) { Check.RequireArgumentNotNull(folder, "folder"); IFolder folderTyped = RepositoryFolder.CastFolder(folder); Check.Require(object.ReferenceEquals(folder.Repository, Repository)); Check.Require(!HasData || position != null, "If we have data we cannot leave a reader unpositioned"); if (IsAccessing(folder, false)) { return(false); } Check.Require(!_position.FolderPositions.ContainsKey(folder.FolderKey) , "Folder position found in repository reader position for a folder not being read"); RepositoryFolderReader reader = new RepositoryFolderReader(folderTyped, this); reader.Direction = this.Direction; _readers.Add(folder.FolderKey, reader); if (position != null) { SeekFolderReader(reader, position); } return(true); }
private static List <T> GetDataAccessors <T>( LinkedList <Util.WeakReferenceT <T> > list , IRepositoryFolder folder , bool subtree , bool firstOnly) where T : class, IRepositoryDataAccessor { List <T> retval = new List <T>(); for (LinkedListNode <Util.WeakReferenceT <T> > node = list.First; node != null;) { LinkedListNode <Util.WeakReferenceT <T> > nextNode = node.Next; IRepositoryDataAccessor accessor = node.Value.Target; if (accessor == null) { _logger.Info("Purging dead accessor"); list.Remove(node); } else { if (accessor.IsAccessing(RepositoryFolder.CastFolder(folder, "folder"), subtree)) { _logger.InfoFormat("GetDataAccessors found alive writer for {0}", folder.LogicalPath); retval.Add((T)accessor); if (firstOnly) { break; } } } node = nextNode; } return(retval); }
/// <summary> /// Check read and/or write access status to the <paramref name="folder"/>. /// </summary> /// <param name="folder"> /// Folder to check current access to. /// </param> /// <param name="subtree"> /// Whether to check access to all folders in the subtree or only to the <paramref name="folder"/> itself /// </param> /// <param name="read"> /// Whether to check read access, i.e. whether target folder[s] is/are being read from /// </param> /// <param name="write"> /// Whether to check write access, i.e. whether target folder[s] is/are being written to /// </param> /// <remarks> /// Access to readers and writers registry is synchronised; all concurrent calls accessing registry of readers and writers /// will wait until this method finishes. /// </remarks> private bool IsDataBeingAccessed(IRepositoryFolder folder, bool subtree, bool write, bool read) { Exceptions.DifferentRepositoriesExceptionHelper.Check(this, folder.Repository); Util.Check.Require(write || read); IFolder f = RepositoryFolder.CastFolder(folder, "folder"); bool retval = false; _dataAccessorRegistryLock.EnterReadLock(); try { if (read) { retval = IsDataBeingAccessed(_readers, f, subtree); } if (!retval && write) { retval = IsDataBeingAccessed(_writers, f, subtree); } } finally { _dataAccessorRegistryLock.ExitReadLock(); } return(retval); }
/// <summary> /// /// </summary> /// <param name="targetFolder"></param> /// <remarks> /// Constructor is internal because all readers and writers are created in the <see cref="IRepositoryManager.Objectactory /> /// to be able to handle concurrency and provide own/different implementation. /// </remarks> internal FolderDataAccessor(IRepositoryFolder targetFolder) { Check.DoRequireArgumentNotNull(targetFolder, "targetFolder"); Check.DoAssertLambda(!targetFolder.IsDetached, () => new ArgumentException(StorageResources.FolderIsNotPartOfARepository)); _targetFolder = RepositoryFolder.CastFolder(targetFolder); _iterator = Repository.ObjectFactory.GetDataFileIterator(targetFolder, false); Check.Ensure(Repository.IsDataBeingAccessed(Folder, false)); }
/// <summary> /// Get writer for this repository /// </summary> /// <param name="targetFolder"> /// Target root folder to write. Data items may go to descendants folders, /// <see cref="bfs.Repository.Interfaces.IDataItem.RelativePath"/> /// </param> /// <returns> /// New instance of <see cref="IRepositoryWriter"/>. /// </returns> /// <remarks> /// Only 1 writer can be created for a particular folder. /// </remarks> public virtual IRepositoryWriter GetWriter(IRepositoryFolder targetFolder) { CheckHelper.CheckRepositoryNotDisposed(Repository); Check.RequireArgumentNotNull(targetFolder, "targetFolder"); RepositoryWriter retval; lock (targetFolder) { RepositoryFolder.CheckNotDetached(targetFolder); Check.DoAssertLambda(!Repository.IsDataBeingWrittenTo(targetFolder, false), () => new InvalidOperationException(string.Format(StorageResources.WriterAlreadyExistsForFolder, targetFolder.LogicalPath))); retval = new RepositoryWriter(RepositoryFolder.CastFolder(targetFolder)); Repository.RegisterWriter(retval); } Check.Ensure(Repository.IsDataBeingWrittenTo(targetFolder, false)); return(retval); }
/// <summary> /// Create single folder writer and add it to the collection of writers. /// </summary> /// <param name="targetFolder"> /// Target folder for writer /// </param> /// <returns> /// New initialised instance. If the method succeeds (does not throw exception) /// </returns> /// <remarks> /// Locking sequence: target folder lock start, then repo manager's registry lock/release, then direct writers collection lock/release, /// then target folder released. /// </remarks> private DirectSingleFolderWriter CreateWriter(IRepositoryFolder targetFolder, string normalisedRelativePath) { Check.RequireArgumentNotNull(targetFolder, "targetFolder"); RepositoryFolder.CheckNotDetached(targetFolder); IFolder folder = RepositoryFolder.CastFolder(targetFolder); Exceptions.DifferentRepositoriesExceptionHelper.Check(Folder.Repository, targetFolder.Repository); DirectSingleFolderWriter writer = null; ICoder compressor = GetCompressor(targetFolder: targetFolder); ICoder encryptor = GetEncryptor(targetFolder: targetFolder); lock (targetFolder) { Check.DoAssertLambda( object.ReferenceEquals(targetFolder, _repoFolder) || // this writer has already seized this folder as target !Folder.Repository.IsDataBeingWrittenTo(targetFolder, false), () => new InvalidOperationException(string.Format(StorageResources.WriterAlreadyExistsForFolder, targetFolder.LogicalPath))); writer = new DirectSingleFolderWriter(folder, compressor, encryptor); writer.EqualTimestampedItemsComparer = this.EqualTimestampedItemsComparer; lock (_directWritersByRelativePath) { _directWritersByRelativePath.Add(normalisedRelativePath, writer); _directWritersByFolderKey.Add(targetFolder.FolderKey, writer); } } writer.TrackUnsavedItems = TrackUnsavedItems; Check.Ensure(Repository.IsDataBeingWrittenTo(targetFolder, false)); return(writer); }