From 59b37f6f74eb55268375d5746c1bbb235538b519 Mon Sep 17 00:00:00 2001 From: Steve Bohlen Date: Sun, 13 Jan 2013 19:39:07 -0500 Subject: [PATCH] SPRNETCODECONFIG-4 introduce support for explicit restriction on assembly load scanning scope --- .../AssemblyObjectDefinitionScanner.cs | 57 +++++++++++++------ .../Context/Attributes/AssemblyTypeScanner.cs | 44 ++++++++++---- .../GenericApplicationContextExtensions.cs | 23 +++++++- .../Attributes/AssemblyTypeScannerTests.cs | 11 +--- .../CodeConfigApplicationContextTests.cs | 24 +++----- 5 files changed, 102 insertions(+), 57 deletions(-) diff --git a/src/Spring/Spring.Core/Context/Attributes/AssemblyObjectDefinitionScanner.cs b/src/Spring/Spring.Core/Context/Attributes/AssemblyObjectDefinitionScanner.cs index 093c2f0a..e6f0b9fe 100644 --- a/src/Spring/Spring.Core/Context/Attributes/AssemblyObjectDefinitionScanner.cs +++ b/src/Spring/Spring.Core/Context/Attributes/AssemblyObjectDefinitionScanner.cs @@ -24,6 +24,7 @@ using System.Linq; using System.Reflection; using Spring.Objects.Factory.Support; using Spring.Stereotype; +using Spring.Util; namespace Spring.Context.Attributes { @@ -39,17 +40,13 @@ namespace Spring.Context.Attributes { "Spring.", "NHibernate.", - "Common.Logging.", + "Common.Logging", "log4net", - "Quartz" + "Quartz", + "NVelocity", + "Rhino.Mocks" }; - //TODO: HACK -- required to permit testing since testing assy also starts with excluded name "Spring." - private readonly IList _springAssemblyIncludeNames = new List() - { - "Spring.Core.Tests" - }; - private IObjectNameGenerator _objectNameGenerator = new AttributeObjectNameGenerator(); /// @@ -148,13 +145,12 @@ namespace Spring.Context.Attributes base.SetDefaultFilters(); //add the desired assembly exclusions to the list - _assemblyExclusionPredicates.Add(a => _springAssemblyExcludePrefixes.Any(n => n.StartsWith(a.GetName().Name))); - //_assemblyExclusionPredicates.Add(a => a.GetName().Name.StartsWith(_springAssemblyPrefix)); - //_assemblyExclusionPredicates.Add(a => _springAssemblies.Contains(a.GetName().Name)); - _assemblyExclusionPredicates.Add(a => a.GetName().Name.StartsWith("System.")); - _assemblyExclusionPredicates.Add(a => a.GetName().Name.StartsWith("Microsoft.")); - _assemblyExclusionPredicates.Add(a => a.GetName().Name == "mscorlib"); - _assemblyExclusionPredicates.Add(a => a.GetName().Name == "System"); + _assemblyExclusionPredicates.Add(assembly => _springAssemblyExcludePrefixes.Any(name => name.StartsWith(assembly.GetName().Name)) + && assembly.GetName().Name != "Spring.Core.Tests"); + _assemblyExclusionPredicates.Add(assembly => assembly.GetName().Name.StartsWith("System.")); + _assemblyExclusionPredicates.Add(assembly => assembly.GetName().Name.StartsWith("Microsoft.")); + _assemblyExclusionPredicates.Add(assembly => assembly.GetName().Name == "mscorlib"); + _assemblyExclusionPredicates.Add(assembly => assembly.GetName().Name == "System"); } /// @@ -172,14 +168,39 @@ namespace Spring.Context.Attributes /// public AssemblyObjectDefinitionScanner() { - //AssemblyLoadExclusionPredicates.Add(name => _springAssemblyExcludePrefixes.Any(n => name.StartsWith(n) && !name.Contains(".Tests"))); - AssemblyLoadExclusionPredicates.Add(candidate => _springAssemblyExcludePrefixes.Any(excludeName => candidate.StartsWith(excludeName) && _springAssemblyIncludeNames.All(includeName => includeName != candidate))); - //AssemblyLoadExclusionPredicates.Add(name => _springAssemblies.Contains(name)); + AssemblyLoadExclusionPredicates.Add(candidate => _springAssemblyExcludePrefixes.Any(candidate.StartsWith) + && candidate != "Spring.Core.Tests"); AssemblyLoadExclusionPredicates.Add(name => name.StartsWith("System.")); AssemblyLoadExclusionPredicates.Add(name => name.StartsWith("Microsoft.")); AssemblyLoadExclusionPredicates.Add(name => name == "mscorlib"); AssemblyLoadExclusionPredicates.Add(name => name == "System"); } + + /// + /// Initializes a new instance of the class. + /// + /// The assemblies to include predicates. + public AssemblyObjectDefinitionScanner(params Func[] assembliesToIncludePredicates) + { + //force exclude for ALL assemblies + AssemblyLoadExclusionPredicates.Add(name => true); + + //since all assemblies are EXCLUDED above, these will be the ONLY assemblies to be loaded + foreach (var predicate in assembliesToIncludePredicates) + { + AssemblyLoadInclusionPredicates.Add(predicate); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The names of assemblies to include. + public AssemblyObjectDefinitionScanner(params string[] assembliesToInclude) + : this(name => assembliesToInclude.Any(candidate => candidate == name)) + { + AssertUtils.ArgumentNotNull(assembliesToInclude, "assembliesToInclude"); + } } } \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/AssemblyTypeScanner.cs b/src/Spring/Spring.Core/Context/Attributes/AssemblyTypeScanner.cs index e7818624..15ab0f9a 100644 --- a/src/Spring/Spring.Core/Context/Attributes/AssemblyTypeScanner.cs +++ b/src/Spring/Spring.Core/Context/Attributes/AssemblyTypeScanner.cs @@ -46,6 +46,11 @@ namespace Spring.Context.Attributes /// protected IList> AssemblyLoadExclusionPredicates = new List>(); + /// + /// Names of Assemblies to include for scanning. + /// + protected IList> AssemblyLoadInclusionPredicates = new List>(); + /// /// Assembly Inclusion Predicates. /// @@ -81,11 +86,29 @@ namespace Spring.Context.Attributes /// protected DocumentDefaultsDefinition _defaults; + + protected string _scanStartFolderPath; + + /// /// Stores the object default definitons defined in the XML configuration documnet /// public DocumentDefaultsDefinition Defaults { get { return _defaults; } set { _defaults = value; } } + public string ScanStartFolderPath + { + get + { + //if we have no value, set it to the current bin dir + if (string.IsNullOrEmpty(_scanStartFolderPath)) + { + _scanStartFolderPath = GetCurrentBinDirectoryPath(); + } + return _scanStartFolderPath; + } + set { _scanStartFolderPath = value; } + } + #region IAssemblyTypeScanner Members /// @@ -106,7 +129,7 @@ namespace Spring.Context.Attributes /// public IAssemblyTypeScanner ExcludeType() { - TypeExclusionPredicates.Add(t => t.FullName == typeof (T).FullName); + TypeExclusionPredicates.Add(t => t.FullName == typeof(T).FullName); return this; } @@ -117,7 +140,7 @@ namespace Spring.Context.Attributes /// public IAssemblyTypeScanner IncludeType() { - TypeInclusionPredicates.Add(t => t.FullName == typeof (T).FullName); + TypeInclusionPredicates.Add(t => t.FullName == typeof(T).FullName); return this; } @@ -145,7 +168,7 @@ namespace Spring.Context.Attributes IList types = new List(); - foreach (Assembly assembly in GetAllMatchingAssemblies()) + foreach (Assembly assembly in GetAllMatchingAssemblies(ScanStartFolderPath)) { TypeSources.Add(new AssemblyTypeSource(assembly)); } @@ -225,23 +248,21 @@ namespace Spring.Context.Attributes #endregion - private List GetAllAssembliesInPath() + private List GetAllAssembliesInPath(string folderPath) { - string folderPath = GetCurrentBinDirectoryPath(); - var assemblies = new List(); assemblies.AddRange(DiscoverAssemblies(folderPath, "*.dll")); assemblies.AddRange(DiscoverAssemblies(folderPath, "*.exe")); Logger.Debug(m => m("Assemblies to be scanned: {0}", StringUtils.ArrayToCommaDelimitedString(assemblies.ToArray()))); - + return assemblies; } - private IEnumerable GetAllMatchingAssemblies() + private IEnumerable GetAllMatchingAssemblies(string folderPath) { - IEnumerable assemblyCandidates = GetAllAssembliesInPath(); + IEnumerable assemblyCandidates = GetAllAssembliesInPath(folderPath); IList assemblies = new List(); @@ -378,7 +399,10 @@ namespace Spring.Context.Attributes { string name = Path.GetFileNameWithoutExtension(file); - if (!AssemblyLoadExclusionPredicates.Any(exclude => exclude(name))) + bool isNotExcluded = !AssemblyLoadExclusionPredicates.Any(exclude => exclude(name)); + bool isIncluded = AssemblyLoadInclusionPredicates.Any(include => include(name)); + + if (isNotExcluded || isIncluded) { assemblies.Add(file); } diff --git a/src/Spring/Spring.Core/Context/Extension/GenericApplicationContextExtensions.cs b/src/Spring/Spring.Core/Context/Extension/GenericApplicationContextExtensions.cs index 0cfd2f58..0e3caee7 100644 --- a/src/Spring/Spring.Core/Context/Extension/GenericApplicationContextExtensions.cs +++ b/src/Spring/Spring.Core/Context/Extension/GenericApplicationContextExtensions.cs @@ -23,6 +23,7 @@ using System; using System.Reflection; using Spring.Context.Attributes; using Spring.Objects.Factory.Support; +using Spring.Util; namespace Spring.Context.Support { @@ -54,9 +55,25 @@ namespace Spring.Context.Support public static void Scan(this GenericApplicationContext context, string assemblyScanPath, Func assemblyPredicate, Func typePredicate) { - //create a scanner instance using the scan path - var scanner = new AssemblyObjectDefinitionScanner(); + Scan(context, assemblyScanPath, assemblyPredicate, typePredicate, new string[0]); + } + + /// + /// Scans the specified context. + /// + /// The context. + /// The assembly scan path. + /// The assembly predicate. + /// The type predicate. + /// The assemblies to scan. + public static void Scan(this GenericApplicationContext context, string assemblyScanPath, Func assemblyPredicate, Func typePredicate, params string[] assembliesToScan) + { + AssemblyObjectDefinitionScanner scanner = + ArrayUtils.HasElements(assembliesToScan) ? new AssemblyObjectDefinitionScanner(assembliesToScan) : new AssemblyObjectDefinitionScanner(); + + scanner.ScanStartFolderPath = assemblyScanPath; + //configure the scanner per the provided constraints scanner.WithAssemblyFilter(assemblyPredicate).WithIncludeFilter(typePredicate); @@ -105,6 +122,6 @@ namespace Spring.Context.Support Scan(context, null, obj => true, typePredicate); } - + } } \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyTypeScannerTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyTypeScannerTests.cs index ac4c8ded..0241999a 100644 --- a/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyTypeScannerTests.cs +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyTypeScannerTests.cs @@ -66,7 +66,7 @@ namespace Spring.Context.Attributes _scanner.IncludeType(); _scanner.IncludeType(); _scanner.WithExcludeFilter(t => t.Name.StartsWith("TheImported")); - + IEnumerable types = _scanner.Scan(); //Assert.That(types.Any(t => t.Name == "TheConfigurationClass")); @@ -88,15 +88,6 @@ namespace Spring.Context.Attributes Assert.That(types.Count(), Is.EqualTo(2)); } - [Serializable] - private class Scanner : AssemblyTypeScanner - { - protected override bool IsCompoundPredicateSatisfiedBy(Type type) - { - return IsIncludedType(type) && !IsExcludedType(type); - } - } - private AssemblyObjectDefinitionScanner _scanner; private List> ExcludePredicates diff --git a/test/Spring/Spring.Core.Tests/Context/Support/CodeConfigApplicationContextTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/CodeConfigApplicationContextTests.cs index 1f2c273d..307108e6 100644 --- a/test/Spring/Spring.Core.Tests/Context/Support/CodeConfigApplicationContextTests.cs +++ b/test/Spring/Spring.Core.Tests/Context/Support/CodeConfigApplicationContextTests.cs @@ -30,12 +30,14 @@ namespace Spring.Context.Support public class CodeConfigApplicationContextTests { private CodeConfigApplicationContext _context; + private AssemblyObjectDefinitionScanner _scanner; [SetUp] public void _TestSetup() { _context = new CodeConfigApplicationContext(); - } + _scanner = new AssemblyObjectDefinitionScanner(); + } [Test] public void Can_Filter_For_Assembly_Based_On_Assembly_Metadata() @@ -47,18 +49,16 @@ namespace Spring.Context.Support } [Test] - [Ignore] public void Can_Filter_For_Assembly_Containing_Specific_Type_But_Having_NO_Definitions() { //specifically filter assemblies for one that we *know* will result in NO [Configuration] types in it _context.ScanWithAssemblyFilter(assy => assy.GetTypes().Any(type => type.FullName.Contains(typeof(Spring.Core.IOrdered).Name))); - _context.Refresh(); + _context.Refresh(); Assert.That(_context.DefaultListableObjectFactory.ObjectDefinitionCount, Is.EqualTo(4)); } [Test] - [Ignore] public void Can_Filter_For_Assembly_Containing_Specific_Type() { _context.ScanWithAssemblyFilter(assy => assy.GetTypes().Any(type => type.FullName.Contains(typeof(MarkerTypeForScannerToFind).Name))); @@ -88,24 +88,15 @@ namespace Spring.Context.Support [Test] public void Can_Filter_For_Specific_Types_With_Multiple_Include_Filters() { - var scanner = new AssemblyObjectDefinitionScanner(); - scanner.WithIncludeFilter(type => type.FullName.Contains(typeof(TheImportedConfigurationClass).Name)); - scanner.WithIncludeFilter(type => type.FullName.Contains(typeof(TheConfigurationClass).Name)); + _scanner.WithIncludeFilter(type => type.FullName.Contains(typeof(TheImportedConfigurationClass).Name)); + _scanner.WithIncludeFilter(type => type.FullName.Contains(typeof(TheConfigurationClass).Name)); - _context.Scan(scanner); + _context.Scan(_scanner); _context.Refresh(); AssertExpectedObjectsAreRegisteredWith(_context, 19); } - [Test] - public void Scanner() - { - AssemblyObjectDefinitionScanner scanner = new AssemblyObjectDefinitionScanner(); - scanner.AssemblyHavingType(); - - } - [Test] public void Can_Perform_Scan_With_No_Filtering() { @@ -137,6 +128,7 @@ namespace Spring.Context.Support } + //DO NOT DELETE: this empty class req'd by the scanning tests! public class MarkerTypeForScannerToFind {