/// <summary> /// This method invokes the finder which scans the assemblies for the types and then loads the result into the type finder. /// Once the results are loaded, we update the cached type xml file /// </summary> /// <param name="typeList"></param> /// <param name="resolutionKind"> </param> /// <param name="finder"></param> /// <remarks> /// THIS METHODS IS NOT THREAD SAFE /// </remarks> private void LoadViaScanningAndUpdateCacheFile <T>(TypeList typeList, TypeResolutionKind resolutionKind, Func <IEnumerable <Type> > finder) { //we don't have a cache for this so proceed to look them up by scanning foreach (var t in finder()) { typeList.AddType(t); } UpdateCachedPluginsFile <T>(typeList.GetTypes(), resolutionKind); }
/// <summary> /// Attempts to resolve the list of plugin + assemblies found in the runtime for the base type 'T' passed in. /// If the cache file doesn't exist, fails to load, is corrupt or the type 'T' element is not found then /// a false attempt is returned. /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> internal Attempt <IEnumerable <string> > TryGetCachedPluginsFromFile <T>(TypeResolutionKind resolutionType) { var filePath = GetPluginListFilePath(); if (!File.Exists(filePath)) { return(Attempt <IEnumerable <string> > .False); } try { //we will load the xml document, if the app context exist, we will load it from the cache (which is only around for 5 minutes) //while the app boots up, this should save some IO time on app startup when the app context is there (which is always unless in unit tests) XDocument xml; if (_appContext != null) { xml = _appContext.ApplicationCache.GetCacheItem(CacheKey, new TimeSpan(0, 0, 5, 0), () => XDocument.Load(filePath)); } else { xml = XDocument.Load(filePath); } if (xml.Root == null) { return(Attempt <IEnumerable <string> > .False); } var typeElement = xml.Root.Elements() .SingleOrDefault(x => x.Name.LocalName == "baseType" && ((string)x.Attribute("type")) == typeof(T).FullName && ((string)x.Attribute("resolutionType")) == resolutionType.ToString()); //return false but specify this exception type so we can detect it if (typeElement == null) { return(new Attempt <IEnumerable <string> >(new CachedPluginNotFoundInFile())); } //return success return(new Attempt <IEnumerable <string> >( true, typeElement.Elements("add") .Select(x => (string)x.Attribute("type")))); } catch (Exception ex) { //if the file is corrupted, etc... return false return(new Attempt <IEnumerable <string> >(ex)); } }
/// <summary> /// Adds/Updates the type list for the base type 'T' in the cached file /// </summary> /// <typeparam name="T"></typeparam> /// <param name="typesFound"></param> ///<param name="resolutionType"> </param> ///<remarks> /// THIS METHOD IS NOT THREAD SAFE /// </remarks> /// <example> /// <![CDATA[ /// <plugins> /// <baseType type="Test.Testing.Tester"> /// <add type="My.Assembly.MyTester" assembly="My.Assembly" /> /// <add type="Your.Assembly.YourTester" assembly="Your.Assembly" /> /// </baseType> /// </plugins> /// ]]> /// </example> internal void UpdateCachedPluginsFile <T>(IEnumerable <Type> typesFound, TypeResolutionKind resolutionType) { var filePath = GetPluginListFilePath(); XDocument xml; try { xml = XDocument.Load(filePath); } catch { //if there's an exception loading then this is somehow corrupt, we'll just replace it. File.Delete(filePath); //create the document and the root xml = new XDocument(new XElement("plugins")); } if (xml.Root == null) { //if for some reason there is no root, create it xml.Add(new XElement("plugins")); } //find the type 'T' element to add or update var typeElement = xml.Root.Elements() .SingleOrDefault(x => x.Name.LocalName == "baseType" && ((string)x.Attribute("type")) == typeof(T).FullName && ((string)x.Attribute("resolutionType")) == resolutionType.ToString()); if (typeElement == null) { //create the type element typeElement = new XElement("baseType", new XAttribute("type", typeof(T).FullName), new XAttribute("resolutionType", resolutionType.ToString())); //then add it to the root xml.Root.Add(typeElement); } //now we have the type element, we need to clear any previous types as children and add/update it with new ones typeElement.ReplaceNodes(typesFound.Select(x => new XElement("add", new XAttribute("type", x.AssemblyQualifiedName)))); //save the xml file xml.Save(filePath); }
private IEnumerable <Type> ResolveTypes <T>( Func <IEnumerable <Type> > finder, TypeResolutionKind resolutionType, bool cacheResult) { using (var readLock = new UpgradeableReadLock(_lock)) { using (DisposableTimer.TraceDuration <PluginManager>( String.Format("Starting resolution types of {0}", typeof(T).FullName), String.Format("Completed resolution of types of {0}", typeof(T).FullName))) { //check if the TypeList already exists, if so return it, if not we'll create it var typeList = _types.SingleOrDefault(x => x.IsTypeList <T>(resolutionType)); //if we're not caching the result then proceed, or if the type list doesn't exist then proceed if (!cacheResult || typeList == null) { //upgrade to a write lock since we're adding to the collection readLock.UpgradeToWriteLock(); typeList = new TypeList <T>(resolutionType); foreach (var t in finder()) { typeList.AddType(t); } //only add the cache if we are to cache the results if (cacheResult) { //add the type list to the collection _types.Add(typeList); } } return(typeList.GetTypes()); } } }
public abstract bool IsTypeList <TLookup>(TypeResolutionKind resolutionType);
private IEnumerable <Type> ResolveTypes <T>( Func <IEnumerable <Type> > finder, TypeResolutionKind resolutionType, bool cacheResult) { using (var readLock = new UpgradeableReadLock(Locker)) { var typesFound = new List <Type>(); using (_logger.TraceDuration <PluginManager>( String.Format("Starting resolution types of {0}", typeof(T).FullName), String.Format("Completed resolution of types of {0}, found {1}", typeof(T).FullName, typesFound.Count))) { //check if the TypeList already exists, if so return it, if not we'll create it var typeList = _types.SingleOrDefault(x => x.IsTypeList <T>(resolutionType)); //need to put some logging here to try to figure out why this is happening: http://issues.umbraco.org/issue/U4-3505 if (cacheResult && typeList != null) { _logger.Logger.Debug <PluginManager>("Existing typeList found for {0} with resolution type {1}", () => typeof(T), () => resolutionType); } //if we're not caching the result then proceed, or if the type list doesn't exist then proceed if (cacheResult == false || typeList == null) { //upgrade to a write lock since we're adding to the collection readLock.UpgradeToWriteLock(); typeList = new TypeList <T>(resolutionType); //we first need to look into our cache file (this has nothing to do with the 'cacheResult' parameter which caches in memory). //if assemblies have not changed and the cache file actually exists, then proceed to try to lookup by the cache file. if (RequiresRescanning == false && File.Exists(GetPluginListFilePath())) { var fileCacheResult = TryGetCachedPluginsFromFile <T>(resolutionType); //here we need to identify if the CachedPluginNotFoundInFile was the exception, if it was then we need to re-scan //in some cases the plugin will not have been scanned for on application startup, but the assemblies haven't changed //so in this instance there will never be a result. if (fileCacheResult.Exception != null && fileCacheResult.Exception is CachedPluginNotFoundInFileException) { _logger.Logger.Debug <PluginManager>("Tried to find typelist for type {0} and resolution {1} in file cache but the type was not found so loading types by assembly scan ", () => typeof(T), () => resolutionType); //we don't have a cache for this so proceed to look them up by scanning LoadViaScanningAndUpdateCacheFile <T>(typeList, resolutionType, finder); } else { if (fileCacheResult.Success) { var successfullyLoadedFromCache = true; //we have a previous cache for this so we don't need to scan we just load what has been found in the file foreach (var t in fileCacheResult.Result) { try { //we use the build manager to ensure we get all types loaded, this is slightly slower than //Type.GetType but if the types in the assembly aren't loaded yet then we have problems with that. var type = BuildManager.GetType(t, true); typeList.AddType(type); } catch (Exception ex) { //if there are any exceptions loading types, we have to exist, this should never happen so //we will need to revert to scanning for types. successfullyLoadedFromCache = false; _logger.Logger.Error <PluginManager>("Could not load a cached plugin type: " + t + " now reverting to re-scanning assemblies for the base type: " + typeof(T).FullName, ex); break; } } if (successfullyLoadedFromCache == false) { //we need to manually load by scanning if loading from the file was not successful. LoadViaScanningAndUpdateCacheFile <T>(typeList, resolutionType, finder); } else { _logger.Logger.Debug <PluginManager>("Loaded plugin types {0} with resolution {1} from persisted cache", () => typeof(T), () => resolutionType); } } } } else { _logger.Logger.Debug <PluginManager>("Assembly changes detected, loading types {0} for resolution {1} by assembly scan", () => typeof(T), () => resolutionType); //we don't have a cache for this so proceed to look them up by scanning LoadViaScanningAndUpdateCacheFile <T>(typeList, resolutionType, finder); } //only add the cache if we are to cache the results if (cacheResult) { //add the type list to the collection var added = _types.Add(typeList); _logger.Logger.Debug <PluginManager>("Caching of typelist for type {0} and resolution {1} was successful = {2}", () => typeof(T), () => resolutionType, () => added); } } typesFound = typeList.GetTypes().ToList(); } return(typesFound); } }