From 962e571ef53afb3f4aae7e1b5a9179c2dbe2a477 Mon Sep 17 00:00:00 2001 From: sbohlen Date: Sat, 20 Nov 2010 19:19:01 +0000 Subject: [PATCH] Initial attempt at fleshing out the scanning extension method API; further review and comment/discussion needed --- .../AttributeConfigObjectDefinitionParser.cs | 43 +++++ .../ComponentScanObjectDefinitionParser.cs | 15 ++ .../Context/Config/spring-context-1.3.xsd | 79 +++++++++ .../AssemblyScanningExtensionMethods.cs | 150 +++++++++++++++++ .../Spring.Core.Configuration.2010.csproj | 4 +- src/Spring/Spring.Core/ContextClassDiagram.cd | 153 ++++++++++++++++++ .../Spring.Core/Spring.Core.2010.csproj | 1 + .../ConfigurationClassPostProcessorTests.cs | 8 +- .../AssemblyScanningExtensionMethodsTests.cs | 56 +++++++ ...pring.Core.Configuration.Tests.2010.csproj | 1 + 10 files changed, 505 insertions(+), 5 deletions(-) create mode 100644 src/Spring/Spring.Core.Configuration/Context/Config/AttributeConfigObjectDefinitionParser.cs create mode 100644 src/Spring/Spring.Core.Configuration/Context/Config/ComponentScanObjectDefinitionParser.cs create mode 100644 src/Spring/Spring.Core.Configuration/Context/Config/spring-context-1.3.xsd create mode 100644 src/Spring/Spring.Core.Configuration/Objects/Factory/Support/AssemblyScanningExtensionMethods.cs create mode 100644 src/Spring/Spring.Core/ContextClassDiagram.cd create mode 100644 test/Spring/Spring.Core.Configuration.Tests/Objects/Factory/Support/AssemblyScanningExtensionMethodsTests.cs diff --git a/src/Spring/Spring.Core.Configuration/Context/Config/AttributeConfigObjectDefinitionParser.cs b/src/Spring/Spring.Core.Configuration/Context/Config/AttributeConfigObjectDefinitionParser.cs new file mode 100644 index 00000000..9e79a0e5 --- /dev/null +++ b/src/Spring/Spring.Core.Configuration/Context/Config/AttributeConfigObjectDefinitionParser.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Spring.Objects.Factory.Xml; +using Spring.Objects.Factory.Support; +using Spring.Util; +using Spring.Context.Attributes; +using Spring.Objects.Factory.Config; + +namespace Spring.Context.Config +{ + public class AttributeConfigObjectDefinitionParser : IObjectDefinitionParser + { + /// + /// The object name of the internally managed configuration attribure processor + /// + public static readonly string CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME = "Spring.Context.Attributes.InternalConfigurationAttributeProcessor"; + + + private static readonly Type ConfigurationClassPostProcessorType = typeof(ConfigurationClassPostProcessor); + + public AttributeConfigObjectDefinitionParser() + { + + } + + public Objects.Factory.Config.IObjectDefinition ParseElement(System.Xml.XmlElement element, ParserContext parserContext) + { + IObjectDefinitionRegistry registry = parserContext.ReaderContext.Registry; + + AssertUtils.ArgumentNotNull(registry, "registry"); + + if (!registry.ContainsObjectDefinition(CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME)) + { + RootObjectDefinition objectDefinition = new RootObjectDefinition(ConfigurationClassPostProcessorType); + objectDefinition.Role = ObjectRole.ROLE_INFRASTRUCTURE; + registry.RegisterObjectDefinition(CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME, objectDefinition); + } + + return null; + } + } +} diff --git a/src/Spring/Spring.Core.Configuration/Context/Config/ComponentScanObjectDefinitionParser.cs b/src/Spring/Spring.Core.Configuration/Context/Config/ComponentScanObjectDefinitionParser.cs new file mode 100644 index 00000000..a39f2956 --- /dev/null +++ b/src/Spring/Spring.Core.Configuration/Context/Config/ComponentScanObjectDefinitionParser.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Config +{ + public class ComponentScanObjectDefinitionParser + { + public ComponentScanObjectDefinitionParser() + { + + } + } +} diff --git a/src/Spring/Spring.Core.Configuration/Context/Config/spring-context-1.3.xsd b/src/Spring/Spring.Core.Configuration/Context/Config/spring-context-1.3.xsd new file mode 100644 index 00000000..7a2ec0eb --- /dev/null +++ b/src/Spring/Spring.Core.Configuration/Context/Config/spring-context-1.3.xsd @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + tag for that purpose. + ]]> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Core.Configuration/Objects/Factory/Support/AssemblyScanningExtensionMethods.cs b/src/Spring/Spring.Core.Configuration/Objects/Factory/Support/AssemblyScanningExtensionMethods.cs new file mode 100644 index 00000000..01f4d1b5 --- /dev/null +++ b/src/Spring/Spring.Core.Configuration/Objects/Factory/Support/AssemblyScanningExtensionMethods.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.Linq; +using System.IO; +using Common.Logging; +using Spring.Context.Attributes; + +namespace Spring.Objects.Factory.Support +{ + public static class AssemblyScanningExtensionMethods + { + private static ILog _logger = LogManager.GetLogger(typeof(AssemblyScanningExtensionMethods)); + + /// + /// Scans the assemblies for definitions. + /// + /// The registry. + /// The assembly scan path. + /// The assembly filename predicate. + /// The assembly metadata predicate. + /// + public static void ScanAssembliesAndRegisterDefinitions(this IObjectDefinitionRegistry registry, string assemblyScanPath, Func assemblyFilenamePredicate, Func assemblyMetadataPredicate) + { + IEnumerable assemblies = GetAllMatchingAssemblies(assemblyScanPath, assemblyFilenamePredicate); + + assemblies = assemblies.Where(assembly => assemblyMetadataPredicate(assembly)); + + IEnumerable configTypes = GetAllConfigurationTypesDefinedIn(assemblies); + + //if we have at least one config class, ensure the post-processor is registered + if (configTypes.Count() > 0) + { + EnsureConfigurationClassPostProcessorIsRegisteredFor(registry); + } + + RegisiterDefintionsForConfigTypes(configTypes, registry); + } + + /// + /// Scans the assemblies for definitions. + /// + /// The registry. + /// The assembly metadata predicate. + /// + public static void ScanAssembliesAndRegisterDefinitions(this IObjectDefinitionRegistry registry, Func assemblyMetadataPredicate) + { + ScanAssembliesAndRegisterDefinitions(registry, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), fn => true, assemblyMetadataPredicate); + } + + /// + /// Scans the assemblies for definitions. + /// + /// The registry. + /// The assembly filename predicate. + /// The assembly metadata predicate. + /// + public static void ScanAssembliesAndRegisterDefinitions(this IObjectDefinitionRegistry registry, Func assemblyFilenamePredicate, Func assemblyMetadataPredicate) + { + ScanAssembliesAndRegisterDefinitions(registry, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), assemblyFilenamePredicate, assemblyMetadataPredicate); + } + + public static void ScanAssembliesAndRegisterDefinitions(this IObjectDefinitionRegistry registry) + { + ScanAssembliesAndRegisterDefinitions(registry, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), fn => true, a => true); + } + + /// + /// Ensures the configuration class post processor is registered for. + /// + /// The registry. + private static void EnsureConfigurationClassPostProcessorIsRegisteredFor(IObjectDefinitionRegistry registry) + { + var postProcessorBuilder = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(ConfigurationClassPostProcessor)); + if (!registry.ContainsObjectDefinition(postProcessorBuilder.ObjectDefinition.ObjectTypeName)) + { + registry.RegisterObjectDefinition(postProcessorBuilder.ObjectDefinition.ObjectTypeName, postProcessorBuilder.ObjectDefinition); + } + } + + /// + /// Gets all configuration types defined in the assemblies. + /// + /// The assemblies. + /// + private static IEnumerable GetAllConfigurationTypesDefinedIn(IEnumerable assemblies) + { + IList types = new List(); + + foreach (Assembly assembly in assemblies) + { + foreach (Type type in assembly.GetTypes()) + { + if (Attribute.GetCustomAttribute(type, typeof(ConfigurationAttribute), true) != null) + { + types.Add(type); + } + } + } + + return types; + } + + /// + /// Gets all matching assemblies. + /// + /// The assembly scan path. + /// The assembly filename predicate. + /// + private static IEnumerable GetAllMatchingAssemblies(string assemblyScanPath, Func assemblyFilenamePredicate) + { + IList assemblies = new List(); + + IEnumerable 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; + } + + /// + /// Regisiters the defintions for config types. + /// + /// The config types. + /// The registry. + private static void RegisiterDefintionsForConfigTypes(IEnumerable configTypes, IObjectDefinitionRegistry registry) + { + foreach (Type configType in configTypes) + { + ObjectDefinitionBuilder definition = ObjectDefinitionBuilder.GenericObjectDefinition(configType); + registry.RegisterObjectDefinition(definition.ObjectDefinition.ObjectTypeName, definition.ObjectDefinition); + } + } + + } +} diff --git a/src/Spring/Spring.Core.Configuration/Spring.Core.Configuration.2010.csproj b/src/Spring/Spring.Core.Configuration/Spring.Core.Configuration.2010.csproj index 422d636c..730a69bc 100644 --- a/src/Spring/Spring.Core.Configuration/Spring.Core.Configuration.2010.csproj +++ b/src/Spring/Spring.Core.Configuration/Spring.Core.Configuration.2010.csproj @@ -10,8 +10,9 @@ Properties Spring Spring.Core.Configuration - v2.0 + v3.5 512 + true @@ -56,6 +57,7 @@ + diff --git a/src/Spring/Spring.Core/ContextClassDiagram.cd b/src/Spring/Spring.Core/ContextClassDiagram.cd new file mode 100644 index 00000000..7a48ca13 --- /dev/null +++ b/src/Spring/Spring.Core/ContextClassDiagram.cd @@ -0,0 +1,153 @@ + + + + + + SHQRYAEIiGBAtMBASYTEWAQkWBAiChEMaAACCAFBXMg= + Context\Support\AbstractApplicationContext.cs + + + + + + + AAQACAAAAABAAACBAAQAAQAAABAAAAAAABAAAAACAEA= + Context\Support\AbstractXmlApplicationContext.cs + + + + + + AAQAAAAAAAAAAAAAAAQAAAAAABAAIAAAAAAAAAAEAAA= + Context\Support\GenericApplicationContext.cs + + + + + + AAAACAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAIAEAAA= + Context\Support\XmlApplicationContext.cs + + + + + + AAEAAAAAAAAAAAAIAAQAAAAAAgAAAAAAAAAAAAAAAAA= + Context\Support\StaticApplicationContext.cs + + + + + + AAAAAAAAAAAAggAAAQAAAAAABAAAAAAAAAAASAAAAAA= + Core\IO\ConfigurableResourceLoader.cs + + + + + + + AAQAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEA= + Context\IConfigurableApplicationContext.cs + + + + + + AAAAAAAAAAAAEAAAAACAAAQAAAAAAAAAAAAAAAAACAA= + Context\IApplicationContext.cs + + + + + + AAAAAAAAACAAAAAAAAAAQAAAAAAAAAAAIAAAAAAAAAA= + Context\ILifecycle.cs + + + + + + ABAAAAAAAAAAIIAACIAACAAAAAAAAgEMAAAAAAAAAIA= + Objects\Factory\IObjectFactory.cs + + + + + + AAAAAAAAAAAAAACAAAAAAAAIAAQAAAAgEAAAAAAAAAA= + Objects\Factory\Config\IAutowireCapableObjectFactory.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAA= + Objects\Factory\IHierarchicalObjectFactory.cs + + + + + + CAAAAAAAAAAAAAAAQABAEAAAAAAAAAAACAAAAAAAAAA= + Objects\Factory\IListableObjectFactory.cs + + + + + + AAAAAAAAAAAAAIAAAIAEAAAgAAAAAAAAAAAACABAAAA= + Objects\Factory\Config\IConfigurableListableObjectFactory.cs + + + + + + AAAAAAAABCAAAAAAAAACAAAAQAAEAAAAAAAAAQAAEAA= + Objects\Factory\Config\IConfigurableObjectFactory.cs + + + + + + AAAAAAAAEAAAABAIAAAAAAAAAAAAAAAAAAAAAgAAAIA= + Objects\Factory\Config\ISingletonObjectRegistry.cs + + + + + + ACAAAAAAAAAAAAAAAAAAAAAACAAAABAAAAAAAAAAAAA= + Context\IMessageSource.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA= + Context\IApplicationEventPublisher.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAA= + Core\IO\IResourceLoader.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAQAAAAAAAAIA= + Objects\Events\IEventRegistry.cs + + + + + + CBAAAAAAAAAAAAAAQAAEEAAgABAAAAAAAAAAAAAAEAA= + Objects\Factory\Support\IObjectDefinitionRegistry.cs + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Core/Spring.Core.2010.csproj b/src/Spring/Spring.Core/Spring.Core.2010.csproj index 82436338..dd761999 100644 --- a/src/Spring/Spring.Core/Spring.Core.2010.csproj +++ b/src/Spring/Spring.Core/Spring.Core.2010.csproj @@ -1218,6 +1218,7 @@ Code + diff --git a/test/Spring/Spring.Core.Configuration.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs b/test/Spring/Spring.Core.Configuration.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs index d1894cf7..5cb6190a 100644 --- a/test/Spring/Spring.Core.Configuration.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs +++ b/test/Spring/Spring.Core.Configuration.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs @@ -18,11 +18,11 @@ namespace Spring.Context.Attributes { _ctx = new GenericApplicationContext(); - var builder = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(TheConfigurationClass)); - _ctx.RegisterObjectDefinition("theConfigClass", builder.ObjectDefinition); + var configDefinitionBuilder = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(TheConfigurationClass)); + _ctx.RegisterObjectDefinition(configDefinitionBuilder.ObjectDefinition.ObjectTypeName, configDefinitionBuilder.ObjectDefinition); - var b2 = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(ConfigurationClassPostProcessor)); - _ctx.RegisterObjectDefinition("thePostProcessor", b2.ObjectDefinition); + var postProcessorDefintionBuilder = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(ConfigurationClassPostProcessor)); + _ctx.RegisterObjectDefinition(postProcessorDefintionBuilder.ObjectDefinition.ObjectTypeName, postProcessorDefintionBuilder.ObjectDefinition); Assert.That(_ctx.ObjectDefinitionCount, Is.EqualTo(2)); diff --git a/test/Spring/Spring.Core.Configuration.Tests/Objects/Factory/Support/AssemblyScanningExtensionMethodsTests.cs b/test/Spring/Spring.Core.Configuration.Tests/Objects/Factory/Support/AssemblyScanningExtensionMethodsTests.cs new file mode 100644 index 00000000..89810733 --- /dev/null +++ b/test/Spring/Spring.Core.Configuration.Tests/Objects/Factory/Support/AssemblyScanningExtensionMethodsTests.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using System.Reflection; +using System.Diagnostics; +using Spring.Context.Config; +using Spring.Context.Support; + +namespace Spring.Objects.Factory.Support +{ + [TestFixture] + public class AssemblyScanningExtensionMethodsTests + { + [Test] + public void Integration_Scenario_With_Defaults() + { + GenericApplicationContext context = new GenericApplicationContext(); + context.ScanAssembliesAndRegisterDefinitions();// (assy => assy.GetTypes().Any(type => type.FullName.Contains("SomeType"))); + context.Refresh(); + + AssertExpectedObjectsAreRegisteredWith(context); + } + + [Test] + public void Integration_Scenario_With_Filtering_Example() + { + GenericApplicationContext context = new GenericApplicationContext(); + context.ScanAssembliesAndRegisterDefinitions(assy => assy.GetTypes().Any(type => type.FullName.Contains(typeof(MarkerTypeForScannerToFind).Name))); + context.Refresh(); + + AssertExpectedObjectsAreRegisteredWith(context); + } + + private static void AssertExpectedObjectsAreRegisteredWith(GenericApplicationContext context) + { + Assert.That(context.DefaultListableObjectFactory.ObjectDefinitionCount, Is.EqualTo(13)); + } + [Test] + public void Integration_Scenario_With_Complex_Filtering_Example() + { + GenericApplicationContext context = new GenericApplicationContext(); + context.ScanAssembliesAndRegisterDefinitions(fn => fn.StartsWith("Spring."), assy => assy.GetTypes().Any(type => type.FullName.Contains(typeof(MarkerTypeForScannerToFind).Name))); + context.Refresh(); + + AssertExpectedObjectsAreRegisteredWith(context); + } + } + + + public class MarkerTypeForScannerToFind + { + + } +} diff --git a/test/Spring/Spring.Core.Configuration.Tests/Spring.Core.Configuration.Tests.2010.csproj b/test/Spring/Spring.Core.Configuration.Tests/Spring.Core.Configuration.Tests.2010.csproj index 6a7f1d11..5da6ef07 100644 --- a/test/Spring/Spring.Core.Configuration.Tests/Spring.Core.Configuration.Tests.2010.csproj +++ b/test/Spring/Spring.Core.Configuration.Tests/Spring.Core.Configuration.Tests.2010.csproj @@ -44,6 +44,7 @@ +