public IInternalConfigRecord GetConfigRecord(string configPath) { IInternalConfigRecord record4; if (!ConfigPathUtility.IsValid(configPath)) { throw ExceptionUtil.ParameterInvalid("configPath"); } string[] parts = ConfigPathUtility.GetParts(configPath); try { int num; BaseConfigurationRecord record; this.AcquireHierarchyLockForRead(); this.hlFindConfigRecord(parts, out num, out record); if ((num == parts.Length) || !record.hlNeedsChildFor(parts[num])) { return(record); } } finally { this.ReleaseHierarchyLockForRead(); } try { int num2; BaseConfigurationRecord record2; this.AcquireHierarchyLockForWrite(); this.hlFindConfigRecord(parts, out num2, out record2); if (num2 == parts.Length) { return(record2); } string parentConfigPath = string.Join("/", parts, 0, num2); while ((num2 < parts.Length) && record2.hlNeedsChildFor(parts[num2])) { BaseConfigurationRecord record3; string childConfigPath = parts[num2]; parentConfigPath = ConfigPathUtility.Combine(parentConfigPath, childConfigPath); if (this._isDesignTime) { record3 = MgmtConfigurationRecord.Create(this, record2, parentConfigPath, null); } else { record3 = (BaseConfigurationRecord)RuntimeConfigurationRecord.Create(this, record2, parentConfigPath); } record2.hlAddChild(childConfigPath, record3); num2++; record2 = record3; } record4 = record2; } finally { this.ReleaseHierarchyLockForWrite(); } return(record4); }
private void RemoveConfigImpl(string configPath, BaseConfigurationRecord configRecord) { BaseConfigurationRecord record; if (!ConfigPathUtility.IsValid(configPath)) { throw ExceptionUtil.ParameterInvalid("configPath"); } string[] parts = ConfigPathUtility.GetParts(configPath); try { int num; this.AcquireHierarchyLockForWrite(); this.hlFindConfigRecord(parts, out num, out record); if ((num != parts.Length) || ((configRecord != null) && !object.ReferenceEquals(configRecord, record))) { return; } record.Parent.hlRemoveChild(parts[parts.Length - 1]); } finally { this.ReleaseHierarchyLockForWrite(); } this.OnConfigRemoved(new InternalConfigEventArgs(configPath)); record.CloseRecursive(); }
// Find and remove the config record and all its children for the config path. // Optionally ensure the config record matches a desired config record. private void RemoveConfigImpl(string configPath, BaseConfigurationRecord configRecord) { if (!ConfigPathUtility.IsValid(configPath)) { throw ExceptionUtil.ParameterInvalid("configPath"); } string[] parts = ConfigPathUtility.GetParts(configPath); BaseConfigurationRecord currentRecord; // search under exclusive writer lock try { int index; AcquireHierarchyLockForWrite(); HlFindConfigRecord(parts, out index, out currentRecord); // Return if not found, or does not match the one we are trying to remove. if ((index != parts.Length) || ((configRecord != null) && !ReferenceEquals(configRecord, currentRecord))) { return; } // Remove it from the hierarchy. currentRecord.Parent.HlRemoveChild(parts[parts.Length - 1]); } finally { ReleaseHierarchyLockForWrite(); } OnConfigRemoved(new InternalConfigEventArgs(configPath)); // Close the record. This is safe to do outside the lock. currentRecord.CloseRecursive(); }
public void IsValid(string configPath, bool expected) { Assert.Equal(expected, ConfigPathUtility.IsValid(configPath)); }
// // Get the config record for a path. // If the record does not exist, create it if it is needed. // public IInternalConfigRecord GetConfigRecord(string configPath) { if (!ConfigPathUtility.IsValid(configPath)) { throw ExceptionUtil.ParameterInvalid("configPath"); } string[] parts = ConfigPathUtility.GetParts(configPath); // // First search under the reader lock, so that multiple searches // can proceed in parallel. // try { int index; BaseConfigurationRecord currentRecord; AcquireHierarchyLockForRead(); hlFindConfigRecord(parts, out index, out currentRecord); // check if found if (index == parts.Length || !currentRecord.hlNeedsChildFor(parts[index])) { return(currentRecord); } } finally { ReleaseHierarchyLockForRead(); } // // Not found, so search again under exclusive writer lock so that // we can create the record. // try { int index; BaseConfigurationRecord currentRecord; AcquireHierarchyLockForWrite(); hlFindConfigRecord(parts, out index, out currentRecord); if (index == parts.Length) { return(currentRecord); } string currentConfigPath = String.Join(BaseConfigurationRecord.ConfigPathSeparatorString, parts, 0, index); // // create new records // while (index < parts.Length && currentRecord.hlNeedsChildFor(parts[index])) { string configName = parts[index]; currentConfigPath = ConfigPathUtility.Combine(currentConfigPath, configName); BaseConfigurationRecord childRecord; Debug.Trace("ConfigurationCreate", "Creating config record for " + currentConfigPath); if (_isDesignTime) { childRecord = MgmtConfigurationRecord.Create(this, currentRecord, currentConfigPath, null); } else { childRecord = (BaseConfigurationRecord)RuntimeConfigurationRecord.Create(this, currentRecord, currentConfigPath); } currentRecord.hlAddChild(configName, childRecord); index++; currentRecord = childRecord; } return(currentRecord); } finally { ReleaseHierarchyLockForWrite(); } }
// Example of configPath = "machine/webroot/1/fxtest/sub/foo.aspx" // The configPath parameter must be lower case. static private CachedPathData GetConfigPathData(string configPath) { Debug.Assert(ConfigPathUtility.IsValid(configPath), "ConfigPathUtility.IsValid(configPath)"); Debug.Assert(configPath == configPath.ToLower(CultureInfo.InvariantCulture), "configPath == configPath.ToLower(CultureInfo.InvariantCulture)"); bool exists = false; bool isDirectory = false; bool isRemovable = IsCachedPathDataRemovable(configPath); // if the sliding expiration is zero, we won't cache it unless it is a configPath for root web.config or above if (isRemovable && DoNotCacheUrlMetadata) { string pathSiteID = null; VirtualPath virtualFilePath = null; string physicalFilePath = null; WebConfigurationHost.GetSiteIDAndVPathFromConfigPath(configPath, out pathSiteID, out virtualFilePath); physicalFilePath = GetPhysicalPath(virtualFilePath); string parentConfigPath = ConfigPathUtility.GetParent(configPath); CachedPathData pathParentData = GetConfigPathData(parentConfigPath); if (!String.IsNullOrEmpty(physicalFilePath)) { FileUtil.PhysicalPathStatus(physicalFilePath, false, false, out exists, out isDirectory); } CachedPathData pathData = new CachedPathData(configPath, virtualFilePath, physicalFilePath, exists); pathData.Init(pathParentData); return(pathData); } // // First, see if the CachedPathData is in the cache. // we don't use Add for this lookup, as doing so requires // creating a CacheDependency, which can be slow as it may hit // the filesystem. // string key = CreateKey(configPath); CacheStoreProvider cacheInternal = HttpRuntime.Cache.InternalCache; CachedPathData data = (CachedPathData)cacheInternal.Get(key); // if found, return the data if (data != null) { data.WaitForInit(); return(data); } // WOS bool cacheEntryIsNotRemovable = false; // if not found, try to add it string siteID = null; VirtualPath virtualPath = null; CachedPathData parentData = null; CacheDependency dependency = null; string physicalPath = null; string[] fileDependencies = null; string[] cacheItemDependencies = null; if (WebConfigurationHost.IsMachineConfigPath(configPath)) { cacheEntryIsNotRemovable = true; } else { // Make sure we have the parent data so we can create a dependency on the parent. // The parent dependency will ensure that configuration data in the parent // will be referenced by a cache hit on the child. (see UtcUpdateUsageRecursive in Cache.cs) string parentConfigPath = ConfigPathUtility.GetParent(configPath); parentData = GetConfigPathData(parentConfigPath); string parentKey = CreateKey(parentConfigPath); cacheItemDependencies = new string[1] { parentKey }; if (!WebConfigurationHost.IsVirtualPathConfigPath(configPath)) { // assume hardcoded levels above the path, such as root web.config, exist cacheEntryIsNotRemovable = true; } else { cacheEntryIsNotRemovable = !isRemovable; WebConfigurationHost.GetSiteIDAndVPathFromConfigPath(configPath, out siteID, out virtualPath); physicalPath = GetPhysicalPath(virtualPath); // Add a dependency on the path itself, if it is a file, // to handle the case where a file is deleted and replaced // with a directory of the same name. if (!String.IsNullOrEmpty(physicalPath)) { FileUtil.PhysicalPathStatus(physicalPath, false, false, out exists, out isDirectory); if (exists && !isDirectory) { fileDependencies = new string[1] { physicalPath }; } } } try { dependency = new CacheDependency(0, fileDependencies, cacheItemDependencies); } catch { // CacheDependency ctor could fail because of bogus file path // and it is ok not to watch those } } // Try to add the CachedPathData to the cache. CachedPathData dataAdd = null; bool isDataCreator = false; bool initCompleted = false; CacheItemPriority priority = cacheEntryIsNotRemovable ? CacheItemPriority.NotRemovable : CacheItemPriority.Normal; TimeSpan slidingExpiration = cacheEntryIsNotRemovable ? Cache.NoSlidingExpiration : UrlMetadataSlidingExpiration; try { using (dependency) { dataAdd = new CachedPathData(configPath, virtualPath, physicalPath, exists); try { } finally { data = (CachedPathData)cacheInternal.Add(key, dataAdd, new CacheInsertOptions() { Dependencies = dependency, SlidingExpiration = slidingExpiration, Priority = priority, OnRemovedCallback = s_callback }); if (data == null) { isDataCreator = true; } } } // If another thread added it first, return the data if (!isDataCreator) { data.WaitForInit(); return(data); } // This thread is the creator of the CachedPathData, initialize it lock (dataAdd) { try { dataAdd.Init(parentData); initCompleted = true; } finally { // free waiters dataAdd._flags[FInited] = true; // Wake up waiters. Monitor.PulseAll(dataAdd); if (dataAdd._flags[FCloseNeeded]) { // If we have received a call back to close, then lets // make sure that our config object is cleaned up dataAdd.Close(); } } } } finally { // All the work in this finally block is for the case where we're the // creator of the CachedPathData. if (isDataCreator) { // if (!dataAdd._flags[FInited]) { lock (dataAdd) { // free waiters dataAdd._flags[FInited] = true; // Wake up waiters. Monitor.PulseAll(dataAdd); if (dataAdd._flags[FCloseNeeded]) { // If we have received a call back to close, then lets // make sure that our config object is cleaned up dataAdd.Close(); } } } // // Even though there is a try/catch handler surrounding the call to Init, // a ThreadAbortException can still cause the handler to be bypassed. // // If there is an error, either a thread abort or an error in the config // file itself, we do want to leave the item cached for a short period // so that we do not revisit the error and potentially reparse the config file // on every request. // // The reason we simply do not leave the item in the cache forever is that the // problem that caused the configuration exception may be fixed without touching // the config file in a way that causes a file change notification (for example, an // acl change in a parent directory, or a change of path mapping in the metabase). // // NOTE: It is important to reinsert the item into the cache AFTER dropping // the lock on dataAdd, in order to prevent the possibility of deadlock. // Debug.Assert(dataAdd._flags[FInited], "_flags[FInited]"); if (!initCompleted || (dataAdd.ConfigRecord != null && dataAdd.ConfigRecord.HasInitErrors)) { // // Create a new dependency object as the old one cannot be reused. // Do not include a file dependency if initialization could not be completed, // as invoking the file system could lead to further errors during a thread abort. // if (dependency != null) { if (!initCompleted) { dependency = new CacheDependency(0, null, cacheItemDependencies); } else { dependency = new CacheDependency(0, fileDependencies, cacheItemDependencies); } } using (dependency) { cacheInternal.Insert(key, dataAdd, new CacheInsertOptions() { Dependencies = dependency, AbsoluteExpiration = DateTime.UtcNow.AddSeconds(5), OnRemovedCallback = s_callback }); } } } } return(dataAdd); }