/// <summary> /// /// </summary> /// <param name="name">A name for this tree, for your reference</param> /// <param name="globalRootItemPath">The 'global' path where this tree is rooted. For example, if this was '/sitecore/content', the root item in this tree would be 'content'</param> /// <param name="databaseName">Name of the database the items in this tree are from. This is for your reference and help when resolving this tree as a destination, and is not directly used.</param> /// <param name="physicalRootPath">The physical root path to write items in this tree to. Will be created if it does not exist.</param> /// <param name="formatter">The formatter to use when reading or writing items to disk</param> /// <param name="useDataCache">Whether to cache items read in memory for later rapid retrieval. Great for small trees, or if you have plenty of RAM. Bad for media trees :)</param> public SerializationFileSystemTree(string name, string globalRootItemPath, string databaseName, string physicalRootPath, ISerializationFormatter formatter, bool useDataCache) { Assert.ArgumentNotNullOrEmpty(globalRootItemPath, "globalRootItemPath"); Assert.ArgumentNotNullOrEmpty(databaseName, "databaseName"); Assert.ArgumentNotNullOrEmpty(physicalRootPath, "physicalRootPath"); Assert.ArgumentNotNull(formatter, "formatter"); Assert.IsTrue(globalRootItemPath.StartsWith("/"), "The global root item path must start with '/', e.g. '/sitecore' or '/sitecore/content'"); Assert.IsTrue(globalRootItemPath.Length > 1, "The global root item path cannot be '/' - there is no root item. You probably mean '/sitecore'."); _globalRootItemPath = globalRootItemPath.TrimEnd('/'); // enforce that the physical root path is filesystem-safe AssertValidPhysicalPath(physicalRootPath); _physicalRootPath = physicalRootPath; _formatter = formatter; _dataCache = new FsCache <IItemData>(useDataCache); Name = name; DatabaseName = databaseName; if (!Directory.Exists(_physicalRootPath)) { Directory.CreateDirectory(_physicalRootPath); } _treeWatcher = new TreeWatcher(_physicalRootPath, _formatter.FileExtension, HandleDataItemChanged); }
/// <summary> /// /// </summary> /// <param name="name">A name for this tree, for your reference</param> /// <param name="globalRootItemPath">The 'global' path where this tree is rooted. For example, if this was '/sitecore/content', the root item in this tree would be 'content'</param> /// <param name="databaseName">Name of the database the items in this tree are from. This is for your reference and help when resolving this tree as a destination, and is not directly used.</param> /// <param name="physicalRootPath">The physical root path to write items in this tree to. Will be created if it does not exist.</param> /// <param name="formatter">The formatter to use when reading or writing items to disk</param> /// <param name="useDataCache">Whether to cache items read in memory for later rapid retrieval. Great for small trees, or if you have plenty of RAM. Bad for media trees :)</param> public SerializationFileSystemTree(string name, string globalRootItemPath, string databaseName, string physicalRootPath, ISerializationFormatter formatter, bool useDataCache) { Assert.ArgumentNotNullOrEmpty(globalRootItemPath, "globalRootItemPath"); Assert.ArgumentNotNullOrEmpty(databaseName, "databaseName"); Assert.ArgumentNotNullOrEmpty(physicalRootPath, "physicalRootPath"); Assert.ArgumentNotNull(formatter, "formatter"); Assert.IsTrue(globalRootItemPath.StartsWith("/"), "The global root item path must start with '/', e.g. '/sitecore' or '/sitecore/content'"); Assert.IsTrue(globalRootItemPath.Length > 1, "The global root item path cannot be '/' - there is no root item. You probably mean '/sitecore'."); _globalRootItemPath = globalRootItemPath.TrimEnd('/'); PhysicalRootPath = physicalRootPath; _formatter = formatter; _dataCache = new FsCache<IItemData>(useDataCache); Name = name; DatabaseName = databaseName; if (!Directory.Exists(PhysicalRootPath)) Directory.CreateDirectory(PhysicalRootPath); _treeWatcher = new TreeWatcher(PhysicalRootPath, _formatter.FileExtension, HandleDataItemChanged); }
protected virtual void HandleDataItemChanged(string path, TreeWatcher.TreeWatcherChangeType changeType) { if (changeType == TreeWatcher.TreeWatcherChangeType.ChangeOrAdd) { Log.Info(string.Format("[Rainbow] SFS tree item {0} changed ({1}), caches updating.", path, changeType), this); const int retries = 5; for (int i = 0; i < retries; i++) { try { // note that the act of reading the metadata will update the metadata cache automatically // (it'll either not be in cache or have a newer write time thus invalidating FsCache) var metadata = ReadItemMetadata(path); if (metadata != null) { if (TreeItemChanged != null) TreeItemChanged(metadata); } _dataCache.Remove(path); } catch (IOException iex) { // this is here because FSW can tell us the file has changed // BEFORE it's done with writing. So if we get access denied, // we wait 500ms and retry up to 5x before rethrowing if (i < retries - 1) { Thread.Sleep(500); continue; } Log.Error("[Rainbow] Failed to read {0} metadata because the file remained locked too long.".FormatWith(path), iex, this); } catch (Exception ex) { Log.Error("[Rainbow] Failed to read updated file {0}. This may indicate a merge conflict or corrupt file. We'll retry reading it if it changes again.".FormatWith(path), ex, this); } break; } } if (changeType == TreeWatcher.TreeWatcherChangeType.Delete) { Log.Info(string.Format("Serialized item {0} deleted, reloading caches.", path), this); var existingCached = _metadataCache.GetValue(path, false); _dataCache.Remove(path); _metadataCache.Remove(path); if (existingCached != null) { IItemMetadata temp; _idCache.TryRemove(existingCached.Id, out temp); if (TreeItemChanged != null) { if (TreeItemChanged != null) TreeItemChanged(existingCached); return; } } if (TreeItemChanged != null) TreeItemChanged(null); } }