initial rework of scanning to use AssemblyObjectDefinitionScanner class and more fluent filter-registration API
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using Common.Logging;
|
||||
using Spring.Util;
|
||||
|
||||
namespace Spring.Context.Attributes
|
||||
{
|
||||
|
||||
public interface IAssemblyObjectDefinitionScanner
|
||||
{
|
||||
IAssemblyObjectDefinitionScanner AssemblyHavingType<T>();
|
||||
IAssemblyObjectDefinitionScanner WithAssemblyFilter(Predicate<Assembly> assemblyPredicate);
|
||||
|
||||
IAssemblyObjectDefinitionScanner WithIncludeFilter(Predicate<Type> predicate);
|
||||
IAssemblyObjectDefinitionScanner WithExcludeFilter(Predicate<Type> predicate);
|
||||
|
||||
IAssemblyObjectDefinitionScanner IncludeTypes(IEnumerable<Type> typeSource);
|
||||
IAssemblyObjectDefinitionScanner IncludeType<T>();
|
||||
|
||||
IEnumerable<Type> Scan();
|
||||
}
|
||||
|
||||
public class AssemblyObjectDefinitionScanner : IAssemblyObjectDefinitionScanner
|
||||
{
|
||||
private readonly List<Predicate<Assembly>> _assemblyPredicates = new List<Predicate<Assembly>>();
|
||||
|
||||
private readonly List<Predicate<Type>> _excludePredicates = new List<Predicate<Type>>();
|
||||
|
||||
private string _folderScanPath;
|
||||
|
||||
private readonly List<Predicate<Type>> _includePredicates = new List<Predicate<Type>>();
|
||||
|
||||
private static ILog _logger = LogManager.GetLogger(typeof(AssemblyObjectDefinitionScanner));
|
||||
|
||||
private readonly List<IEnumerable<Type>> _typeSources = new List<IEnumerable<Type>>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AssemblyObjectDefinitionScanner class.
|
||||
/// </summary>
|
||||
public AssemblyObjectDefinitionScanner()
|
||||
{
|
||||
_folderScanPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AssemblyObjectDefinitionScanner class.
|
||||
/// </summary>
|
||||
/// <param name="folderScanPath">The folder scan path.</param>
|
||||
public AssemblyObjectDefinitionScanner(string folderScanPath)
|
||||
{
|
||||
_folderScanPath = folderScanPath;
|
||||
}
|
||||
|
||||
public IAssemblyObjectDefinitionScanner AssemblyHavingType<T>()
|
||||
{
|
||||
_typeSources.Add(new AssemblyTypeSource((typeof(T).Assembly)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IAssemblyObjectDefinitionScanner IncludeType<T>()
|
||||
{
|
||||
_includePredicates.Add(t => t == typeof(T));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IAssemblyObjectDefinitionScanner IncludeTypes(IEnumerable<Type> typeSource)
|
||||
{
|
||||
AssertUtils.ArgumentNotNull(typeSource, "typeSource");
|
||||
_typeSources.Add(typeSource);
|
||||
_includePredicates.Add(t => typeSource.Any(t1 => t1 == t));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEnumerable<Type> Scan()
|
||||
{
|
||||
SetDefaultFiltersIfNeeded();
|
||||
|
||||
IList<Type> types = new List<Type>();
|
||||
|
||||
foreach (Assembly assembly in GetAllMatchingAssemblies())
|
||||
{
|
||||
_typeSources.Add(new AssemblyTypeSource(assembly));
|
||||
}
|
||||
|
||||
foreach (var typeSource in _typeSources)
|
||||
{
|
||||
foreach (Type type in typeSource)
|
||||
{
|
||||
if (IsIncludedType(type) && !IsExcludedType(type) && HasComponentAttribute(type))
|
||||
{
|
||||
types.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
public IAssemblyObjectDefinitionScanner WithAssemblyFilter(Predicate<Assembly> assemblyPredicate)
|
||||
{
|
||||
_assemblyPredicates.Add(assemblyPredicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IAssemblyObjectDefinitionScanner WithExcludeFilter(Predicate<Type> predicate)
|
||||
{
|
||||
_excludePredicates.Add(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IAssemblyObjectDefinitionScanner WithIncludeFilter(Predicate<Type> predicate)
|
||||
{
|
||||
_includePredicates.Add(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected virtual bool IsExcludedType(Type type)
|
||||
{
|
||||
foreach (var exclude in _excludePredicates)
|
||||
{
|
||||
if (exclude(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool IsIncludedType(Type type)
|
||||
{
|
||||
foreach (var include in _includePredicates)
|
||||
{
|
||||
if (include(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private bool HasComponentAttribute(Type type)
|
||||
{
|
||||
return Attribute.GetCustomAttribute(type, typeof(ConfigurationAttribute), true) != null;
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<Assembly> GetAllMatchingAssemblies()
|
||||
{
|
||||
IList<Assembly> assemblyCandidates = new List<Assembly>();
|
||||
|
||||
IEnumerable<string> files = Directory.GetFiles(_folderScanPath, "*.dll");
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
assemblyCandidates.Add(Assembly.LoadFrom(file));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//log and swallow everything that might go wrong here...
|
||||
if (_logger.IsDebugEnabled)
|
||||
_logger.Debug("Failed to load type while scanning Assemblies for Defintions!", ex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
IList<Assembly> assemblies = new List<Assembly>();
|
||||
|
||||
foreach (Assembly assemblyCandidate in assemblyCandidates)
|
||||
{
|
||||
foreach (var include in _assemblyPredicates)
|
||||
{
|
||||
if (include(assemblyCandidate))
|
||||
{
|
||||
assemblies.Add(assemblyCandidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return assemblies;
|
||||
}
|
||||
|
||||
private void SetDefaultFiltersIfNeeded()
|
||||
{
|
||||
if (_includePredicates.Count == 0)
|
||||
{
|
||||
_includePredicates.Add(t => true);
|
||||
}
|
||||
|
||||
if (_excludePredicates.Count == 0)
|
||||
{
|
||||
_excludePredicates.Add(t => false);
|
||||
}
|
||||
|
||||
if (_assemblyPredicates.Count == 0)
|
||||
{
|
||||
_assemblyPredicates.Add(a => true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright © 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Spring.Util;
|
||||
|
||||
namespace Spring.Context.Attributes
|
||||
{
|
||||
public class AssemblyTypeSource : IEnumerable<Type>
|
||||
{
|
||||
private readonly _Assembly assembly;
|
||||
|
||||
public AssemblyTypeSource(Assembly assembly)
|
||||
{
|
||||
AssertUtils.ArgumentNotNull(assembly, "assembly");
|
||||
this.assembly = assembly;
|
||||
}
|
||||
|
||||
public IEnumerator<Type> GetEnumerator()
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
yield return type;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Spring.Stereotype;
|
||||
|
||||
namespace Spring.Context.Attributes
|
||||
{
|
||||
@@ -25,7 +26,7 @@ namespace Spring.Context.Attributes
|
||||
/// </ul>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ConfigurationAttribute : Attribute
|
||||
public class ConfigurationAttribute : ComponentAttribute
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,23 +11,9 @@ namespace Spring.Objects.Factory.Support
|
||||
{
|
||||
public static class AssemblyScanningExtensionMethods
|
||||
{
|
||||
private static ILog _logger = LogManager.GetLogger(typeof(AssemblyScanningExtensionMethods));
|
||||
|
||||
/// <summary>
|
||||
/// Scans the assemblies for definitions.
|
||||
/// </summary>
|
||||
/// <param name="registry">The registry.</param>
|
||||
/// <param name="assemblyScanPath">The assembly scan path.</param>
|
||||
/// <param name="assemblyFilenamePredicate">The assembly filename predicate.</param>
|
||||
/// <param name="assemblyMetadataPredicate">The assembly metadata predicate.</param>
|
||||
/// <returns></returns>
|
||||
public static void ScanAssembliesAndRegisterDefinitions(this IObjectDefinitionRegistry registry, string assemblyScanPath, Func<string, bool> assemblyFilenamePredicate, Func<Assembly, bool> assemblyMetadataPredicate)
|
||||
public static void Scan(this IObjectDefinitionRegistry registry, IAssemblyObjectDefinitionScanner scanner)
|
||||
{
|
||||
IEnumerable<Assembly> assemblies = GetAllMatchingAssemblies(assemblyScanPath, assemblyFilenamePredicate);
|
||||
|
||||
assemblies = assemblies.Where(assembly => assemblyMetadataPredicate(assembly));
|
||||
|
||||
IEnumerable<Type> configTypes = GetAllConfigurationTypesDefinedIn(assemblies);
|
||||
IEnumerable<Type> configTypes = scanner.Scan();
|
||||
|
||||
//if we have at least one config class, ensure the post-processor is registered
|
||||
if (configTypes.Count() > 0)
|
||||
@@ -38,32 +24,45 @@ namespace Spring.Objects.Factory.Support
|
||||
RegisiterDefintionsForConfigTypes(configTypes, registry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scans the assemblies for definitions.
|
||||
/// </summary>
|
||||
/// <param name="registry">The registry.</param>
|
||||
/// <param name="assemblyMetadataPredicate">The assembly metadata predicate.</param>
|
||||
/// <returns></returns>
|
||||
public static void ScanAssembliesAndRegisterDefinitions(this IObjectDefinitionRegistry registry, Func<Assembly, bool> assemblyMetadataPredicate)
|
||||
public static void Scan(this IObjectDefinitionRegistry registry, Predicate<Type> typePredicate)
|
||||
{
|
||||
ScanAssembliesAndRegisterDefinitions(registry, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), fn => true, assemblyMetadataPredicate);
|
||||
Scan(registry, string.Empty, ta => true, typePredicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scans the assemblies for definitions.
|
||||
/// </summary>
|
||||
/// <param name="registry">The registry.</param>
|
||||
/// <param name="assemblyFilenamePredicate">The assembly filename predicate.</param>
|
||||
/// <param name="assemblyMetadataPredicate">The assembly metadata predicate.</param>
|
||||
/// <returns></returns>
|
||||
public static void ScanAssembliesAndRegisterDefinitions(this IObjectDefinitionRegistry registry, Func<string, bool> assemblyFilenamePredicate, Func<Assembly, bool> assemblyMetadataPredicate)
|
||||
public static void Scan(this IObjectDefinitionRegistry registry, string assemblyScanPath, Predicate<Assembly> assemblyPredicate, Predicate<Type> typePredicate)
|
||||
{
|
||||
ScanAssembliesAndRegisterDefinitions(registry, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), assemblyFilenamePredicate, assemblyMetadataPredicate);
|
||||
IAssemblyObjectDefinitionScanner scanner;
|
||||
|
||||
//create a scanner instance using the scan path (or not!) as appropropriate
|
||||
if (string.IsNullOrEmpty(assemblyScanPath))
|
||||
{
|
||||
scanner = new AssemblyObjectDefinitionScanner();
|
||||
}
|
||||
else
|
||||
{
|
||||
scanner = new AssemblyObjectDefinitionScanner(assemblyScanPath);
|
||||
}
|
||||
|
||||
//configure the scanner per the provided constraints
|
||||
scanner.WithAssemblyFilter(assemblyPredicate).WithIncludeFilter(typePredicate);
|
||||
|
||||
//pass the scanner to primary Scan method to actually do the work
|
||||
Scan(registry, scanner);
|
||||
}
|
||||
|
||||
public static void ScanAssembliesAndRegisterDefinitions(this IObjectDefinitionRegistry registry)
|
||||
public static void Scan(this IObjectDefinitionRegistry registry, Predicate<Assembly> assemblyPredicate)
|
||||
{
|
||||
ScanAssembliesAndRegisterDefinitions(registry, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), fn => true, a => true);
|
||||
Scan(registry, string.Empty, assemblyPredicate, t => true);
|
||||
}
|
||||
|
||||
public static void Scan(this IObjectDefinitionRegistry registry, Predicate<Assembly> assemblyPredicate, Predicate<Type> typePredicate)
|
||||
{
|
||||
Scan(registry, string.Empty, assemblyPredicate, typePredicate);
|
||||
}
|
||||
|
||||
public static void Scan(this IObjectDefinitionRegistry registry)
|
||||
{
|
||||
Scan(registry, new AssemblyObjectDefinitionScanner());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,59 +78,6 @@ namespace Spring.Objects.Factory.Support
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all configuration types defined in the assemblies.
|
||||
/// </summary>
|
||||
/// <param name="assemblies">The assemblies.</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<Type> GetAllConfigurationTypesDefinedIn(IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
IList<Type> types = new List<Type>();
|
||||
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
foreach (Type type in assembly.GetTypes())
|
||||
{
|
||||
if (Attribute.GetCustomAttribute(type, typeof(ConfigurationAttribute), true) != null)
|
||||
{
|
||||
types.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all matching assemblies.
|
||||
/// </summary>
|
||||
/// <param name="assemblyScanPath">The assembly scan path.</param>
|
||||
/// <param name="assemblyFilenamePredicate">The assembly filename predicate.</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<Assembly> GetAllMatchingAssemblies(string assemblyScanPath, Func<string, bool> assemblyFilenamePredicate)
|
||||
{
|
||||
IList<Assembly> assemblies = new List<Assembly>();
|
||||
|
||||
IEnumerable<string> files = Directory.GetFiles(assemblyScanPath, "*.dll").Where(s => assemblyFilenamePredicate(Path.GetFileName(s)));
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
assemblies.Add(Assembly.LoadFrom(file));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//log and swallow everything that might go wrong here...
|
||||
if (_logger.IsDebugEnabled)
|
||||
_logger.Debug("Failed to load type while scanning Assemblies for Defintions!", ex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return assemblies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regisiters the defintions for config types.
|
||||
/// </summary>
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Context\Advice\SpringObjectMethodInterceptor.cs" />
|
||||
<Compile Include="Context\Attributes\AssemblyObjectDefinitionScanner.cs" />
|
||||
<Compile Include="Context\Attributes\AssemblyTypeSource.cs" />
|
||||
<Compile Include="Context\Attributes\ConfigurationAttribute.cs" />
|
||||
<Compile Include="Context\Attributes\ConfigurationClass.cs" />
|
||||
<Compile Include="Context\Attributes\ConfigurationClassMethod.cs" />
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
using Spring.Context.Config;
|
||||
using Spring.Context.Support;
|
||||
using Spring.Context.Attributes;
|
||||
|
||||
namespace Spring.Objects.Factory.Support
|
||||
{
|
||||
@@ -14,30 +15,43 @@ namespace Spring.Objects.Factory.Support
|
||||
public class AssemblyScanningExtensionMethodsTests
|
||||
{
|
||||
[Test]
|
||||
public void Integration_Scenario_With_Assembly_Filename_And_Assembly_Metadata_Filtering()
|
||||
public void Integration_Scenario_With_Assembly_Filtering()
|
||||
{
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.ScanAssembliesAndRegisterDefinitions(fn => fn.StartsWith("Spring."), assy => assy.GetTypes().Any(type => type.FullName.Contains(typeof(MarkerTypeForScannerToFind).Name)));
|
||||
context.Scan(a => a.GetName().Name.StartsWith("Spring.Core.Configuration."));
|
||||
context.Refresh();
|
||||
|
||||
AssertExpectedObjectsAreRegisteredWith(context);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
//TODO: double check to ensure that this test really SHOULD pass...seems like its finding too wide a collection of assy's to scan... :(
|
||||
public void Integration_Scenario_With_Assembly_Filtering_Containing_Specific_Type()
|
||||
{
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.Scan(assy => assy.GetTypes().Any(type => type.FullName.Contains(typeof(MarkerTypeForScannerToFind).Name)));
|
||||
context.Refresh();
|
||||
|
||||
AssertExpectedObjectsAreRegisteredWith(context);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Integration_Scenario_With_Assembly_Metadata_Filtering()
|
||||
public void Integration_Scenario_With_Type_Filtering()
|
||||
{
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.ScanAssembliesAndRegisterDefinitions(assy => assy.GetTypes().Any(type => type.FullName.Contains(typeof(MarkerTypeForScannerToFind).Name)));
|
||||
context.Scan(type => ((Type)type).FullName.Contains(typeof(TheConfigurationClass).Name));
|
||||
context.Refresh();
|
||||
|
||||
AssertExpectedObjectsAreRegisteredWith(context);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Integration_Scenario_With_Default_of_No_Filtering()
|
||||
{
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.ScanAssembliesAndRegisterDefinitions();
|
||||
context.Scan();
|
||||
context.Refresh();
|
||||
|
||||
AssertExpectedObjectsAreRegisteredWith(context);
|
||||
|
||||
Reference in New Issue
Block a user