diff --git a/src/Spring/Spring.Core/Context/Attributes/AssemblyObjectDefinitionScanner.cs b/src/Spring/Spring.Core/Context/Attributes/AssemblyObjectDefinitionScanner.cs new file mode 100644 index 00000000..5618aa93 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/AssemblyObjectDefinitionScanner.cs @@ -0,0 +1,189 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; +using System.Reflection; +using Spring.Objects.Factory.Support; +using Spring.Stereotype; + +namespace Spring.Context.Attributes +{ + /// + /// AssemblyTypeScanner that only accepts types that also meet the requirements of being ObjectDefintions. + /// + [Serializable] + public class AssemblyObjectDefinitionScanner : RequiredConstraintAssemblyTypeScanner + { + private readonly List> _assemblyExclusionPredicates = new List>(); + + private readonly IList _springAssemblies = new List() + { + "Spring.Core", + "Spring.Core.Configuration", + "Spring.Aop", + "Spring.Data", + "Spring.Services", + "Spring.Messaging", + "Spring.Messaging.Ems", + "Spring.Messaging.Nms", + "Spring.Template.Velocity", + "Spring.Messaging.Quartz", + "Spring.Testing.Microsoft", + "Spring.Testing.Nunit", + "Spring.Data.NHibernate12", + "Spring.Data.NHibernate21", + "Spring.Data.NHibernate20", + "Spring.Data.NHibernate30", + "Spring.Web", + "Spring.Web.Extensions", + "Spring.Web.Mvc", + }; + + private IObjectNameGenerator _objectNameGenerator = new AttributeObjectNameGenerator(); + + /// + /// Provides the name generator for all scanned objects. + /// Default is + /// + public IObjectNameGenerator ObjectNameGenerator + { + get { return _objectNameGenerator; } + set { _objectNameGenerator = value; } + } + + /// + /// Registers the defintions for types. + /// + /// The registry. + /// The types to register. + private void RegisterDefinitionsForTypes(IObjectDefinitionRegistry registry, IEnumerable typesToRegister) + { + foreach (Type type in typesToRegister) + { + var definition = new ScannedGenericObjectDefinition(type, Defaults); + string objectName = ObjectNameGenerator.GenerateObjectName(definition, registry); + registry.RegisterObjectDefinition(objectName, definition); + } + } + + + /// + /// Applies the assembly filters to the assembly candidates. + /// + /// The assembly candidates. + /// + protected override IEnumerable ApplyAssemblyFiltersTo(IEnumerable assemblyCandidates) + { + return assemblyCandidates.Where( + delegate(Assembly candidate) { return IsIncludedAssembly(candidate) && !IsExcludedAssembly(candidate); }); + } + + /// + /// Determines whether the specified candidate is and excluded assembly. + /// + /// The candidate. + /// + /// true if the specified candidate is an excluded assembly ; otherwise, false. + /// + protected virtual bool IsExcludedAssembly(Assembly candidate) + { + return _assemblyExclusionPredicates.Any(delegate(Predicate exclude) { return exclude(candidate); }); + } + + /// + /// Determines whether the required constraint is satisfied by the specified type. + /// + /// The type. + /// + /// true if the required constraint is satisfied by the specified type; otherwise, false. + /// + protected override bool IsRequiredConstraintSatisfiedBy(Type type) + { + if (!type.Assembly.ReflectionOnly) + { + try + { + return Attribute.GetCustomAttribute(type, typeof(ComponentAttribute), true) != null && + !type.IsAbstract; + } + catch (AmbiguousMatchException) + { + Logger.Error(m => m("Type {0} has more than one ComponentAttributes assigned to it.", type.FullName)); + return false; + } + } + + bool satisfied = false; + + foreach (CustomAttributeData customAttributeData in CustomAttributeData.GetCustomAttributes(type)) + { + if (customAttributeData.Constructor.DeclaringType.FullName == typeof(ComponentAttribute).FullName && + !type.IsAbstract) + { + satisfied = true; + break; + } + } + + return satisfied; + } + + /// + /// Sets the default filters. + /// + protected override void SetDefaultFilters() + { + //set the built-in defaults + base.SetDefaultFilters(); + + //add the desired assembly exclusions to the list + _assemblyExclusionPredicates.Add( + delegate(Assembly a) { return _springAssemblies.Contains(a.GetName().Name); }); + _assemblyExclusionPredicates.Add(delegate(Assembly a) { return a.GetName().Name.StartsWith("System."); }); + _assemblyExclusionPredicates.Add(delegate(Assembly a) { return a.GetName().Name.StartsWith("Microsoft."); }); + _assemblyExclusionPredicates.Add(delegate(Assembly a) { return a.GetName().Name == "mscorlib"; }); + _assemblyExclusionPredicates.Add(delegate(Assembly a) { return a.GetName().Name == "System"; }); + } + + /// + /// Scans the and register types. + /// + /// The registry within which to register the types. + public virtual void ScanAndRegisterTypes(IObjectDefinitionRegistry registry) + { + IEnumerable configTypes = base.Scan(); + RegisterDefinitionsForTypes(registry, configTypes); + } + + /// + /// Initializes a new instance of the class. + /// + public AssemblyObjectDefinitionScanner() + { + AssemblyLoadExclusionPredicates.Add(delegate(string name) { return _springAssemblies.Contains(name); }); + AssemblyLoadExclusionPredicates.Add(delegate(string name) { return name.StartsWith("System."); }); + AssemblyLoadExclusionPredicates.Add(delegate(string name) { return name.StartsWith("Microsoft."); }); + AssemblyLoadExclusionPredicates.Add(delegate(string name) { return name == "mscorlib"; }); + AssemblyLoadExclusionPredicates.Add(delegate(string name) { return name == "System"; }); + } + + } +} \ 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 new file mode 100644 index 00000000..d41bedab --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/AssemblyTypeScanner.cs @@ -0,0 +1,400 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; +using System.IO; +using System.Reflection; +using Common.Logging; +using Spring.Context.Attributes.TypeFilters; +using Spring.Util; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Attributes +{ + /// + /// Scans Assebmlies for Types that satisfy a given set of constraints. + /// + [Serializable] + public abstract class AssemblyTypeScanner : IAssemblyTypeScanner + { + /// + /// Logger Instance. + /// + protected static readonly ILog Logger = LogManager.GetLogger(); + + /// + /// Names of Assemblies to exclude from being loaded for scanning. + /// + protected IList> AssemblyLoadExclusionPredicates = new List>(); + + /// + /// Assembly Inclusion Predicates. + /// + protected readonly List> AssemblyInclusionPredicates = new List>(); + + /// + /// Type Exclusion Predicates. + /// + protected readonly List> TypeExclusionPredicates = new List>(); + + /// + /// Type Exclusion Predicates. + /// + protected readonly List TypeExclusionTypeFilters = new List(); + + /// + /// Type Inclusion Predicates. + /// + protected readonly List> TypeInclusionPredicates = new List>(); + + /// + /// Type Inclusion TypeFilters. + /// + protected readonly List TypeInclusionTypeFilter = new List(); + + /// + /// Assemblies to scan. + /// + protected readonly List> TypeSources = new List>(); + + /// + /// Stores the object default definitons defined in the XML configuration documnet + /// + protected DocumentDefaultsDefinition _defaults; + + /// + /// Stores the object default definitons defined in the XML configuration documnet + /// + public DocumentDefaultsDefinition Defaults { get { return _defaults; } set { _defaults = value; } } + + #region IAssemblyTypeScanner Members + + /// + /// Assemblies the type of the having. + /// + /// + /// + public IAssemblyTypeScanner AssemblyHavingType() + { + TypeSources.Add(new AssemblyTypeSource((typeof(T).Assembly))); + return this; + } + + /// + /// Excludes the type. + /// + /// + /// + public IAssemblyTypeScanner ExcludeType() + { + TypeExclusionPredicates.Add(delegate(Type t) { return t.FullName == typeof(T).FullName; }); + return this; + } + + /// + /// Includes the type. + /// + /// + /// + public IAssemblyTypeScanner IncludeType() + { + TypeInclusionPredicates.Add(delegate(Type t) { return t.FullName == typeof(T).FullName; }); + return this; + } + + /// + /// Includes the types. + /// + /// The type source. + /// + public IAssemblyTypeScanner IncludeTypes(IEnumerable typeSource) + { + AssertUtils.ArgumentNotNull(typeSource, "typeSource"); + TypeSources.Add(typeSource); + TypeInclusionPredicates.Add( + delegate(Type t) { return typeSource.Any(delegate(Type t1) { return t1.FullName == t.FullName; }); }); + return this; + } + + /// + /// Performs the Scan, respecting all filter settings. + /// + /// + public virtual IEnumerable Scan() + { + SetDefaultFilters(); + + IList types = new List(); + + foreach (Assembly assembly in GetAllMatchingAssemblies()) + { + TypeSources.Add(new AssemblyTypeSource(assembly)); + } + + foreach (var typeSource in TypeSources) + { + foreach (Type type in typeSource) + { + if (IsCompoundPredicateSatisfiedBy(type)) + { + types.Add(type); + } + } + } + + return types; + } + + /// + /// Adds the assembly filter. + /// + /// The assembly predicate. + /// + public IAssemblyTypeScanner WithAssemblyFilter(Predicate assemblyPredicate) + { + AssemblyInclusionPredicates.Add(assemblyPredicate); + return this; + } + + /// + /// Adds the exclude filter. + /// + /// The predicate. + /// + public IAssemblyTypeScanner WithExcludeFilter(Predicate predicate) + { + TypeExclusionPredicates.Add(predicate); + return this; + } + + /// + /// Adds the exclude filter. + /// + /// The type filter. + /// + public IAssemblyTypeScanner WithExcludeFilter(ITypeFilter filter) + { + if (filter != null) + TypeExclusionTypeFilters.Add(filter); + + return this; + } + + /// + /// Adds the include filter. + /// + /// The predicate. + /// + public IAssemblyTypeScanner WithIncludeFilter(Predicate predicate) + { + TypeInclusionPredicates.Add(predicate); + return this; + } + + /// + /// Adds the include filter. + /// + /// The filter type. + /// + public IAssemblyTypeScanner WithIncludeFilter(ITypeFilter filter) + { + if (filter != null) + TypeInclusionTypeFilter.Add(filter); + + return this; + } + + #endregion + + private List GetAllAssembliesInPath() + { + + 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() + { + IEnumerable assemblyCandidates = GetAllAssembliesInPath(); + + IList assemblies = new List(); + + foreach (string assembly in assemblyCandidates) + { + if (!string.IsNullOrEmpty(assembly)) + { + Assembly loadedAssembly = TryLoadAssemblyFromPath(assembly); + + if (null != loadedAssembly) + { + assemblies.Add(loadedAssembly); + } + } + } + + return ApplyAssemblyFiltersTo(assemblies); + } + + private Assembly TryLoadAssemblyFromPath(string filename) + { + Assembly assembly = null; + + try + { + assembly = Assembly.LoadFrom(filename); + } + catch (Exception ex) + { + //log and swallow everything that might go wrong here... + Logger.Debug(m => m("Failed to load assembly {0} to inspect for [Configuration] types!", filename), ex); + } + + return assembly; + } + + private string GetCurrentBinDirectoryPath() + { + return string.IsNullOrEmpty(AppDomain.CurrentDomain.DynamicDirectory) + ? AppDomain.CurrentDomain.BaseDirectory + : AppDomain.CurrentDomain.DynamicDirectory; + } + + /// + /// Applies the assembly filters to the assembly candidates. + /// + /// The assembly candidates. + /// + protected virtual IEnumerable ApplyAssemblyFiltersTo(IEnumerable assemblyCandidates) + { + return + assemblyCandidates.Where(IsIncludedAssembly). + AsEnumerable(); + } + + /// + /// Determines whether the compound predicate is satisfied by the specified type. + /// + /// The type. + /// + /// true if the compound predicate is satisfied by the specified type; otherwise, false. + /// + protected abstract bool IsCompoundPredicateSatisfiedBy(Type type); + + /// + /// Determines whether [is excluded type] [the specified type]. + /// + /// The type. + /// + /// true if [is excluded type] [the specified type]; otherwise, false. + /// + protected virtual bool IsExcludedType(Type type) + { + if (TypeExclusionPredicates.Count > 0 && TypeExclusionPredicates.Any(delegate(Predicate exclude) { return exclude(type); })) + return true; + + foreach(var filter in TypeExclusionTypeFilters) + { + if (filter.Match(type)) + return true; + } + return false; + } + + /// + /// Determines whether [is included assembly] [the specified assembly]. + /// + /// The assembly. + /// + /// true if [is included assembly] [the specified assembly]; otherwise, false. + /// + protected virtual bool IsIncludedAssembly(Assembly assembly) + { + return AssemblyInclusionPredicates.Any(delegate(Predicate include) { return include(assembly); }); + } + + /// + /// Determines whether [is included type] [the specified type]. + /// + /// The type. + /// + /// true if [is included type] [the specified type]; otherwise, false. + /// + protected virtual bool IsIncludedType(Type type) + { + if (TypeInclusionPredicates.Count > 0 && TypeInclusionPredicates.Any(delegate(Predicate include) { return include(type); })) + return true; + + foreach(var filter in TypeInclusionTypeFilter) + { + if (filter.Match(type)) + return true; + } + return false; + } + + /// + /// Sets the default filters. + /// + protected virtual void SetDefaultFilters() + { + if (TypeInclusionPredicates.Count == 0 && TypeInclusionTypeFilter.Count == 0) + TypeInclusionPredicates.Add(delegate { return true; }); + + if (TypeExclusionPredicates.Count == 0 && TypeExclusionTypeFilters.Count == 0) + TypeExclusionPredicates.Add(delegate { return false; }); + + if (AssemblyInclusionPredicates.Count == 0) + AssemblyInclusionPredicates.Add(delegate { return true; }); + } + + /// + /// Loads the assemblies found. + /// + /// The folder path. + /// The extension. + private IList DiscoverAssemblies(string folderPath, string extension) + { + IList assemblies = new List(); + + IEnumerable files = Directory.GetFiles(folderPath, extension, SearchOption.AllDirectories); + + foreach (string file in files) + { + string name = Path.GetFileNameWithoutExtension(file); + + if (!AssemblyLoadExclusionPredicates.Any(delegate(Predicate exclude) { return exclude(name); })) + { + assemblies.Add(file); + } + + } + + return assemblies; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/AssemblyTypeSource.cs b/src/Spring/Spring.Core/Context/Attributes/AssemblyTypeSource.cs new file mode 100644 index 00000000..31d7b073 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/AssemblyTypeSource.cs @@ -0,0 +1,63 @@ +#region License + +/* + * Copyright © 2010-2011 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 +{ + /// + /// Represents a collection of Types. + /// + [Serializable] + public class AssemblyTypeSource : IEnumerable + { + private readonly _Assembly _assembly; + + /// + /// Initializes a new instance of the class. + /// + /// The assembly. + public AssemblyTypeSource(Assembly assembly) + { + AssertUtils.ArgumentNotNull(assembly, "assembly"); + this._assembly = assembly; + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator GetEnumerator() + { + foreach (var type in _assembly.GetTypes()) + yield return type; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/AttributeConfigUtils.cs b/src/Spring/Spring.Core/Context/Attributes/AttributeConfigUtils.cs new file mode 100644 index 00000000..365c69ac --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/AttributeConfigUtils.cs @@ -0,0 +1,104 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Objects.Factory.Attributes; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Attributes +{ + /// + /// Utility class that allows for convenient registration of common + /// and definitions for attribute based configuration + /// + /// + /// + /// + /// Mark Pollack (.NET) + /// Mark Fisher + /// Juergen Hoeller + /// Chris Beams + public class AttributeConfigUtils + { + + /// + /// The object name of the internally managed Configuration attribute processor. + /// + public static readonly string CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME = + "Spring.Context.Attributes.InternalConfigurationClassPostProcessor"; + + /// + /// The object name of the internally managed Autowire attribute processor + /// + public static readonly string AUTOWIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME = + "Spring.Context.Attributes.InternalAutowiredClassPostProcessor"; + + /// + ///The object name of the internally managed Required attribute processor. + /// + public static readonly string REQUIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME = + "Spring.Context.Attributes.InternalRequiredClassPostProcessor"; + + /// + ///The object name of the internally managed InitDestroy attribute processor. + /// + public static readonly string INITDESTROY_ATTRIBUTE_PROCESSOR_OBJECT_NAME = + "Spring.Context.Attributes.InternalInitDestroyClassPostProcessor"; + + + /// + /// Registers the attribute config processors. + /// + /// The registry. + public static void RegisterAttributeConfigProcessors(IObjectDefinitionRegistry registry) + { + if (!registry.ContainsObjectDefinition(CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME)) + { + RootObjectDefinition objectDefinition = new RootObjectDefinition(typeof(ConfigurationClassPostProcessor)); + RegisterPostProcessor(registry, objectDefinition, CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME); + } + + if (!registry.ContainsObjectDefinition(AUTOWIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME)) + { + RootObjectDefinition objectDefinition = new RootObjectDefinition(typeof(AutowiredAttributeObjectPostProcessor)); + RegisterPostProcessor(registry, objectDefinition, AUTOWIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME); + } + + if (!registry.ContainsObjectDefinition(REQUIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME)) + { + RootObjectDefinition objectDefinition = new RootObjectDefinition(typeof(RequiredAttributeObjectPostProcessor)); + RegisterPostProcessor(registry, objectDefinition, REQUIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME); + } + + if (!registry.ContainsObjectDefinition(INITDESTROY_ATTRIBUTE_PROCESSOR_OBJECT_NAME)) + { + RootObjectDefinition objectDefinition = new RootObjectDefinition(typeof(InitDestroyAttributeObjectPostProcessor)); + RegisterPostProcessor(registry, objectDefinition, INITDESTROY_ATTRIBUTE_PROCESSOR_OBJECT_NAME); + } + } + + private static void RegisterPostProcessor(IObjectDefinitionRegistry registry, IConfigurableObjectDefinition objectDefinition, string objectName) + { + objectDefinition.Role = ObjectRole.ROLE_INFRASTRUCTURE; + registry.RegisterObjectDefinition(objectName, objectDefinition); + } + } + +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/AttributeObjectNameGenerator.cs b/src/Spring/Spring.Core/Context/Attributes/AttributeObjectNameGenerator.cs new file mode 100644 index 00000000..f98c1da2 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/AttributeObjectNameGenerator.cs @@ -0,0 +1,59 @@ +#region License + +/* + * Copyright 2002-2010 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 Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Attributes +{ + /// + /// Default Name Generator for attribute driven component scan. + /// + /// First choice is the provided name of the Component attribute. + /// Fallback is the short type name. + /// + public class AttributeObjectNameGenerator : IObjectNameGenerator + { + /// + /// Generates an object name for the given object definition. + /// + /// The object definition to generate a name for. + /// The object definitions registry that the given definition is + /// supposed to be registerd with + /// + /// the generated object name + /// + public string GenerateObjectName(IObjectDefinition definition, IObjectDefinitionRegistry registry) + { + if (definition is ScannedGenericObjectDefinition) + { + string objectName = ((ScannedGenericObjectDefinition) definition).ComponentName; + if (!string.IsNullOrEmpty(objectName)) + return objectName; + } + return BuildDefaultObjectName(definition); + } + + private string BuildDefaultObjectName(IObjectDefinition definition) + { + return definition.ObjectType.FullName; + } + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/ConfigurationAttribute.cs b/src/Spring/Spring.Core/Context/Attributes/ConfigurationAttribute.cs new file mode 100644 index 00000000..8a4fa6fb --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ConfigurationAttribute.cs @@ -0,0 +1,65 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Stereotype; + +namespace Spring.Context.Attributes +{ + /// + /// Indicates that a class declares one or more methods and may be processed + /// by the Spring container to generate object definitions and service requests for those objects + /// at runtime. + /// + /// Configuration is meta-annotated as a , therefore Configuration + /// classes are candidates for component-scanning. + /// + /// May be used in conjunction with the attribute to indicate that all object + /// methods declared within this class are by default lazily initialized. + /// + ///

Constraints

+ ///
    + ///
  • Configuration classes must be non-sealed
  • + ///
  • Configuration classes must have a default/no-arg constructor
  • + ///
+ ///
+ [AttributeUsage(AttributeTargets.Class)] + public class ConfigurationAttribute : ComponentAttribute + { + + /// + /// Initializes a new instance of the ConfigurationAttribute class. + /// + public ConfigurationAttribute() + { + + } + + /// + /// Initializes a new instance of the Configuration class. + /// + /// + public ConfigurationAttribute(string name) + { + Name = name; + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/ConfigurationClass.cs b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClass.cs new file mode 100644 index 00000000..59e19b92 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClass.cs @@ -0,0 +1,252 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; +using System.Reflection; +using System.Text; +using Spring.Core.IO; +using Spring.Collections.Generic; +using Spring.Objects.Factory.Parsing; + +namespace Spring.Context.Attributes +{ + /// + /// Represents an instance of the metadata that has been parsed from a class with the applied to it. + /// + public class ConfigurationClass + { + private Type _configurationClassType; + + private readonly IDictionary _importedResources = new Dictionary(); + + private readonly Collections.Generic.ISet _methods = new HashedSet(); + + private string _objectName; + + private readonly IResource _resource; + + /// + /// Initializes a new instance of the ConfigurationClass class. + /// + /// + /// + public ConfigurationClass(string objectName, Type type) + { + _objectName = objectName; + _configurationClassType = type; + _resource = new ConfigurationClassAssemblyResource(type); + + } + + /// + /// Gets the type of the configuration class. + /// + /// The type of the configuration class. + public Type ConfigurationClassType + { + get + { + return _configurationClassType; + } + } + + /// + /// Gets the imported resources. + /// + /// The imported resources. + public IDictionary ImportedResources + { + get + { + return _importedResources; + } + } + + /// + /// Gets the methods. + /// + /// The methods. + public Collections.Generic.ISet Methods + { + get + { + return _methods; + } + } + + /// + /// Gets or sets the name of the object. + /// + /// The name of the object. + public string ObjectName + { + get + { + return _objectName; + } + set + { + _objectName = value; + } + } + + /// + /// Gets the resource. + /// + /// The resource. + public IResource Resource + { + get + { + return _resource; + } + } + + /// + /// Gets the SimpleName of the object. + /// + /// The simple name. + public string SimpleName + { + get { return ConfigurationClassType.Name; } + } + + /// + /// Adds the imported resource. + /// + /// The imported resource. + /// The reader class capable of interpreting the imported resource. + public void AddImportedResource(string importedResource, Type readerClass) + { + _importedResources.Add(importedResource, readerClass); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object other) + { + return this == other || (other is ConfigurationClass && ConfigurationClassType == ((ConfigurationClass)other).ConfigurationClassType); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return ConfigurationClassType.GetHashCode() * 14; + } + + /// + /// Validates the specified and reports all discovered violations to the provided problem reporter for appropriate action. + /// + /// The problem reporter. + public void Validate(IProblemReporter problemReporter) + { + // A [ObjectDef] method may only be overloaded through inheritance. No single + // [Configuration] class may declare two [ObjectDef] methods with the same name. + const char hashDelim = '#'; + Dictionary methodNameCounts = new Dictionary(); + foreach (ConfigurationClassMethod method in _methods) + { + String dClassName = method.MethodMetadata.DeclaringType.FullName; + String methodName = method.MethodMetadata.Name; + + string paramTypes = ParamTypesToString(method.MethodMetadata); + + String fqMethodName = dClassName + hashDelim + methodName + paramTypes; + if (!methodNameCounts.ContainsKey(fqMethodName)) + { + methodNameCounts.Add(fqMethodName, 1); + } + else + { + int currentCount = methodNameCounts[fqMethodName]; + methodNameCounts.Add(fqMethodName, currentCount++); + } + } + + foreach (String methodName in methodNameCounts.Keys) + { + int count = methodNameCounts[methodName]; + if (count > 1) + { + String shortMethodName = methodName.Substring(methodName.IndexOf(hashDelim) + 1); + problemReporter.Error(new ObjectMethodOverloadingProblem(shortMethodName, count, Resource, ConfigurationClassType)); + } + } + + if (Attribute.GetCustomAttribute(_configurationClassType, typeof(ConfigurationAttribute)) != null) + { + + if (ConfigurationClassType.IsSealed) + { + problemReporter.Error(new SealedConfigurationProblem(SimpleName, Resource, ConfigurationClassType)); + + } + + foreach (ConfigurationClassMethod method in _methods) + { + method.Validate(problemReporter); + } + } + } + + private string ParamTypesToString(MethodInfo methodMetadata) + { + var result = new StringBuilder(); + + foreach (var parameter in methodMetadata.GetParameters()) + { + result.Append(parameter.ParameterType.ToString()); + } + + return result.ToString(); + } + + private class SealedConfigurationProblem : Problem + { + public SealedConfigurationProblem(string name, IResource resource, Type configurationClassType) + : base(String.Format("[Configuration] class '{0}' may not be sealed. Remove the sealed modifier to continue.", name), new Location(resource, configurationClassType)) + { } + + } + + //This class is for future use when parameterized [ObjectDef] methods are supported in the future. + //Until then, the test for only permitting zero-param [ObjectDef] methods would fail first, previnting this error from ever being reported + private class ObjectMethodOverloadingProblem : Problem + { + public ObjectMethodOverloadingProblem(string methodName, int count, IResource resource, Type configurationClassType) + : base(String.Format("[Configuration] class '{0}' has {1} overloaded [Definiton] methods named '{2}'. " + + "Only one [ObjectDef] method of a given name is allowed within each [Configuration] class.", configurationClassType.Name, count, methodName), new Location(resource, configurationClassType)) + { } + + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassAssemblyResource.cs b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassAssemblyResource.cs new file mode 100644 index 00000000..5a93bf07 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassAssemblyResource.cs @@ -0,0 +1,203 @@ +#region License + +/* + * Copyright © 2010-2011 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.IO; +using System.Reflection; +using Spring.Core.IO; + +namespace Spring.Context.Attributes +{ + /// + /// Implementation of the IResource that represents an assembly containing one or more resources. + /// + public class ConfigurationClassAssemblyResource : IResource + { + private readonly string _containingAssemblyFileName; + private readonly Type _type; + + /// + /// Initializes a new instance of the class. + /// + /// The type. + public ConfigurationClassAssemblyResource(Type type) + { + _type = type; + _containingAssemblyFileName = Assembly.GetAssembly(_type.GetType()).Location; + } + + #region IResource Members + + /// + /// Creates a resource relative to this resource. + /// + /// The path (always resolved as relative to this resource). + /// The relative resource. + /// + /// If the relative resource could not be created from the supplied + /// path. + /// + /// + /// If the resource does not support the notion of a relative path. + /// + public IResource CreateRelative(string relativePath) + { + throw new InvalidOperationException(); + } + + /// + /// Does this resource represent a handle with an open stream? + /// + /// + /// if this resource represents a handle with an + /// open stream. + /// + /// + ///

+ /// If , the + /// cannot be read multiple times, and must be read and then closed to + /// avoid resource leaks. + ///

+ ///

+ /// Will be for all usual resource descriptors. + ///

+ ///
+ /// + public bool IsOpen + { + get { return false; } + } + + /// + /// Returns the handle for this resource. + /// + /// The handle for this resource. + /// + ///

+ /// For safety, always check the value of the + /// property prior to + /// accessing this property; resources that cannot be exposed as + /// a will typically return + /// from a call to the + /// property. + ///

+ ///
+ /// + /// If the resource is not available or cannot be exposed as a + /// . + /// + /// + /// + public Uri Uri + { + get { return new Uri(_containingAssemblyFileName); } + } + + /// + /// Returns a handle for this resource. + /// + /// + /// The handle for this resource. + /// + /// + ///

+ /// For safety, always check the value of the + /// property prior to + /// accessing this property; resources that cannot be exposed as + /// a will typically return + /// from a call to the + /// property. + ///

+ ///
+ /// + /// If the resource is not available on a filesystem, or cannot be + /// exposed as a handle. + /// + /// + /// + public FileInfo File + { + get { return new FileInfo(_containingAssemblyFileName); } + } + + /// + /// Returns a description for this resource. + /// + /// A description for this resource. + /// + ///

+ /// The description is typically used for diagnostics and other such + /// logging when working with the resource. + ///

+ ///

+ /// Implementations are also encouraged to return this value from their + /// method. + ///

+ ///
+ public string Description + { + get { return _type.FullName; } + } + + /// + /// Does this resource actually exist in physical form? + /// + /// + /// if this resource actually exists in physical + /// form (for example on a filesystem). + /// + /// + ///

+ /// An example of a resource that physically exists would be a + /// file on a local filesystem. An example of a resource that does not + /// physically exist would be an in-memory stream. + ///

+ ///
+ /// + /// + public bool Exists + { + get { return System.IO.File.Exists(_containingAssemblyFileName); } + } + + /// + /// Return an for this resource. + /// + /// An . + /// + /// + /// Clients of this interface must be aware that every access of this + /// property will create a fresh + /// ; + /// it is the responsibility of the calling code to close any such + /// . + /// + /// + /// + /// If the stream could not be opened. + /// + public Stream InputStream + { + get { throw new InvalidOperationException(); } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassEnhancer.cs b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassEnhancer.cs new file mode 100644 index 00000000..a33d612a --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassEnhancer.cs @@ -0,0 +1,284 @@ +#region License + +/* + * Copyright © 2010-2011 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.Reflection; +using System.Reflection.Emit; + +using Spring.Objects.Factory.Config; +using Spring.Util; +using Spring.Proxy; +using Common.Logging; + +namespace Spring.Context.Attributes +{ + /// + /// Enhances Configuration classes by generating a dynamic proxy capable of + /// interacting with the Spring container to respect object semantics. + /// + /// Chris Beams + /// Juergen Hoeller + /// Bruno Baia (.NET) + /// + public class ConfigurationClassEnhancer + { + private IConfigurationClassInterceptor interceptor; + + /// + /// Creates a new instance of the class. + /// + /// + /// The supplied ObjectFactory to check for the existence of object definitions. + /// + public ConfigurationClassEnhancer(IConfigurableListableObjectFactory objectFactory) + { + AssertUtils.ArgumentNotNull(objectFactory, "objectFactory"); + + this.interceptor = new ConfigurationClassInterceptor(objectFactory); + } + + /// + /// Generates a dynamic subclass of the specified Configuration class with a + /// container-aware interceptor capable of respecting scoping and other bean semantics. + /// + /// The Configuration class. + /// The enhanced subclass. + public Type Enhance(Type configClass) + { + ConfigurationClassProxyTypeBuilder proxyTypeBuilder = new ConfigurationClassProxyTypeBuilder(configClass, this.interceptor); + return proxyTypeBuilder.BuildProxyType(); + } + + /// + /// Intercepts the invocation of any -decorated methods in order + /// to ensure proper handling of object semantics such as scoping and AOP proxying. + /// + public interface IConfigurationClassInterceptor + { + /// + /// Process the -decorated method to check + /// for the existence of this object. + /// + /// The method providing the object definition. + /// When this method returns true, contains the object definition. + /// true if the object exists; otherwise, false. + bool ProcessDefinition(MethodInfo method, out object instance); + } + + private sealed class ConfigurationClassInterceptor : IConfigurationClassInterceptor + { + #region Logging + + private static readonly ILog Logger = LogManager.GetLogger(); + + #endregion + + private readonly IConfigurableListableObjectFactory _configurableListableObjectFactory; + + public ConfigurationClassInterceptor(IConfigurableListableObjectFactory configurableListableObjectFactory) + { + this._configurableListableObjectFactory = configurableListableObjectFactory; + } + + public bool ProcessDefinition(MethodInfo method, out object instance) + { + instance = null; + + string objectName = method.Name; + + if (objectName.StartsWith("set_") || objectName.StartsWith("get_")) + { + return false; + } + + object[] attribs = method.GetCustomAttributes(typeof(ObjectDefAttribute), true); + if (attribs.Length == 0) + { + return false; + } + + if (this._configurableListableObjectFactory.IsCurrentlyInCreation(objectName)) + { + Logger.Debug(m => m("Object '{0}' currently in creation, created one", objectName)); + + return false; + } + + Logger.Debug(m => m("Object '{0}' not in creation, asked the application context for one", objectName)); + + instance = this._configurableListableObjectFactory.GetObject(objectName); + return true; + } + } + + #region Proxy builder classes definition + + private sealed class ConfigurationClassProxyTypeBuilder : InheritanceProxyTypeBuilder + { + private FieldBuilder interceptorField; + private IConfigurationClassInterceptor interceptor; + + public ConfigurationClassProxyTypeBuilder(Type configurationClassType, IConfigurationClassInterceptor interceptor) + { + if (configurationClassType.IsSealed) + { + throw new ArgumentException(String.Format( + "[Configuration] classes '{0}' cannot be sealed [{0}].", configurationClassType.FullName)); + } + + this.Name = "ConfigurationClassProxy"; + this.DeclaredMembersOnly = false; + this.BaseType = configurationClassType; + this.TargetType = configurationClassType; + + this.interceptor = interceptor; + } + + public override Type BuildProxyType() + { + IDictionary targetMethods = new Hashtable(); + + TypeBuilder typeBuilder = CreateTypeBuilder(Name, BaseType); + + // apply custom attributes to the proxy type. + //ApplyTypeAttributes(typeBuilder, BaseType); + + // declare interceptor field + interceptorField = typeBuilder.DefineField("__Interceptor", typeof(IConfigurationClassInterceptor), + FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly); + + // create constructors + ImplementConstructors(typeBuilder); + + // proxy base virtual methods + InheritType(typeBuilder, + new ConfigurationClassProxyMethodBuilder(typeBuilder, this, false, targetMethods), + BaseType, this.DeclaredMembersOnly); + + Type proxyType = typeBuilder.CreateType(); + + // set target method references + foreach (DictionaryEntry entry in targetMethods) + { + FieldInfo targetMethodFieldInfo = proxyType.GetField((string)entry.Key, BindingFlags.NonPublic | BindingFlags.Static); + targetMethodFieldInfo.SetValue(proxyType, entry.Value); + } + + // set interceptor + FieldInfo interceptorFieldInfo = proxyType.GetField("__Interceptor", BindingFlags.NonPublic | BindingFlags.Static); + interceptorFieldInfo.SetValue(proxyType, this.interceptor); + + return proxyType; + } + + public void PushInterceptor(ILGenerator il) + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, interceptorField); + } + } + + private sealed class ConfigurationClassProxyMethodBuilder : AbstractProxyMethodBuilder + { + public static readonly MethodInfo ProcessDefinitionMethod = + typeof(IConfigurationClassInterceptor).GetMethod("ProcessDefinition", BindingFlags.Instance | BindingFlags.Public); + + private ConfigurationClassProxyTypeBuilder customProxyGenerator; + + private IDictionary targetMethods; + + public ConfigurationClassProxyMethodBuilder( + TypeBuilder typeBuilder, ConfigurationClassProxyTypeBuilder proxyGenerator, + bool explicitImplementation, IDictionary targetMethods) + : base(typeBuilder, proxyGenerator, explicitImplementation) + { + this.customProxyGenerator = proxyGenerator; + this.targetMethods = targetMethods; + } + + protected override void GenerateMethod( + ILGenerator il, MethodInfo method, MethodInfo interfaceMethod) + { + // Declare local variables + LocalBuilder interceptedReturnValue = il.DeclareLocal(typeof(Object)); +//#if DEBUG +// interceptedReturnValue.SetLocalSymInfo("interceptedReturnValue"); +//#endif + LocalBuilder returnValue = null; + if (method.ReturnType != typeof(void)) + { + returnValue = il.DeclareLocal(method.ReturnType); +//#if DEBUG +// returnValue.SetLocalSymInfo("returnValue"); +//#endif + } + + // Declare static field that will cache base method + string methodId = "_m" + Guid.NewGuid().ToString("N"); + targetMethods.Add(methodId, method); + FieldBuilder targetMethodCacheField = typeBuilder.DefineField(methodId, typeof(MethodInfo), + FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly); + + // Call IConfigurationClassInterceptor.TryGetObject method + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Stloc, interceptedReturnValue); + customProxyGenerator.PushInterceptor(il); + il.Emit(OpCodes.Ldsfld, targetMethodCacheField); + il.Emit(OpCodes.Ldloca_S, interceptedReturnValue); + il.EmitCall(OpCodes.Callvirt, ProcessDefinitionMethod, null); + Label jmpBaseCall = il.DefineLabel(); + Label jmpEndIf = il.DefineLabel(); + il.Emit(OpCodes.Brfalse_S, jmpBaseCall); + + // if true + if (returnValue != null) + { + il.Emit(OpCodes.Ldloc, interceptedReturnValue); + if (method.ReturnType.IsValueType || method.ReturnType.IsGenericParameter) + { + il.Emit(OpCodes.Unbox_Any, method.ReturnType); + } + il.Emit(OpCodes.Stloc, returnValue); + il.Emit(OpCodes.Br, jmpEndIf); + } + + // if false + il.MarkLabel(jmpBaseCall); + CallDirectBaseMethod(il, method); + if (returnValue != null) + { + il.Emit(OpCodes.Stloc, returnValue); + } + + // end if + il.MarkLabel(jmpEndIf); + + // return value + if (returnValue != null) + { + il.Emit(OpCodes.Ldloc, returnValue); + } + } + } + + #endregion + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassMethod.cs b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassMethod.cs new file mode 100644 index 00000000..305041e6 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassMethod.cs @@ -0,0 +1,145 @@ +#region License + +/* + * Copyright © 2010-2011 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.Reflection; +using Spring.Objects.Factory.Parsing; + +namespace Spring.Context.Attributes +{ + /// + /// Represents a class method marked with the . + /// + public class ConfigurationClassMethod + { + private readonly ConfigurationClass _configurationClass; + + private readonly MethodInfo _methodInfo; + + /// + /// Initializes a new instance of the ConfigurationClassMethod class. + /// + /// + /// + public ConfigurationClassMethod(MethodInfo methodInfo, ConfigurationClass configurationClass) + { + _methodInfo = methodInfo; + _configurationClass = configurationClass; + } + + /// + /// Gets the configuration class. + /// + /// The configuration class. + public ConfigurationClass ConfigurationClass + { + get { return _configurationClass; } + } + + /// + /// Gets the method metadata. + /// + /// The method metadata. + public MethodInfo MethodMetadata + { + get { return _methodInfo; } + } + + /// + /// Gets the resource location. + /// + /// The resource location. + public Location ResourceLocation + { + get { return new Location(_configurationClass.Resource, _methodInfo); } + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format("{0}:name={1},declaringClass={2}", GetType().Name, _methodInfo.Name, + _methodInfo.DeclaringType.FullName); + } + + /// + /// Validates the specified problem reporter. + /// + /// The problem reporter. + public void Validate(IProblemReporter problemReporter) + { + //TODO: investigate whether this should be "if method has ObjectDef attribute" instead of "if class has Configuration attribute" + if ( + Attribute.GetCustomAttribute(ConfigurationClass.ConfigurationClassType, typeof (ConfigurationAttribute)) != + null) + { + + if (MethodMetadata.IsStatic) + { + problemReporter.Error(new StaticMethodError(MethodMetadata.Name, ResourceLocation)); + } + + if (!MethodMetadata.IsVirtual) + { + problemReporter.Error(new NonVirtualMethodError(MethodMetadata.Name, ResourceLocation)); + } + + if (MethodMetadata.GetParameters().Length != 0) + { + problemReporter.Error(new MethodWithParametersError(MethodMetadata.Name, ResourceLocation)); + } + } + } + + private class MethodWithParametersError : Problem + { + public MethodWithParametersError(string methodName, Location location) + : base( + String.Format( + "Method '{0}' must not accept parameters; remove the method's parameters to continue.", + methodName), location) + { + } + } + + private class NonVirtualMethodError : Problem + { + public NonVirtualMethodError(string methodName, Location location) + : base(String.Format("Method '{0}' must be public virtual; change the method's modifiers to continue.", + methodName), location) + { + } + } + + private class StaticMethodError : Problem + { + public StaticMethodError(string methodName, Location location) + : base( + String.Format("Method '{0}' must not be static; remove the method's static modifier to continue.", + methodName), location) + { + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassObjectDefinitionReader.cs b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassObjectDefinitionReader.cs new file mode 100644 index 00000000..a2d0a9f4 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassObjectDefinitionReader.cs @@ -0,0 +1,308 @@ +#region License + +/* + * Copyright © 2010-2011 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 + +#region + +using System; +using System.Collections.Generic; +using System.Reflection; + +using Common.Logging; + +using Spring.Core.TypeResolution; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Parsing; +using Spring.Objects.Factory.Support; +using Spring.Stereotype; + +#endregion + +namespace Spring.Context.Attributes +{ + /// + /// Reads the class with the applied and converts it into an instance. + /// + public class ConfigurationClassObjectDefinitionReader + { + private static readonly ILog Logger = LogManager.GetLogger(); + + private IProblemReporter _problemReporter; + + private IObjectDefinitionRegistry _registry; + + /// + /// Initializes a new instance of the ConfigurationClassObjectDefinitionReader class. + /// + /// + /// + public ConfigurationClassObjectDefinitionReader(IObjectDefinitionRegistry registry, + IProblemReporter problemReporter) + { + _registry = registry; + _problemReporter = problemReporter; + } + + private static bool HasAttributeOnMethods(Type objectType, Type attributeType) + { + Collections.Generic.ISet methods = ConfigurationClassParser.GetAllMethodsWithCustomAttributeForClass(objectType, + attributeType); + foreach (MethodInfo method in methods) + { + if (Attribute.GetCustomAttribute(method, attributeType) != null) + { + return true; + } + } + return false; + } + + /// + /// Loads the object definitions. + /// + /// The configuration model. + public void LoadObjectDefinitions(Collections.Generic.ISet configurationModel) + { + foreach (ConfigurationClass configClass in configurationModel) + { + LoadObjectDefinitionsForConfigurationClass(configClass); + } + } + + private void LoadObjectDefinitionForConfigurationClassIfNecessary(ConfigurationClass configClass) + { + if (configClass.ObjectName != null) + { + // a Object definition already exists for this configuration class -> nothing to do + return; + } + + // no Object definition exists yet -> this must be an imported configuration class ([Import]). + GenericObjectDefinition configObjectDef = new GenericObjectDefinition(); + String className = configClass.ConfigurationClassType.Name; + configObjectDef.ObjectTypeName = className; + configObjectDef.ObjectType = configClass.ConfigurationClassType; + if (CheckConfigurationClassCandidate(configClass.ConfigurationClassType)) + { + String configObjectName = ObjectDefinitionReaderUtils.RegisterWithGeneratedName(configObjectDef, + _registry); + configClass.ObjectName = configObjectName; + Logger.Debug(m => m("Registered object definition for imported [Configuration] class {0}", + configObjectName)); + } + } + + /// + /// Checks the class to see if it is a candidate to be a source. + /// + /// The object definition. + /// + public static bool CheckConfigurationClassCandidate(IObjectDefinition objectDefinition) + { + Type objectType = null; + if (objectDefinition is AbstractObjectDefinition) + { + AbstractObjectDefinition definition = (AbstractObjectDefinition)objectDefinition; + if (definition.HasObjectType) + { + objectType = definition.ObjectType; + } + else + { + if (definition.ObjectTypeName != null && !definition.IsAbstract) + { + objectType = TypeResolutionUtils.ResolveType(definition.ObjectTypeName); + } + } + if (objectType != null) + { + if (Attribute.GetCustomAttribute(objectType, typeof(ConfigurationAttribute)) != null) + { + return true; + } + if (Attribute.GetCustomAttribute(objectType, typeof(ComponentAttribute)) != null || + HasAttributeOnMethods(objectType, typeof(ObjectDefAttribute))) + { + return true; + } + } + } + return false; + } + + private bool CheckConfigurationClassCandidate(Type type) + { + if (type != null) + { + return (Attribute.GetCustomAttribute(type, typeof(ConfigurationAttribute)) != null); + } + + return false; + } + + private void LoadObjectDefinitionsForConfigurationClass(ConfigurationClass configClass) + { + LoadObjectDefinitionForConfigurationClassIfNecessary(configClass); + + foreach (ConfigurationClassMethod method in configClass.Methods) + { + LoadObjectDefinitionsForModelMethod(method); + } + + LoadObjectDefinitionsFromImportedResources(configClass.ImportedResources); + } + + private void LoadObjectDefinitionsForModelMethod(ConfigurationClassMethod method) + { + ConfigurationClass configClass = method.ConfigurationClass; + MethodInfo metadata = method.MethodMetadata; + + RootObjectDefinition objDef = new ConfigurationClassObjectDefinition(); + + objDef.FactoryObjectName = configClass.ObjectName; + objDef.FactoryMethodName = metadata.Name; + objDef.AutowireMode = Objects.Factory.Config.AutoWiringMode.Constructor; + + // consider name and any aliases + //Dictionary ObjectAttributes = metadata.getAnnotationAttributes(Object.class.getName()); + object[] objectAttributes = metadata.GetCustomAttributes(typeof(ObjectDefAttribute), true); + List names = new List(); + foreach (object t in objectAttributes) + { + string[] namesAndAliases = ((ObjectDefAttribute)t).NamesToArray; + + if (namesAndAliases != null) + { + names.Add(metadata.Name); + } + else + { + namesAndAliases = new[] { metadata.Name }; + } + + names.AddRange(namesAndAliases); + } + + string objectName = (names.Count > 0 ? names[0] : method.MethodMetadata.Name); + for (int i = 1; i < names.Count; i++) + { + _registry.RegisterAlias(objectName, names[i]); + } + + // has this already been overridden (e.g. via XML)? + if (_registry.ContainsObjectDefinition(objectName)) + { + IObjectDefinition existingObjectDef = _registry.GetObjectDefinition(objectName); + // is the existing Object definition one that was created from a configuration class? + if (!(existingObjectDef is ConfigurationClassObjectDefinition)) + { + // no -> then it's an external override, probably XML + // overriding is legal, return immediately + Logger.Debug(m => m("Skipping loading Object definition for {0}: a definition for object " + + "'{1}' already exists. This is likely due to an override in XML.", method, + objectName)); + return; + } + } + + //TODO: container does not presently support the concept of Primary object defintion for type resolution + //if (Attribute.GetCustomAttribute(metadata, typeof(PrimaryAttribute)) != null) + //{ + // ObjectDef.isPrimary = true; + //} + + // is this Object to be instantiated lazily? + if (Attribute.GetCustomAttribute(metadata, typeof(LazyAttribute)) != null) + { + objDef.IsLazyInit = + (Attribute.GetCustomAttribute(metadata, typeof(LazyAttribute)) as LazyAttribute).LazyInitialize; + } + + if (Attribute.GetCustomAttribute(metadata, typeof(DependsOnAttribute)) != null) + { + objDef.DependsOn = + (Attribute.GetCustomAttribute(metadata, typeof(DependsOnAttribute)) as DependsOnAttribute).Name; + } + + //TODO: container does not presently support autowiring to the degree needed to support this feature as of yet + //Autowire autowire = (Autowire) ObjectAttributes.get("autowire"); + //if (autowire.isAutowire()) { + // ObjectDef.setAutowireMode(autowire.value()); + //} + + if (Attribute.GetCustomAttribute(metadata, typeof(ObjectDefAttribute)) != null) + { + objDef.InitMethodName = + (Attribute.GetCustomAttribute(metadata, typeof(ObjectDefAttribute)) as ObjectDefAttribute). + InitMethod; + objDef.DestroyMethodName = + (Attribute.GetCustomAttribute(metadata, typeof(ObjectDefAttribute)) as ObjectDefAttribute). + DestroyMethod; + } + + // consider scoping + if (Attribute.GetCustomAttribute(metadata, typeof(ScopeAttribute)) != null) + { + objDef.Scope = + (Attribute.GetCustomAttribute(metadata, typeof(ScopeAttribute)) as ScopeAttribute).ObjectScope.ToString(); + } + + Logger.Debug(m => m("Registering Object definition for [ObjectDef] method {0}.{1}()", + configClass.ConfigurationClassType.Name, objectName)); + + _registry.RegisterObjectDefinition(objectName, objDef); + } + + private void LoadObjectDefinitionsFromImportedResources(IEnumerable> importedResources) + { + IDictionary readerInstanceCache = + new Dictionary(); + foreach (KeyValuePair entry in importedResources) + { + String resource = entry.Key; + Type readerClass = entry.Value; + + if (!readerInstanceCache.ContainsKey(readerClass)) + { + try + { + IObjectDefinitionReader readerInstance = + (IObjectDefinitionReader)Activator.CreateInstance(readerClass, _registry); + + readerInstanceCache.Add(readerClass, readerInstance); + } + catch (Exception) + { + throw new InvalidOperationException( + String.Format("Could not instantiate IObjectDefinitionReader class {0}", + readerClass.FullName)); + } + } + + IObjectDefinitionReader reader = readerInstanceCache[readerClass]; + + reader.LoadObjectDefinitions(resource); + } + } + + private class ConfigurationClassObjectDefinition : RootObjectDefinition + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassParser.cs b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassParser.cs new file mode 100644 index 00000000..2385ec2d --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassParser.cs @@ -0,0 +1,194 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; +using Spring.Objects.Factory.Parsing; +using Spring.Collections.Generic; +using System.Reflection; + +namespace Spring.Context.Attributes +{ + + /// + /// Parses classes with the applied to them. + /// + public class ConfigurationClassParser + { + private Collections.Generic.ISet _configurationClasses = new HashedSet(); + + private Stack _importStack = new Stack(); + + private IProblemReporter _problemReporter; + + /// + /// Initializes a new instance of the ConfigurationClassParser class. + /// + /// + public ConfigurationClassParser(IProblemReporter problemReporter) + { + _problemReporter = problemReporter; + } + + /// + /// Gets the configuration classes. + /// + /// The configuration classes. + public Collections.Generic.ISet ConfigurationClasses + { + get { return _configurationClasses; } + } + + /// + /// Parses the specified type. + /// + /// The type. + /// Name of the object. + public void Parse(Type type, string objectName) + { + ProcessConfigurationClass(new ConfigurationClass(objectName, type)); + } + + /// + /// Validates this instance. + /// + public void Validate() + { + foreach (ConfigurationClass configClass in ConfigurationClasses) + { + configClass.Validate(_problemReporter); + } + } + + /// + /// Processes the configuration class. + /// + /// The configuration class. + protected void ProcessConfigurationClass(ConfigurationClass configurationClass) + { + DoProcessConfigurationClass(configurationClass); + + if (ConfigurationClasses.Contains(configurationClass) && configurationClass.ObjectName != null) + { + // Explicit object definition found, probably replacing an import. + // Let's remove the old one and go with the new one. + ConfigurationClasses.Remove(configurationClass); + } + ConfigurationClasses.Add(configurationClass); + } + + private void DoProcessConfigurationClass(ConfigurationClass configurationClass) + { + + Attribute[] importAttributes = Attribute.GetCustomAttributes(configurationClass.ConfigurationClassType, typeof(ImportAttribute)); + + if (importAttributes.Length > 0) + { + foreach (Attribute importAttribute in importAttributes) + { + ImportAttribute attrib = importAttribute as ImportAttribute; + + if (null != attrib) + { + ProcessImport(configurationClass, attrib.Types); + } + } + } + + Attribute[] importResourceAttributes = Attribute.GetCustomAttributes(configurationClass.ConfigurationClassType, typeof(ImportResourceAttribute)); + + if (importResourceAttributes.Length > 0) + { + foreach (Attribute importResourceAttribute in importResourceAttributes) + { + ImportResourceAttribute attrib = importResourceAttribute as ImportResourceAttribute; + + if (null != attrib) + { + foreach (string resource in attrib.Resources) + { + configurationClass.AddImportedResource(resource, attrib.DefinitionReader); + } + } + } + } + + Collections.Generic.ISet definitionMethods = GetAllMethodsWithCustomAttributeForClass(configurationClass.ConfigurationClassType, typeof(ObjectDefAttribute)); + foreach (MethodInfo definitionMethod in definitionMethods) + { + configurationClass.Methods.Add(new ConfigurationClassMethod(definitionMethod, configurationClass)); + + } + } + + /// + /// Gets all methods with custom attribute for class. + /// + /// The class. + /// The custom attribute. + /// + public static Collections.Generic.ISet GetAllMethodsWithCustomAttributeForClass(Type theClass, Type customAttribute) + { + Collections.Generic.ISet methods = new HashedSet(); + + foreach (MethodInfo method in theClass.GetMethods()) + { + if (Attribute.GetCustomAttribute(method, customAttribute) != null) + { + methods.Add(method); + } + } + + return methods; + } + + private void ProcessImport(ConfigurationClass configClass, IEnumerable classesToImport) + { + if (_importStack.Contains(configClass)) + { + _problemReporter.Error(new CircularImportProblem(configClass, _importStack, configClass.ConfigurationClassType)); + } + else + { + _importStack.Push(configClass); + foreach (Type classToImport in classesToImport) + { + ProcessConfigurationClass(new ConfigurationClass(null, classToImport)); + } + _importStack.Pop(); + } + } + + private class CircularImportProblem : Problem + { + public CircularImportProblem(ConfigurationClass configClass, Stack importStack, Type configurationClassType) + : base(String.Format("A circular [Import] has been detected: " + + "Illegal attempt by [Configuration] class '{0}' to import class '{1}' as '{2}' is " + + "already present in the current import stack [{3}]", + importStack.Peek().SimpleName, configClass.SimpleName, + configClass.SimpleName, importStack), + new Location(importStack.Peek().Resource, configurationClassType) + ) + { } + + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassPostProcessor.cs b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassPostProcessor.cs new file mode 100644 index 00000000..735891d2 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ConfigurationClassPostProcessor.cs @@ -0,0 +1,192 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; + +using Common.Logging; + +using Spring.Core; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Parsing; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Config; +using Spring.Collections.Generic; + +namespace Spring.Context.Attributes +{ + /// + /// Postprocesses the applied types registered with the . + /// + public class ConfigurationClassPostProcessor : IObjectDefinitionRegistryPostProcessor, IOrdered + { + #region Logging + + private static readonly ILog Logger = LogManager.GetLogger(); + + #endregion + + private bool _postProcessObjectDefinitionRegistryCalled; + + private bool _postProcessObjectFactoryCalled; + + private IProblemReporter _problemReporter = new FailFastProblemReporter(); + + /// + /// Return the order value of this object, where a higher value means greater in + /// terms of sorting. + /// + /// + /// + ///

+ /// Normally starting with 0 or 1, with indicating + /// greatest. Same order values will result in arbitrary positions for the affected + /// objects. + ///

+ ///

+ /// Higher value can be interpreted as lower priority, consequently the first object + /// has highest priority. + ///

+ ///
+ /// The order value. + public int Order + { + get { return int.MinValue; } + } + + /// + /// Sets the problem reporter. + /// + /// The problem reporter. + public IProblemReporter ProblemReporter + { + set { _problemReporter = (value ?? new FailFastProblemReporter()); } + } + + /// + /// Postsprocesses the object definition registry. + /// + /// The registry. + public void PostProcessObjectDefinitionRegistry(IObjectDefinitionRegistry registry) + { + if (_postProcessObjectDefinitionRegistryCalled) + { + throw new InvalidOperationException("PostProcessObjectDefinitionRegistry already called for this post-processor"); + } + if (_postProcessObjectFactoryCalled) + { + throw new InvalidOperationException("PostProcessObjectFactory already called for this post-processor"); + } + _postProcessObjectDefinitionRegistryCalled = true; + ProcessConfigObjectDefinitions(registry); + } + + /// + /// Postprocesses the object factory. + /// + /// The object factory. + public void PostProcessObjectFactory(IConfigurableListableObjectFactory objectFactory) + { + if (_postProcessObjectFactoryCalled) + { + throw new InvalidOperationException( + "PostProcessObjectFactory already called for this post-processor"); + } + _postProcessObjectFactoryCalled = true; + if (!_postProcessObjectDefinitionRegistryCalled) + { + // ObjectDefinitionRegistryPostProcessor hook apparently not supported... + // Simply call processConfigObjectDefinitions lazily at this point then. + ProcessConfigObjectDefinitions((IObjectDefinitionRegistry)objectFactory); + } + + EnhanceConfigurationClasses(objectFactory); + } + + private void EnhanceConfigurationClasses(IConfigurableListableObjectFactory objectFactory) + { + ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(objectFactory); + + IList objectNames = objectFactory.GetObjectDefinitionNames(); + + foreach (string name in objectNames) + { + IObjectDefinition objDef = objectFactory.GetObjectDefinition(name); + + if (((AbstractObjectDefinition)objDef).HasObjectType) + { + if (Attribute.GetCustomAttribute(objDef.ObjectType, typeof(ConfigurationAttribute)) != null) + { + //TODO check type of object isn't infrastructure type. + + Type configClass = objDef.ObjectType; + Type enhancedClass = enhancer.Enhance(configClass); + + Logger.Debug(m => m("Replacing object definition '{0}' existing class '{1}' with enhanced class", name, configClass.FullName)); + + ((IConfigurableObjectDefinition)objDef).ObjectType = enhancedClass; + } + } + } + } + + private void ProcessConfigObjectDefinitions(IObjectDefinitionRegistry registry) + { + Collections.Generic.ISet configCandidates = new HashedSet(); + foreach (string objectName in registry.GetObjectDefinitionNames()) + { + IObjectDefinition objectDef = registry.GetObjectDefinition(objectName); + if (ConfigurationClassObjectDefinitionReader.CheckConfigurationClassCandidate(objectDef)) + { + configCandidates.Add(new ObjectDefinitionHolder(objectDef, objectName)); + } + } + + //if nothing to process, bail out + if (configCandidates.Count == 0) { return; } + + ConfigurationClassParser parser = new ConfigurationClassParser(_problemReporter); + foreach (ObjectDefinitionHolder holder in configCandidates) + { + IObjectDefinition bd = holder.ObjectDefinition; + try + { + if (bd is AbstractObjectDefinition && ((AbstractObjectDefinition)bd).HasObjectType) + { + parser.Parse(((AbstractObjectDefinition)bd).ObjectType, holder.ObjectName); + } + else + { + //parser.Parse(bd.ObjectTypeName, holder.ObjectName); + } + } + catch (ObjectDefinitionParsingException ex) + { + throw new ObjectDefinitionStoreException("Failed to load object class: " + bd.ObjectTypeName, ex); + } + } + parser.Validate(); + + // Read the model and create Object definitions based on its content + ConfigurationClassObjectDefinitionReader reader = new ConfigurationClassObjectDefinitionReader(registry, _problemReporter); + reader.LoadObjectDefinitions(parser.ConfigurationClasses); + } + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/DependsOnAttribute.cs b/src/Spring/Spring.Core/Context/Attributes/DependsOnAttribute.cs new file mode 100644 index 00000000..9c4f5613 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/DependsOnAttribute.cs @@ -0,0 +1,67 @@ +#region License + +/* + * Copyright © 2010-2011 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; + +namespace Spring.Context.Attributes +{ + /// + /// objects on which the current object depends. Any objects specified are guaranteed to be + /// created by the container before this object. Used infrequently in cases where a object + /// does not explicitly depend on another through properties or constructor arguments, + /// but rather depends on the side effects of another object's initialization. + /// Note: This attribute will not be inherited by child object definitions, + /// hence it needs to be specified per concrete object definition. + /// + /// Using at the class level has no effect unless component-scanning + /// is being used. If a -attributed class is declared via XML, + /// attribute metadata is ignored, and + /// <object depends-on="..."/> is respected instead. + /// + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + public class DependsOnAttribute : Attribute + { + private string[] _name; + + /// + /// Initializes a new instance of the DependsOn class. + /// + /// + public DependsOnAttribute(params string[] name) + { + _name = name; + } + + /// + /// Gets or sets the name. + /// + /// The name. + public string[] Name + { + get { return _name; } + set + { + _name = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/IAssemblyTypeScanner.cs b/src/Spring/Spring.Core/Context/Attributes/IAssemblyTypeScanner.cs new file mode 100644 index 00000000..ff75cb14 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/IAssemblyTypeScanner.cs @@ -0,0 +1,89 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; +using System.Reflection; + +namespace Spring.Context.Attributes +{ + + /// + /// Scanner that can filter types from assemblies based on constraints. + /// + public interface IAssemblyTypeScanner + { + /// + /// Add the Assembly containing the specified . + /// + /// + /// + IAssemblyTypeScanner AssemblyHavingType(); + + /// + /// Adds the predicate to the assembly filter constraints. + /// + /// The assembly predicate. + /// + IAssemblyTypeScanner WithAssemblyFilter(Predicate assemblyPredicate); + + /// + /// Adds the predicte to the include filter for . + /// + /// The predicate. + /// + IAssemblyTypeScanner WithIncludeFilter(Predicate predicate); + + /// + /// Adds the predicte to the exclude filter for . + /// + /// The predicate. + /// + IAssemblyTypeScanner WithExcludeFilter(Predicate predicate); + + /// + /// Includes the specific types. + /// + /// The types. + /// + IAssemblyTypeScanner IncludeTypes(IEnumerable typeSource); + + + /// + /// Includes the type. + /// + /// The to include. + /// + IAssemblyTypeScanner IncludeType(); + + /// + /// Excludes the type. + /// + /// The to exclude. + /// + IAssemblyTypeScanner ExcludeType(); + + /// + /// Perform the Scan, applying all provided + /// + /// + IEnumerable Scan(); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/ImportAttribute.cs b/src/Spring/Spring.Core/Context/Attributes/ImportAttribute.cs new file mode 100644 index 00000000..791f735b --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ImportAttribute.cs @@ -0,0 +1,64 @@ +#region License + +/* + * Copyright © 2010-2011 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; + +namespace Spring.Context.Attributes +{ + /// + /// Indicates one or more classes to import. + /// + /// Provides functionality equivalent to the <import/> element in Spring XML. + /// Only supported for actual -attributed classes. + /// + /// + /// If XML or other non- object definition resources need to be + /// imported, use + /// + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class ImportAttribute : Attribute + { + private Type[] _types; + + /// + /// Initializes a new instance of the Import class. + /// + /// + public ImportAttribute(params Type[] types) + { + _types = types; + } + + /// + /// The class or classes to import. + /// + /// The type. + public Type[] Types + { + get { return _types; } + set + { + _types = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/ImportResourceAttribute.cs b/src/Spring/Spring.Core/Context/Attributes/ImportResourceAttribute.cs new file mode 100644 index 00000000..70497763 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ImportResourceAttribute.cs @@ -0,0 +1,99 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Core.IO; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +namespace Spring.Context.Attributes +{ + /// + /// Supports providing one or more implementations to import when creating s. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class ImportResourceAttribute : Attribute + { + private Type _objectDefinitionReader = typeof(XmlObjectDefinitionReader); + + private string[] _resources; + + /// + /// Initializes a new instance of the ImportResourceAttribute class. + /// + /// + public ImportResourceAttribute(string[] resources) + { + if (resources ==null || resources.Length ==0) + throw new ArgumentException("resources cannot be null or empty!"); + + _resources = resources; + } + + + /// + /// Initializes a new instance of the ImportResourceAttribute class. + /// + /// + public ImportResourceAttribute(string resource) + { + if (StringUtils.IsNullOrEmpty(resource)) + throw new ArgumentException("resource cannot be null or empty!"); + + _resources = new[] { resource }; + } + + /// + /// implementation to use when processing resources specified + /// by the attribute. + /// + /// The . + public Type DefinitionReader + { + get + { + return _objectDefinitionReader; + } + set + { + if (!((typeof(IObjectDefinitionReader).IsAssignableFrom(value)))) + throw new ArgumentException(string.Format("DefinitionReader must be of type IObjectDefinitionReader but was of type {0}", value.Name)); + + _objectDefinitionReader = value; + } + } + + /// + /// Resource paths to import. Resource-loading prefixes such as assembly:// and + /// file://, etc may be used. + /// + /// The resources. + public string[] Resources + { + get { return _resources; } + set + { + _resources = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/LazyAttribute.cs b/src/Spring/Spring.Core/Context/Attributes/LazyAttribute.cs new file mode 100644 index 00000000..a15b997a --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/LazyAttribute.cs @@ -0,0 +1,79 @@ +#region License + +/* + * Copyright © 2010-2011 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; + +namespace Spring.Context.Attributes +{ + /// + /// Indicates whether a object is to be lazily initialized. + /// + /// If this attribute is not present on a Component or object definition, eager + /// initialization will occur. If present and set to true, the + /// object/Component will not be initialized until referenced by another object or + /// explicitly retrieved from the enclosing . + /// If present and set to false, the object will be instantiated on startup by object factories + /// that perform eager initialization of singletons. + /// + /// + /// If Lazy is present on a class, this indicates that all + /// methods within that should be lazily + /// initialized. If Lazy is present and false on a object method within a + /// Lazy-annotated Configuration class, this indicates overriding the 'default + /// lazy' behavior and that the object should be eagerly initialized. + /// + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + public class LazyAttribute : Attribute + { + private bool _lazyInitialize = true; + + /// + /// Initializes a new instance of the LazyAttribute class. + /// + /// + public LazyAttribute(bool lazyInitialize) + { + _lazyInitialize = lazyInitialize; + } + + /// + /// Initializes a new instance of the LazyAttribute class. + /// + public LazyAttribute() + { + + } + + /// + /// Whether lazy initialization should occur. + /// + /// true if [lazy initialize]; otherwise, false. + public bool LazyInitialize + { + get { return _lazyInitialize; } + set + { + _lazyInitialize = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/LinqExtensionMethods.cs b/src/Spring/Spring.Core/Context/Attributes/LinqExtensionMethods.cs new file mode 100644 index 00000000..d30b0123 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/LinqExtensionMethods.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; + +namespace Spring.Context.Attributes +{ + /// + /// Limited extension methods reproducing the small subset of LINQ that is needed in the code; required b/c the project targets .NET 2.0 where LINQ is not available. + /// + internal static class LinqExtensionMethods + { + public static int Count(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException("source"); + + int counter = 0; + foreach (TSource obj in source) + { + counter++; + } + + return counter; + } + + internal static bool Contains(this IEnumerable source, TSource value) where TSource : class + { + if (source == null) throw new ArgumentNullException("source"); + + foreach (TSource obj in source) + { + if (obj == value) + { + return true; + } + } + + return false; + } + + internal static IEnumerable AsEnumerable(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException("source"); + + IList results = new List(); + + foreach (TSource obj in source) + { + results.Add(obj); + } + + return results; + } + + + internal static IEnumerable Where(this IEnumerable source, + Predicate predicate) + { + if (source == null) throw new ArgumentNullException("source"); + if (predicate == null) throw new ArgumentNullException("predicate"); + + IList matching = new List(); + + foreach (TSource obj in source) + { + if (predicate(obj)) + { + matching.Add(obj); + } + } + + return matching; + } + + + internal static bool Any(this IEnumerable source, Predicate predicate) + { + if (source == null) throw new ArgumentNullException("source"); + if (predicate == null) throw new ArgumentNullException("predicate"); + + foreach (TSource obj in source) + { + if (predicate(obj)) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/ObjectDefAttribute.cs b/src/Spring/Spring.Core/Context/Attributes/ObjectDefAttribute.cs new file mode 100644 index 00000000..44bbaf86 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ObjectDefAttribute.cs @@ -0,0 +1,122 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Util; + +namespace Spring.Context.Attributes +{ + /// + /// Identifies the Method as providing and Object Definition. + /// + [AttributeUsage(AttributeTargets.Method)] + public class ObjectDefAttribute : Attribute + { + //private AutoWiringMode _autoWire = AutoWiringMode.No; + + private string _destroyMethod; + + private string _initMethod; + + private string _names; + + //TODO: constructor injection via factory methods is not presently supported by the container + ///// + ///// Are dependencies to be injected via autowiring? + ///// + ///// The auto wire. + //public AutoWiringMode AutoWire + //{ + // get { return _autoWire; } + // set + // { + // _autoWire = value; + // } + //} + + /// + /// The optional name of a method to call on the Object instance upon closing the + /// application context, for example a Close() method on a DataSource. + /// The method must have no arguments but may throw any exception. + /// + /// Note: Only invoked on objects whose lifecycle is under the full control of the + /// factory, which is always the case for singletons but not guaranteed + /// for any other scope. + /// + /// + /// + /// The destroy method. + public string DestroyMethod + { + get { return _destroyMethod; } + set + { + _destroyMethod = value; + } + } + + /// + /// The optional name of a method to call on the object instance during initialization. + /// Not commonly used, given that the method may be called programmatically directly + /// within the body of a Object-annotated method. + /// + /// The init method. + public string InitMethod + { + get { return _initMethod; } + set + { + _initMethod = value; + } + } + + /// + /// The name of this object, or if multiple, aliases for this object. If left unspecified + /// the name of the object is the name of the attributed method. If specified, the method + /// name is ignored. + /// + /// The name. + public string Names + { + get + { + return _names; + } + set + { + _names = value; + } + } + + /// + /// Gets the comma-delimited list of names/aliases as an array. + /// + /// The array of names. + public string[] NamesToArray + { + get + { + return StringUtils.DelimitedListToStringArray(_names, ","); + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/ReflectionOnlyUtils.cs b/src/Spring/Spring.Core/Context/Attributes/ReflectionOnlyUtils.cs new file mode 100644 index 00000000..815bf61b --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ReflectionOnlyUtils.cs @@ -0,0 +1,107 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; +using System.Reflection; +using System.Security.Permissions; +using System.Security.Policy; + +namespace Spring.Context.Attributes +{ + /// + /// Utilities to provide support for manipulating ReflectionOnly types in the . + /// + public static class ReflectionOnlyUtils + { + /// + /// Load the into the ReflectionsOnly context based on its partial name. + /// + /// The partial name. + /// The matching + public static Assembly ReflectionOnlyLoadWithPartialName(string partialName) + { + return ReflectionOnlyLoadWithPartialName(partialName, null); + } + + private static Assembly ReflectionOnlyLoadWithPartialName(string partialName, Evidence securityEvidence) + { + if (securityEvidence != null) + new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand(); + + AssemblyName fileName = new AssemblyName(partialName); + + var assembly = nLoad(fileName, null, securityEvidence, null, null, false, true); + + if (assembly != null) + return assembly; + + var assemblyRef = EnumerateCache(fileName); + + if (assemblyRef != null) + return InternalLoad(assemblyRef, securityEvidence, null, true); + + return assembly; + } + + private static Assembly nLoad(params object[] args) + { + return (Assembly)typeof(Assembly) + .GetMethod("nLoad", BindingFlags.NonPublic | BindingFlags.Static) + .Invoke(null, args); + } + + private static AssemblyName EnumerateCache(params object[] args) + { + return (AssemblyName)typeof(Assembly) + .GetMethod("EnumerateCache", BindingFlags.NonPublic | BindingFlags.Static) + .Invoke(null, args); + } + + private static Assembly InternalLoad(params object[] args) + { + // Easiest to query because the StackCrawlMark type is internal + /* + * TODO: cannot do it this way under .NET 2.0 b/c .First(...) relies on LINQ which we don't have (yet) + * (plan to eventually uncomment this impl once we move to .NET 3.5 or greater) + * + return (Assembly) + typeof(Assembly).GetMethods(BindingFlags.NonPublic | BindingFlags.Static) + .First(m => m.Name == "InternalLoad" && + m.GetParameters()[0].ParameterType == typeof (AssemblyName)) + .Invoke(null, args); + */ + IEnumerable methods = + typeof(Assembly).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where( + delegate(MethodInfo m) + { + return m.Name == "InternalLoad" && + m.GetParameters()[0].ParameterType == typeof(AssemblyName); + }); + + foreach (MethodInfo methodInfo in methods) + { + return (Assembly)methodInfo.Invoke(null, args); + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/RequiredConstraintAssemblyTypeScanner.cs b/src/Spring/Spring.Core/Context/Attributes/RequiredConstraintAssemblyTypeScanner.cs new file mode 100644 index 00000000..bcf61b6a --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/RequiredConstraintAssemblyTypeScanner.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2010-2011 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; + +namespace Spring.Context.Attributes +{ + /// + /// AssemblyTypeScanner that provides for applying a final hard-coded Required Constraint to all types found in the the scanned assemblies + /// in addition to respecting the constraints passed to it during its configuration. + /// + [Serializable] + public abstract class RequiredConstraintAssemblyTypeScanner : AssemblyTypeScanner + { + + /// + /// Determines whether the compound predicate is satisfied by the specified type. + /// + /// The type. + /// + /// true if the compound predicate is satisfied by the specified type; otherwise, false. + /// + protected override bool IsCompoundPredicateSatisfiedBy(Type type) + { + return IsRequiredConstraintSatisfiedBy(type) && IsIncludedType(type) && !IsExcludedType(type); + } + + /// + /// Determines whether the required constraint is satisfied by the specified type. + /// + /// The type. + /// + /// true if the required constraint is satisfied by the specified type; otherwise, false. + /// + protected abstract bool IsRequiredConstraintSatisfiedBy(Type type); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Attributes/ScannedGenericObjectDefinition.cs b/src/Spring/Spring.Core/Context/Attributes/ScannedGenericObjectDefinition.cs new file mode 100644 index 00000000..cdd5c80e --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ScannedGenericObjectDefinition.cs @@ -0,0 +1,134 @@ +#region License + +/* + * Copyright 2002-2010 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 Spring.Objects; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Stereotype; +using Spring.Objects.Factory.Attributes; + +namespace Spring.Context.Attributes +{ + /// + /// A GenericObjectDefinition that provides attribute driven propulation + /// of properties like LazyInit, Scope or Qualifier + /// + public class ScannedGenericObjectDefinition : GenericObjectDefinition + { + /// + /// Name provided by the Component Attribute + /// + private string _componentName; + + /// + /// Creates a GenericObjectDefinition that applies the default values provided + /// in the XML Spring config document. Additionally parses the specific class + /// attributesthat allows the definition of LazyInit, Scope or Qualifier + /// + /// Type of scanned component + /// Defualts provided in Spring Config document + public ScannedGenericObjectDefinition(Type typeOfObject, DocumentDefaultsDefinition defaults) + { + ObjectType = typeOfObject; + + ParseName(); + ApplyDefaults(defaults); + ParseScopeAttribute(); + ParseLazyAttribute(); + ParseQualifierAttribute(); + } + + private void ParseName() + { + var attr = Attribute.GetCustomAttribute(ObjectType, typeof (ComponentAttribute), true) as ComponentAttribute; + if (attr != null && !string.IsNullOrEmpty(attr.Name)) + _componentName = attr.Name; + } + + private void ApplyDefaults(DocumentDefaultsDefinition defaults) + { + if (defaults == null) + return; + + bool lazyInit = false; + bool.TryParse(defaults.LazyInit, out lazyInit); + IsLazyInit = lazyInit; + } + + private void ParseScopeAttribute() + { + var attr = Attribute.GetCustomAttribute(ObjectType, typeof(ScopeAttribute), true) as ScopeAttribute; + if (attr != null) + Scope = attr.ObjectScope.ToString().ToLower(); + } + + private void ParseLazyAttribute() + { + var attr = Attribute.GetCustomAttribute(ObjectType, typeof(LazyAttribute), true) as LazyAttribute; + if (attr != null) + IsLazyInit = attr.LazyInitialize; + } + + private void ParseQualifierAttribute() + { + var attr = Attribute.GetCustomAttribute(ObjectType, typeof(QualifierAttribute), true) as QualifierAttribute; + if (attr != null) + { + var qualifier = new AutowireCandidateQualifier(attr.GetType()); + + if (!string.IsNullOrEmpty(attr.Value)) + qualifier.SetAttribute(AutowireCandidateQualifier.VALUE_KEY, attr.Value); + + ParseQualifierProperties(attr, qualifier); + + AddQualifier(qualifier); + } + } + + private void ParseQualifierProperties(QualifierAttribute attr, AutowireCandidateQualifier qualifier) + { + foreach (var property in attr.GetType().GetProperties()) + { + if (!property.Name.Equals("TypeId") && !property.Name.Equals("Value")) + { + object value = property.GetValue(attr, null); + if (value != null) + { + var attribute = new ObjectMetadataAttribute(property.Name, value); + qualifier.AddMetadataAttribute(attribute); + } + } + } + } + + /// + /// Provides the name of the object scanned + /// + /// return the provided attribute name of the full object type name + public string ComponentName + { + get + { + return _componentName; + } + } + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/ScopeAttribute.cs b/src/Spring/Spring.Core/Context/Attributes/ScopeAttribute.cs new file mode 100644 index 00000000..3cb343da --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/ScopeAttribute.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Objects.Factory.Support; + +namespace Spring.Context.Attributes +{ + /// + /// When used as a type-level attribute, indicates the name of a scope to use + /// for instances of the attributed type. + /// + /// When used as a method-level attribute in conjunction with the + /// attribute, indicates the name of a scope to use for + /// the instance returned from the method. + /// + /// In this context, scope means the lifecycle of an instance, such as + /// singleton, prototype, and so forth. + /// + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class ScopeAttribute : Attribute + { + private ObjectScope _scope = ObjectScope.Singleton; + + /// + /// Initializes a new instance of the Scope class. + /// + /// + public ScopeAttribute(ObjectScope scope) + { + _scope = scope; + } + + /// + /// Specifies the scope to use for the annotated object. + /// + /// The scope. + public ObjectScope ObjectScope + { + get { return _scope; } + set + { + _scope = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AbstractLoadTypeFilter.cs b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AbstractLoadTypeFilter.cs new file mode 100644 index 00000000..b0a17c59 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AbstractLoadTypeFilter.cs @@ -0,0 +1,66 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Core.TypeResolution; +using Common.Logging; + +namespace Spring.Context.Attributes.TypeFilters +{ + /// + /// Abstract Type Filter that provides methods to load a required type from assembly. + /// + public abstract class AbstractLoadTypeFilter : ITypeFilter + { + private static readonly ILog Logger = LogManager.GetLogger(); + + + /// + /// Required Type to compare against provided Type + /// + protected Type RequiredType; + + + /// + /// Determine a match based on the given type object. + /// + /// + /// true if there is a match; false is there is no match + public abstract bool Match(Type type); + + + /// + /// Is loading a Type from a string passed to method in the form [Type.FullName], [Assembly.Name] + /// + protected void GetRequiredType(string typeToLoad) + { + try + { + RequiredType = TypeResolutionUtils.ResolveType(typeToLoad); + } + catch (Exception) + { + RequiredType = null; + Logger.Error("Can't load type defined in exoression:" + typeToLoad); + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AssignableTypeFilter.cs b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AssignableTypeFilter.cs new file mode 100644 index 00000000..26919208 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AssignableTypeFilter.cs @@ -0,0 +1,55 @@ +#region License + +/* + * Copyright © 2010-2011 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; + +namespace Spring.Context.Attributes.TypeFilters +{ + + /// + /// A simple filter which matches classes that are assignable to a given type. + /// + public class AssignableTypeFilter : AbstractLoadTypeFilter + { + + /// + /// Create a Type Filter with required type + /// + /// type name including assembly name + public AssignableTypeFilter(string expression) + { + GetRequiredType(expression); + } + + /// + /// Determine a match based on the given type object. + /// + /// Type to compare against + /// true if there is a match; false is there is no match + public override bool Match(Type type) + { + if (RequiredType == null) + return false; + + return (type.GetInterfaces().Any(i => i.Equals(RequiredType)) || RequiredType.Equals(type.BaseType)); + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AttributeTypeFilter.cs b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AttributeTypeFilter.cs new file mode 100644 index 00000000..22ad4642 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/AttributeTypeFilter.cs @@ -0,0 +1,56 @@ +#region License + +/* + * Copyright © 2010-2011 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; + +namespace Spring.Context.Attributes.TypeFilters +{ + + /// + /// A simple filter which matches classes with a given attribute, + /// checking inherited annotations as well. + /// + public class AttributeTypeFilter : AbstractLoadTypeFilter + { + + /// + /// Creates a Type Filter with required type attribute + /// + /// + public AttributeTypeFilter(string expression) + { + GetRequiredType(expression); + } + + /// + /// Determine a match based on the given type object. + /// + /// Type to compare against + /// true if there is a match; false is there is no match + public override bool Match(Type type) + { + if (RequiredType == null) + return false; + + return (Attribute.GetCustomAttribute(type, RequiredType) != null); + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/TypeFilters/CustomTypeFactory.cs b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/CustomTypeFactory.cs new file mode 100644 index 00000000..45f7bc14 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/CustomTypeFactory.cs @@ -0,0 +1,91 @@ +#region License + +/* + * Copyright © 2010-2011 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 Common.Logging; +using Spring.Core.TypeResolution; +using Spring.Util; +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Attributes.TypeFilters +{ + /// + /// Creates a new instance of a givin type string + /// + public static class CustomTypeFactory + { + private static readonly ILog Logger = LogManager.GetLogger(typeof(CustomTypeFactory).FullName); + + /// + /// Creates a new instance of given type filter type string + /// + /// Custom type filter to create + /// An instance of ITypeFilter or NULL if no instance can be created + public static ITypeFilter GetTypeFilter(string expression) + { + return GetCustomType(expression) as ITypeFilter; + } + + + /// + /// Creates a new instance of given name generator type string + /// + /// Custom type name generator string to create + /// An instance of IObjectNameGenerator or NULL if no instance can be created + public static IObjectNameGenerator GetNameGenerator(string expression) + { + return GetCustomType(expression) as IObjectNameGenerator; + } + + private static object GetCustomType(string expression) + { + var customTypeFilterType = LoadType(expression); + if (customTypeFilterType == null) + return null; + + try + { + var instance = ObjectUtils.InstantiateType(customTypeFilterType); + return instance; + } + catch + { + Logger.Error(string.Format("Can't instatiate {0}. Type needs to have a non arg constructor.", expression)); + } + + return null; + } + + + private static Type LoadType(string typeToLoad) + { + try + { + return TypeResolutionUtils.ResolveType(typeToLoad); + } + catch (Exception) + { + Logger.Error("Can't load type defined in exoression:" + typeToLoad); + } + + return null; + } + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/TypeFilters/ITypeFilter.cs b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/ITypeFilter.cs new file mode 100644 index 00000000..58294a61 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/ITypeFilter.cs @@ -0,0 +1,37 @@ +#region License + +/* + * Copyright © 2010-2011 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; + +namespace Spring.Context.Attributes.TypeFilters +{ + /// + /// Represents the base interface for all component-scan type filters + /// + public interface ITypeFilter + { + /// + /// Determine a match based on the given type object. + /// + /// + /// true if there is a match; false is there is no match + bool Match(Type type); + } +} diff --git a/src/Spring/Spring.Core/Context/Attributes/TypeFilters/RegexPatternTypeFilter.cs b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/RegexPatternTypeFilter.cs new file mode 100644 index 00000000..f4e5468b --- /dev/null +++ b/src/Spring/Spring.Core/Context/Attributes/TypeFilters/RegexPatternTypeFilter.cs @@ -0,0 +1,53 @@ +#region License + +/* + * Copyright © 2010-2011 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.Text.RegularExpressions; + +namespace Spring.Context.Attributes.TypeFilters +{ + /// + /// A simple filter for matching a fully-qualified class name with a regex + /// + public class RegexPatternTypeFilter : ITypeFilter + { + private string _pattern; + + + /// + /// Creates a type filter with provided pattern + /// + /// Regex pattern + public RegexPatternTypeFilter(string pattern) + { + _pattern = pattern; + } + + /// + /// Determine a match based on the given type object. + /// + /// Type to compare against + /// true if there is a match; false is there is no match + public bool Match(Type type) + { + return Regex.IsMatch(type.FullName, _pattern); + } + } +} diff --git a/src/Spring/Spring.Core/Context/Config/AttributeConfigObjectDefinitionParser.cs b/src/Spring/Spring.Core/Context/Config/AttributeConfigObjectDefinitionParser.cs new file mode 100644 index 00000000..e73eae91 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Config/AttributeConfigObjectDefinitionParser.cs @@ -0,0 +1,61 @@ +#region License + +/* + * Copyright © 2010-2011 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.Xml; +using Spring.Context.Attributes; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +namespace Spring.Context.Config +{ + /// + /// Object Defintion Parser for interpreting classes when primary configuration is peformed via XML. + /// + public class AttributeConfigObjectDefinitionParser : IObjectDefinitionParser + { + /// + /// Parse the specified XmlElement and register the resulting + /// ObjectDefinitions with the IObjectDefinitionRegistry + /// embedded in the supplied + /// + /// The element to be parsed. + /// TThe object encapsulating the current state of the parsing process. + /// Provides access to a IObjectDefinitionRegistry + /// The primary object definition. + /// + ///

+ /// This method is never invoked if the parser is namespace aware + /// and was called to process the root node. + ///

+ ///
+ public IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + IObjectDefinitionRegistry registry = parserContext.ReaderContext.Registry; + AssertUtils.ArgumentNotNull(registry, "registry"); + + AttributeConfigUtils.RegisterAttributeConfigProcessors(registry); + + return null; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Config/ComponentScanObjectDefinitionParser.cs b/src/Spring/Spring.Core/Context/Config/ComponentScanObjectDefinitionParser.cs new file mode 100644 index 00000000..e63bae82 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Config/ComponentScanObjectDefinitionParser.cs @@ -0,0 +1,159 @@ +#region License + +/* + * Copyright © 2010-2011 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.ComponentModel; +using System.Xml; +using Common.Logging; +using Spring.Context.Attributes; +using Spring.Context.Attributes.TypeFilters; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Config +{ + /// + /// Parses ObjectDefinitions from classes identified by an . + /// + public class ComponentScanObjectDefinitionParser : IObjectDefinitionParser + { + private static readonly ILog Logger = LogManager.GetLogger(); + + private const string ATTRIBUTE_CONFIG_ATTRIBUTE = "attribute-config"; + + private const string NAME_GENERATOR_ATTRIBUTE = "name-generator"; + + private const string BASE_ASSEMBLIES_ATTRIBUTE = "base-assemblies"; + + private const string EXCLUDE_FILTER_ELEMENT = "exclude-filter"; + + private const string INCLUDE_FILTER_ELEMENT = "include-filter"; + + + /// + /// Parse the specified XmlElement and register the resulting + /// ObjectDefinitions with the IObjectDefinitionRegistry + /// embedded in the supplied + /// + /// The element to be parsed. + /// TThe object encapsulating the current state of the parsing process. + /// Provides access to a IObjectDefinitionRegistry + /// The primary object definition. + /// + ///

+ /// This method is never invoked if the parser is namespace aware + /// and was called to process the root node. + ///

+ ///
+ public IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + AssemblyObjectDefinitionScanner scanner = ConfigureScanner(parserContext, element); + IObjectDefinitionRegistry registry = parserContext.Registry; + + // Actually scan for objects definitions and register them. + scanner.ScanAndRegisterTypes(registry); + RegisterComponents(element, registry); + + return null; + } + + /// + /// Configures the scanner. + /// + /// The parser context. + /// The element. + /// + protected virtual AssemblyObjectDefinitionScanner ConfigureScanner(ParserContext parserContext, XmlElement element) + { + var scanner = new AssemblyObjectDefinitionScanner(); + + ParseBaseAssembliesAttribute(scanner, element); + ParseNameGeneratorAttribute(scanner, element); + ParseTypeFilters(scanner, element); + + scanner.Defaults = parserContext.ParserHelper.Defaults; + + return scanner; + } + + private void ParseBaseAssembliesAttribute(AssemblyObjectDefinitionScanner scanner, XmlElement element) + { + var baseAssemblies = element.GetAttribute(BASE_ASSEMBLIES_ATTRIBUTE); + + if (string.IsNullOrEmpty(baseAssemblies)) + return; + + foreach (var baseAssembly in baseAssemblies.Split(',')) + { + scanner.WithAssemblyFilter(assy => assy.FullName.StartsWith(baseAssembly)); + } + } + + private void ParseNameGeneratorAttribute(AssemblyObjectDefinitionScanner scanner, XmlElement element) + { + var nameGeneratorString = element.GetAttribute(NAME_GENERATOR_ATTRIBUTE); + var nameGenerator = CustomTypeFactory.GetNameGenerator(nameGeneratorString); + if (nameGenerator != null) + scanner.ObjectNameGenerator = nameGenerator; + } + + private void ParseTypeFilters(AssemblyObjectDefinitionScanner scanner, XmlElement element) + { + foreach (XmlNode node in element.ChildNodes) + { + if (node.Name.Contains(INCLUDE_FILTER_ELEMENT)) + scanner.WithIncludeFilter(CreateTypeFilter(node)); + else if (node.Name.Contains(EXCLUDE_FILTER_ELEMENT)) + scanner.WithExcludeFilter(CreateTypeFilter(node)); + } + } + + private void RegisterComponents(XmlElement element, IObjectDefinitionRegistry registry) + { + bool attributeConfig = true; + var attr = element.GetAttribute(ATTRIBUTE_CONFIG_ATTRIBUTE); + if (attr != null) + bool.TryParse(attr, out attributeConfig); + if (attributeConfig) + AttributeConfigUtils.RegisterAttributeConfigProcessors(registry); + } + + private ITypeFilter CreateTypeFilter(XmlNode node) + { + var type = node.Attributes["type"].Value; + var expression = node.Attributes["expression"].Value; + + switch (type) + { + case "regex": + return new RegexPatternTypeFilter(expression); + case "attribute": + return new AttributeTypeFilter(expression); + case "assignable": + return new AssignableTypeFilter(expression); + case "custom": + return CustomTypeFactory.GetTypeFilter(expression); + default: + throw new InvalidEnumArgumentException(string.Format("Filter type {0} is not defined", type)); + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Config/ContextNamespaceParser.cs b/src/Spring/Spring.Core/Context/Config/ContextNamespaceParser.cs new file mode 100644 index 00000000..37fd3592 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Config/ContextNamespaceParser.cs @@ -0,0 +1,55 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Objects.Factory.Xml; + +namespace Spring.Context.Config +{ + /// + /// NamespaceParser allowing for the configuration of + /// declarative transaction management using either XML or using attributes. + /// This namespace handler is the central piece of functionality in the + /// Spring transaction management facilities and offers two appraoches + /// to declaratively manage transactions. + /// One approach uses transaction semantics defined in XML using the + /// <tx:advice> elements, the other uses attributes + /// in combination with the <tx:annotation-driven> element. + /// Both approached are detailed in the Spring reference manual. + /// + [ + NamespaceParser( + Namespace = "http://www.springframework.net/context", + SchemaLocationAssemblyHint = typeof(ContextNamespaceParser), + SchemaLocation = "/Spring.Context.Config/spring-context-2.0.xsd" + ) + ] + public class ContextNamespaceParser : NamespaceParserSupport + { + /// + /// Register the for the 'advice' and + /// 'attribute-driven' tags. + /// + public override void Init() + { + RegisterObjectDefinitionParser("attribute-config", new AttributeConfigObjectDefinitionParser()); + RegisterObjectDefinitionParser("component-scan", new ComponentScanObjectDefinitionParser()); + } + } +} diff --git a/src/Spring/Spring.Core/Context/Config/spring-context-1.3.xsd b/src/Spring/Spring.Core/Context/Config/spring-context-1.3.xsd new file mode 100644 index 00000000..a74947a8 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Config/spring-context-1.3.xsd @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Config/spring-context-2.0.xsd b/src/Spring/Spring.Core/Context/Config/spring-context-2.0.xsd new file mode 100644 index 00000000..cd35a59a --- /dev/null +++ b/src/Spring/Spring.Core/Context/Config/spring-context-2.0.xsd @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + tag for that purpose. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Core/Context/Extension/GenericApplicationContextExtensions.cs b/src/Spring/Spring.Core/Context/Extension/GenericApplicationContextExtensions.cs new file mode 100644 index 00000000..99c5789e --- /dev/null +++ b/src/Spring/Spring.Core/Context/Extension/GenericApplicationContextExtensions.cs @@ -0,0 +1,110 @@ +#region License + +/* + * Copyright © 2010-2011 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.Reflection; +using Spring.Context.Attributes; +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Support +{ + /// + /// Extensions to enable scanning on any AbstractApplicationContext-derived type. + /// + public static class GenericApplicationContextExtensions + { + /// + /// Scans for types using the provided scanner. + /// + /// The context. + /// The scanner. + public static void Scan(this GenericApplicationContext context, AssemblyObjectDefinitionScanner scanner) + { + var registry = context.ObjectFactory as IObjectDefinitionRegistry; + scanner.ScanAndRegisterTypes(registry); + + AttributeConfigUtils.RegisterAttributeConfigProcessors(registry); + } + + /// + /// Scans for types that satisfy specified predicates located in the specified scan path. + /// + /// The context. + /// The assembly scan path. + /// The assembly predicate. + /// The type predicate. + public static void Scan(this GenericApplicationContext context, string assemblyScanPath, Predicate assemblyPredicate, + Predicate typePredicate) + { + //create a scanner instance using the scan path + var scanner = new AssemblyObjectDefinitionScanner(); + + //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(context, scanner); + } + + /// + /// Scans for types that satisfy specified predicates. + /// + /// The context. + /// The assembly predicate. + /// The type predicate. + public static void Scan(this GenericApplicationContext context, Predicate assemblyPredicate, Predicate typePredicate) + { + Scan(context, null, assemblyPredicate, typePredicate); + } + + /// + /// Scans for types using the default scanner. + /// + /// The context. + public static void ScanAllAssemblies(this GenericApplicationContext context) + { + Scan(context, new AssemblyObjectDefinitionScanner()); + } + + + /// + /// Scans the with assembly filter. + /// + /// The context. + /// The assembly predicate. + public static void ScanWithAssemblyFilter(this GenericApplicationContext context, Predicate assemblyPredicate) + { + Scan(context, null, assemblyPredicate, delegate { return true; }); + } + + /// + /// Scans the with type filter. + /// + /// The context. + /// The type predicate. + public static void ScanWithTypeFilter(this GenericApplicationContext context, Predicate typePredicate) + { + Scan(context, null, delegate { return true; }, typePredicate); + } + + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/CodeConfigApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/CodeConfigApplicationContext.cs new file mode 100644 index 00000000..633909bf --- /dev/null +++ b/src/Spring/Spring.Core/Context/Support/CodeConfigApplicationContext.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Objects.Factory.Support; + +namespace Spring.Context.Support +{ + /// + /// ApplicationContext that can scan to identify object definitions + /// + public class CodeConfigApplicationContext : GenericApplicationContext + { + /// + /// Initializes a new instance of the class. + /// + public CodeConfigApplicationContext() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// if set to true names in the context are case sensitive. + public CodeConfigApplicationContext(bool caseSensitive) + : base(caseSensitive) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The object factory instance to use for this context. + public CodeConfigApplicationContext(DefaultListableObjectFactory objectFactory) + : base(objectFactory) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The parent application context. + public CodeConfigApplicationContext(IApplicationContext parent) + : base(parent) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the application context.if set to true names in the context are case sensitive.The parent application context. + public CodeConfigApplicationContext(string name, bool caseSensitive, IApplicationContext parent) + : base(name, caseSensitive, parent) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The object factory to use for this contextThe parent applicaiton context. + public CodeConfigApplicationContext(DefaultListableObjectFactory objectFactory, IApplicationContext parent) + : base(objectFactory, parent) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the application context.if set to true names in the context are case sensitive.The parent application context.The object factory to use for this context + public CodeConfigApplicationContext(string name, bool caseSensitive, IApplicationContext parent, + DefaultListableObjectFactory objectFactory) + : base(name, caseSensitive, parent, objectFactory) + { + } + } +} \ 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 ac306901..2e67ede7 100644 --- a/src/Spring/Spring.Core/Spring.Core.2010.csproj +++ b/src/Spring/Spring.Core/Spring.Core.2010.csproj @@ -177,6 +177,39 @@ Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code @@ -186,6 +219,7 @@ Code + Code @@ -234,6 +268,7 @@ Code + Code @@ -1239,6 +1274,12 @@ Code + + Designer + + + Designer + diff --git a/src/Spring/Spring.Web.Conversation.NHibernate32/Spring.Web.Conversation.NHibernate32.xml b/src/Spring/Spring.Web.Conversation.NHibernate32/Spring.Web.Conversation.NHibernate32.xml index 953538d6..6666fa98 100644 --- a/src/Spring/Spring.Web.Conversation.NHibernate32/Spring.Web.Conversation.NHibernate32.xml +++ b/src/Spring/Spring.Web.Conversation.NHibernate32/Spring.Web.Conversation.NHibernate32.xml @@ -128,62 +128,6 @@ Create a new session on demand - - - Setting for - - Hailton de Castro - - - - Default value for property. - - - - - Initialize a new instance of with default values. - - - Calling this constructor from your derived class leaves - uninitialized. See for more. - - - - - Initialize a new instance of with the given values and references. - - - Specify the to be set on each session provided by the instance. - - - Specify the flushmode to be applied on each session provided by the instance. - - - Calling this constructor marks all properties initialized. - - - - - Override this method to resolve an instance according to your chosen strategy. - - - - - Gets the configured instance to be used. - - - - - - - Gets or Sets the flushmode to be applied on each newly created session. - - - This property defaults to to ensure that modifying objects outside the boundaries - of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation - within a transaction. - - Port to conversation. If the object is not found in the current @@ -867,6 +811,62 @@ Returns the current context. Supports serialization and deserialization. + + + Setting for + + Hailton de Castro + + + + Default value for property. + + + + + Initialize a new instance of with default values. + + + Calling this constructor from your derived class leaves + uninitialized. See for more. + + + + + Initialize a new instance of with the given values and references. + + + Specify the to be set on each session provided by the instance. + + + Specify the flushmode to be applied on each session provided by the instance. + + + Calling this constructor marks all properties initialized. + + + + + Override this method to resolve an instance according to your chosen strategy. + + + + + Gets the configured instance to be used. + + + + + + + Gets or Sets the flushmode to be applied on each newly created session. + + + This property defaults to to ensure that modifying objects outside the boundaries + of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation + within a transaction. + + This was made to stay under session scope. diff --git a/src/Spring/Spring.Web.Conversation.NHibernate33/Spring.Web.Conversation.NHibernate33.XML b/src/Spring/Spring.Web.Conversation.NHibernate33/Spring.Web.Conversation.NHibernate33.XML index 3e824e7c..39b80a8c 100644 --- a/src/Spring/Spring.Web.Conversation.NHibernate33/Spring.Web.Conversation.NHibernate33.XML +++ b/src/Spring/Spring.Web.Conversation.NHibernate33/Spring.Web.Conversation.NHibernate33.XML @@ -128,146 +128,6 @@ Create a new session on demand - - - Setting for - - Hailton de Castro - - - - Default value for property. - - - - - Initialize a new instance of with default values. - - - Calling this constructor from your derived class leaves - uninitialized. See for more. - - - - - Initialize a new instance of with the given values and references. - - - Specify the to be set on each session provided by the instance. - - - Specify the flushmode to be applied on each session provided by the instance. - - - Calling this constructor marks all properties initialized. - - - - - Override this method to resolve an instance according to your chosen strategy. - - - - - Gets the configured instance to be used. - - - - - - - Gets or Sets the flushmode to be applied on each newly created session. - - - This property defaults to to ensure that modifying objects outside the boundaries - of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation - within a transaction. - - - - - manager for Conversations. - - Hailton de Castro - - - - Returns the conversation if it is still alive, otherwise it returns null. - - - - - - - Ends all conversations with the timeout exceeded. - - - - - Close IDbConnections for that - use 'session-per-conversation'. It calls - in all conversations. - - - - - Release the ended conversations And removes them. - If the conversation supports 'session-per-conversation', also close the session. - - - - - Add conversation. If is null - it resolves to 'this'. - - - - If already has another manager. - - - - - Makes the 'root conversation' of - the current active conversation and open/reopen the - if - the conversation supports 'session-per-conversation'. Close all - the connection for all session before. - If is true will end all - paused conversations. - - - - - Returns the active conversation if exists, otherwise returns null. - It depends on - - - - - - If this is non-null run pattern 'session-per-conversation'. - Must be the same SessionFactory of the managed conversations. - - - - - - Ends the "paused conversations" in call to . - Important: Unexpected behavior may occur if there are nested conversations, - as in only the current conversation and its parents - are started, the 'conversations children' remain paused, so these will be ended. - Defaul value: false. - - - When it is true, "start/resume a conversation" will cause the other to be - ended and cleaned up. - - This is useful to avoid memory leak where there are many conversations. - This leak can be very considerable, as the conversation may keep a "NHibernate session" - that can contain many objects in its cache from the database queries. - - - Port to conversation. If the object is not found in the current @@ -423,6 +283,151 @@ Indicates that the conversation is paused. + + + manager for Conversations. + + Hailton de Castro + + + + Returns the conversation if it is still alive, otherwise it returns null. + + + + + + + Ends all conversations with the timeout exceeded. + + + + + Close IDbConnections for that + use 'session-per-conversation'. It calls + in all conversations. + + + + + Release the ended conversations And removes them. + If the conversation supports 'session-per-conversation', also close the session. + + + + + Add conversation. If is null + it resolves to 'this'. + + + + If already has another manager. + + + + + Makes the 'root conversation' of + the current active conversation and open/reopen the + if + the conversation supports 'session-per-conversation'. Close all + the connection for all session before. + If is true will end all + paused conversations. + + + + + Returns the active conversation if exists, otherwise returns null. + It depends on + + + + + + If this is non-null run pattern 'session-per-conversation'. + Must be the same SessionFactory of the managed conversations. + + + + + + Ends the "paused conversations" in call to . + Important: Unexpected behavior may occur if there are nested conversations, + as in only the current conversation and its parents + are started, the 'conversations children' remain paused, so these will be ended. + Defaul value: false. + + + When it is true, "start/resume a conversation" will cause the other to be + ended and cleaned up. + + This is useful to avoid memory leak where there are many conversations. + This leak can be very considerable, as the conversation may keep a "NHibernate session" + that can contain many objects in its cache from the database queries. + + + + + + HttpModule for ending Conversations with Timeout exceeded. + + Hailton de Castro + + + + Add PostRequestHandlerExecute event to clear conversations with timeout exceeded. + + + + + + Disposes of the resources (other than memory) used by the module that implements . + + + + + Handles the Unload event of the page control. + + The source of the event. + The instance containing the event data. + + Necessary for Redirect or Abort for any reason. + + + + + The Names of the s in the + + + + + Sets the that this + object runs in. + + + +

+ Used to obtain the instances of +

+

+ Invoked after population of normal object properties but before an + init callback such as + 's + + or a custom init-method. Invoked after the setting of any + 's + + property. +

+
+ + In the case of application context initialization errors. + + + If thrown by any application context methods. + + +
List that make validation for Circular Dependency for @@ -590,93 +595,6 @@ - - - This was made to stay under session scope. - - Hailton de Castro - - - - Semaphore to synchronize writes to the dictionary. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Ends all conversations and Closes all their Session. - - - - - Remove conversation. - - - - - - - - - - - "SessionFactory" name in the current context. - This approach is required to support serialization. - - - - - - - - - - - - - - - Returns the current context. Supports serialization and deserialization. - - Implementation of conversation in the infrastructure of Spring. @@ -893,66 +811,148 @@ Returns the current context. Supports serialization and deserialization. - + - HttpModule for ending Conversations with Timeout exceeded. + Setting for Hailton de Castro - + - Add PostRequestHandlerExecute event to clear conversations with timeout exceeded. - - - - - - Disposes of the resources (other than memory) used by the module that implements . + Default value for property. - + - Handles the Unload event of the page control. + Initialize a new instance of with default values. - The source of the event. - The instance containing the event data. - Necessary for Redirect or Abort for any reason. + Calling this constructor from your derived class leaves + uninitialized. See for more. - + - The Names of the s in the + Initialize a new instance of with the given values and references. + + + Specify the to be set on each session provided by the instance. + + + Specify the flushmode to be applied on each session provided by the instance. + + + Calling this constructor marks all properties initialized. + + + + + Override this method to resolve an instance according to your chosen strategy. - + - Sets the that this - object runs in. + Gets the configured instance to be used. - -

- Used to obtain the instances of -

-

- Invoked after population of normal object properties but before an - init callback such as - 's - - or a custom init-method. Invoked after the setting of any - 's - - property. -

- - In the case of application context initialization errors. - - - If thrown by any application context methods. - - +
+ + + Gets or Sets the flushmode to be applied on each newly created session. + + + This property defaults to to ensure that modifying objects outside the boundaries + of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation + within a transaction. + + + + + This was made to stay under session scope. + + Hailton de Castro + + + + Semaphore to synchronize writes to the dictionary. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ends all conversations and Closes all their Session. + + + + + Remove conversation. + + + + + + + + + + + "SessionFactory" name in the current context. + This approach is required to support serialization. + + + + + + + + + + + + + + + Returns the current context. Supports serialization and deserialization. + diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/AbstractConfigurationClassPostProcessorTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/AbstractConfigurationClassPostProcessorTests.cs new file mode 100644 index 00000000..240d731b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/AbstractConfigurationClassPostProcessorTests.cs @@ -0,0 +1,354 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; +using Spring.Context.Support; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Attributes +{ + + public abstract class AbstractConfigurationClassPostProcessorTests + { + protected AbstractApplicationContext _ctx; + + [SetUp] + public void _SetUp() + { + SingletonParent.InstanceCount = 0; + SingletonChild.InstanceCount = 0; + PrototypeParent.InstanceCount = 0; + PrototypeChild.InstanceCount = 0; + CreateApplicationContext(); + } + + + protected abstract void CreateApplicationContext(); + + + [Test] + public void Can_Assign_Init_And_Destroy_Methods() + { + IObjectDefinition def = _ctx.GetObjectDefinition(typeof(ObjectWithInitAndDestroyMethods).Name); + + Assert.That(def, Is.Not.Null); + Assert.That(def.InitMethodName, Is.EqualTo("CallToInit")); + Assert.That(def.DestroyMethodName, Is.EqualTo("CallToDestroy")); + } + + [Test] + public void Can_Import_Configurations_From_Additional_Classes() + { + Assert.That(_ctx.GetObject(typeof(AnImportedType).Name), Is.Not.Null); + } + + [Test] + public void Can_Respect_Assigned_Aliases() + { + var firstObject = _ctx["TheFirstAlias"]; + var secondObject = _ctx["TheSecondAlias"]; + Assert.That(firstObject, Is.InstanceOf()); + Assert.That(secondObject, Is.InstanceOf()); + } + + [Test] + public void Can_Respect_Assigned_Name() + { + var result = _ctx["TheName"]; + Assert.That(result, Is.InstanceOf()); + } + + [Test] + public void Can_Respect_Default_Singleton_Scope() + { + var firstObject = (SingletonChild)_ctx[typeof(SingletonChild).Name]; + var secondObject = (SingletonChild)_ctx[typeof(SingletonChild).Name]; + + Assert.That(SingletonChild.InstanceCount, Is.EqualTo(1)); + Assert.That(firstObject, Is.SameAs(secondObject)); + } + + [Test] + public void Can_Respect_Default_Singleton_Scope_With_Explicit_Prototype_Dependency() + { + var firstObject = (SingletonParent)_ctx[typeof(SingletonParent).Name]; + var secondObject = (SingletonParent)_ctx[typeof(SingletonParent).Name]; + + Assert.That(SingletonParent.InstanceCount, Is.EqualTo(1)); + //Assert.That(PrototypeChild.InstanceCount, Is.EqualTo(2)); // Requires scoped proxies + Assert.That(firstObject, Is.SameAs(secondObject)); + } + + [Test] + public void Can_Respect_Explicit_Prototype_Scope() + { + var firstObject = (PrototypeChild)_ctx[typeof(PrototypeChild).Name]; + var secondObject = (PrototypeChild)_ctx[typeof(PrototypeChild).Name]; + + Assert.That(PrototypeChild.InstanceCount, Is.EqualTo(3)); // One instance used by SingletonParent + Assert.That(firstObject, Is.Not.SameAs(secondObject)); + } + + [Test] + public void Can_Respect_Explicit_Prototype_Scope_With_Default_Singleton_Dependency() + { + var firstObject = (PrototypeParent)_ctx[typeof(PrototypeParent).Name]; + var secondObject = (PrototypeParent)_ctx[typeof(PrototypeParent).Name]; + + Assert.That(PrototypeParent.InstanceCount, Is.EqualTo(2)); + Assert.That(SingletonChild.InstanceCount, Is.EqualTo(1)); + Assert.That(firstObject, Is.Not.SameAs(secondObject)); + } + + [Test] + public void Can_Respect_Lazy_Attribute() + { + Assert.That(_ctx.GetObjectDefinition(typeof(ImplicitLazyInitObject).Name).IsLazyInit, Is.True); + Assert.That(_ctx.GetObjectDefinition(typeof(ExplicitLazyInitObject).Name).IsLazyInit, Is.True); + Assert.That(_ctx.GetObjectDefinition(typeof(ExplicitNonLazyInitObject).Name).IsLazyInit, Is.False); + } + + [Test] + public void Can_Retreive_Actual_Objects_From_Context() + { + Assert.That(_ctx[typeof(SingletonParent).Name], Is.TypeOf()); + Assert.That(_ctx[typeof(PrototypeChild).Name], Is.TypeOf()); + } + + [Test] + public void Can_Satisfy_Dependencies_Of_Objects() + { + Assert.That(((SingletonParent)_ctx[typeof(SingletonParent).Name]).Child, Is.Not.Null); + } + + + [Test] + public void Can_Respect_Imported_Resources() + { + Assert.That(_ctx["xmlRegisteredObject"], Is.Not.Null); + } + } + + public class ObjectWithInitAndDestroyMethods + { + public void CallToDestroy() { } + public void CallToInit() { } + } + + + + + [Configuration] + [ImportResource("assembly://Spring.Core.Tests/Spring.Context.Attributes/ObjectDefinitions.xml", DefinitionReader = typeof(XmlObjectDefinitionReader))] + [ImportResource("assembly://Spring.Core.Tests/Spring.Context.Attributes/ObjectDefinitionsTwo.xml")] + public class TheImportedConfigurationClass + { + [ObjectDef] + public virtual AnImportedType AnImportedType() + { + return new AnImportedType(); + } + } + + [Configuration] + [Import(typeof(TheImportedConfigurationClass))] + public class TheConfigurationClass + { + [ObjectDef(Names = "TheName")] + public virtual SingleNamedObject NamedObject() + { + return new SingleNamedObject(); + } + + [ObjectDef(DestroyMethod = "CallToDestroy", InitMethod = "CallToInit")] + public virtual ObjectWithInitAndDestroyMethods ObjectWithInitAndDestroyMethods() + { + return new ObjectWithInitAndDestroyMethods(); + } + + [ObjectDef(Names = "TheFirstAlias,TheSecondAlias")] + public virtual ObjectWithAnAlias ObjectWithAnAlias() + { + return new ObjectWithAnAlias(); + } + + [ObjectDef] + [Scope(ObjectScope.Prototype)] + public virtual PrototypeParent PrototypeParent() + { + return new PrototypeParent(SingletonChild()); + } + + [ObjectDef] + [Scope(ObjectScope.Prototype)] + public virtual PrototypeChild PrototypeChild() + { + return new PrototypeChild(); + } + + [ObjectDef] + public virtual SingletonParent SingletonParent() + { + return new SingletonParent(PrototypeChild()); + } + + [ObjectDef] + public virtual SingletonChild SingletonChild() + { + return new SingletonChild(); + } + + [ObjectDef] + [Lazy] + public virtual ImplicitLazyInitObject ImplicitLazyInitObject() + { + return new ImplicitLazyInitObject(); + } + + [ObjectDef] + [Lazy(true)] + public virtual ExplicitLazyInitObject ExplicitLazyInitObject() + { + return new ExplicitLazyInitObject(); + } + + [ObjectDef] + [Lazy(false)] + public virtual ExplicitNonLazyInitObject ExplicitNonLazyInitObject() + { + return new ExplicitNonLazyInitObject(); + } + + } + + + [Configuration] + public class DerivedConfiguration : BaseConfigurationClass + { + [ObjectDef] + public virtual TestObject DerivedDefinition() + { + return new TestObject(BaseDefinition()); + } + } + + public class BaseConfigurationClass + { + [ObjectDef] + public virtual string BaseDefinition() + { + return Guid.NewGuid().ToString(); + } + } + + public class TypeRegisteredInXml { } + + public class TypeRegisteredInXmlTwo { } + + public class AnImportedType { } + + public class ImplicitLazyInitObject { } + + public class ExplicitLazyInitObject { } + + public class ExplicitNonLazyInitObject { } + + public class ObjectWithAnAlias { } + + public class SingleNamedObject { } + + public class SingletonParent + { + public static int InstanceCount = 0; + private PrototypeChild _child; + + public SingletonParent(PrototypeChild child) + { + InstanceCount++; + _child = child; + } + + public PrototypeChild Child + { + get + { + return _child; + } + } + } + + public class SingletonChild + { + public static int InstanceCount = 0; + + public SingletonChild() + { + InstanceCount++; + } + } + + public class PrototypeParent + { + public static int InstanceCount = 0; + private SingletonChild _child; + + public PrototypeParent(SingletonChild child) + { + InstanceCount++; + _child = child; + } + + public SingletonChild Child + { + get + { + return _child; + } + } + } + + public class PrototypeChild + { + public static int InstanceCount = 0; + + public PrototypeChild() + { + InstanceCount++; + } + } + + public class TestObject + { + private readonly string _value; + + public TestObject(string value) + { + _value = value; + } + + public string Value + { + get { return _value; } + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyObjectDefinitionScannerTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyObjectDefinitionScannerTests.cs new file mode 100644 index 00000000..06fc43ce --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyObjectDefinitionScannerTests.cs @@ -0,0 +1,30 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class AssemblyObjectDefinitionScannerTests + { + + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyTypeScannerTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyTypeScannerTests.cs new file mode 100644 index 00000000..df0396c0 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/AssemblyTypeScannerTests.cs @@ -0,0 +1,133 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; +using System.Linq; +using NUnit.Framework; +using Spring.Core; +using Spring.Util; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class AssemblyTypeScannerTests + { + #region Setup/Teardown + + [SetUp] + public void _TestSetup() + { + _scanner = new AssemblyObjectDefinitionScanner(); + } + + #endregion + + [Test] + public void AssemblyHavingType_T_Adds_Assembly() + { + _scanner.AssemblyHavingType(); + Assert.That(TypeSources.Any(t => t.Contains(typeof (IOrdered)))); + } + + [Test] + public void IncludeType_T_Adds_Type() + { + _scanner.IncludeType(); + _scanner.IncludeType(); + + IncludePredicates.Any(p => p(typeof (IOrdered))); + IncludePredicates.Any(p => p(typeof (IPriorityOrdered))); + } + + [Test] + public void WithExcludeFilter_Excludes_Type() + { + //var scanner1 = new AssemblyObjectDefinitionScanner(); + + _scanner.IncludeType(); + _scanner.IncludeType(); + _scanner.WithExcludeFilter(t => t.Name.StartsWith("TheImported")); + + IEnumerable types = _scanner.Scan(); + + //Assert.That(types.Any(t => t.Name == "TheConfigurationClass")); + //Assert.False(types.Any(t => t.Name == "TheImportedConfigurationClass")); + + Assert.That(types, Contains.Item((typeof (TheConfigurationClass)))); + Assert.False(types.Contains(typeof (TheImportedConfigurationClass))); + } + + [Test] + public void WithIncludeFilter_Includes_Types() + { + _scanner.WithIncludeFilter(t => t.Name.Contains("ConfigurationClass")); + + IEnumerable types = _scanner.Scan(); + + Assert.That(types, Contains.Item((typeof (TheConfigurationClass)))); + Assert.That(types, Contains.Item((typeof (TheImportedConfigurationClass)))); + 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 + { + get + { + //get at the collection of excludePredicates from the private field + //(yuck!-- test smell, but at least its wrapped up in a neat private property getter!) + return + (List>) (ReflectionUtils.GetInstanceFieldValue(_scanner, "TypeExclusionPredicates")); + } + } + + private List> IncludePredicates + { + get + { + //get at the collection of includePredicates from the private field + //(yuck!-- test smell, but at least its wrapped up in a neat private property getter!) + return + (List>) (ReflectionUtils.GetInstanceFieldValue(_scanner, "TypeInclusionPredicates")); + } + } + + private List> TypeSources + { + get + { + //get at the collection of typeSources from the private field + //(yuck!-- test smell, but at least its wrapped up in a neat private property getter!) + return (List>) (ReflectionUtils.GetInstanceFieldValue(_scanner, "TypeSources")); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/CodeConfigApplicationContextTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/CodeConfigApplicationContextTests.cs new file mode 100644 index 00000000..1424d899 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/CodeConfigApplicationContextTests.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; +using Spring.Context.Support; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class CodeConfigApplicationContextTests : AbstractConfigurationClassPostProcessorTests + { + + protected override void CreateApplicationContext() + { + GenericApplicationContext ctx = new GenericApplicationContext(); + + ctx.ScanAllAssemblies(); + + ctx.Refresh(); + + _ctx = ctx; + } + + } + +} diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassObjectDefinitionReaderTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassObjectDefinitionReaderTests.cs new file mode 100644 index 00000000..92caff71 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassObjectDefinitionReaderTests.cs @@ -0,0 +1,39 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; + +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class ConfigurationClassObjectDefinitionReaderTests + { + [Test] + public void ShouldNotTryToResolveAbstractDefinitionsToType() + { + GenericObjectDefinition definition = new GenericObjectDefinition(); + definition.ObjectTypeName = "~/Default.aspx"; + definition.IsAbstract = true; + Assert.That(ConfigurationClassObjectDefinitionReader.CheckConfigurationClassCandidate(definition), Is.False); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassParserTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassParserTests.cs new file mode 100644 index 00000000..ee7dfa59 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassParserTests.cs @@ -0,0 +1,74 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; + +using Spring.Context.Attributes; +using Spring.Objects.Factory.Parsing; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class ConfigurationClassParserTests + { + private ConfigurationClassParser _parser; + + [SetUp] + public void SetUp() + { + _parser = new ConfigurationClassParser(new FailFastProblemReporter()); + } + + [Test] + public void ShouldBeAbleToRegisterSameNamedConfigurationClassesFromDifferentNamespaces() + { + _parser.Parse(typeof(ConfigurationNameSpace1.SpringConfiguration), "1"); + _parser.Parse(typeof(ConfigurationNameSpace2.SpringConfiguration), "2"); + + Assert.That(_parser.ConfigurationClasses.Count, Is.EqualTo(2), "Did not find two configuration classes"); + } + } +} + +namespace ConfigurationNameSpace1 +{ + [Configuration] + public class SpringConfiguration + { + [ObjectDef] + public virtual string ConfigurationNameSpaceObjectA() + { + return "A"; + } + } +} + +namespace ConfigurationNameSpace2 +{ + [Configuration] + public class SpringConfiguration + { + [ObjectDef] + public virtual string ConfigurationNameSpaceObjectB() + { + return "B"; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs new file mode 100644 index 00000000..74463044 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs @@ -0,0 +1,71 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; +using Spring.Context.Support; +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class ConfigurationClassPostProcessorTests : AbstractConfigurationClassPostProcessorTests + { + + protected override void CreateApplicationContext() + { + GenericApplicationContext ctx = new GenericApplicationContext(); + + var configDefinitionBuilder = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(TheConfigurationClass)); + ctx.RegisterObjectDefinition(configDefinitionBuilder.ObjectDefinition.ObjectTypeName, configDefinitionBuilder.ObjectDefinition); + + var postProcessorDefintionBuilder = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(ConfigurationClassPostProcessor)); + ctx.RegisterObjectDefinition(postProcessorDefintionBuilder.ObjectDefinition.ObjectTypeName, postProcessorDefintionBuilder.ObjectDefinition); + + Assert.That(ctx.ObjectDefinitionCount, Is.EqualTo(2)); + + ctx.Refresh(); + + _ctx = ctx; + } + + + [Test] + public void ShouldAllowConfigurationClassInheritance() + { + var factory = new DefaultListableObjectFactory(); + factory.RegisterObjectDefinition("DerivedConfiguration", new GenericObjectDefinition + { + ObjectType = typeof(DerivedConfiguration) + }); + + var processor = new ConfigurationClassPostProcessor(); + + processor.PostProcessObjectFactory(factory); + + // we should get singleton instances only + TestObject testObject = (TestObject) factory.GetObject("DerivedDefinition"); + string singletonParent = (string) factory.GetObject("BaseDefinition"); + + + Assert.That(testObject.Value, Is.SameAs(singletonParent)); + } + } + +} diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/FailAssemblyObjectDefinitionScannerTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/FailAssemblyObjectDefinitionScannerTests.cs new file mode 100644 index 00000000..f3ead625 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/FailAssemblyObjectDefinitionScannerTests.cs @@ -0,0 +1,207 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; +using Spring.Context.Support; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Parsing; +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class FailAssemblyObjectDefinitionScannerTests + { + #region Setup/Teardown + + [SetUp] + public void _SetUp() + { + _scanner = new AssemblyObjectDefinitionScanner(); + _context = new CodeConfigApplicationContext(); + } + + #endregion + + private void ScanForAndRegisterSingleType(Type type) + { + _scanner.WithIncludeFilter(t => t.Name == type.Name); + _scanner.ScanAndRegisterTypes(_context.DefaultListableObjectFactory); + AttributeConfigUtils.RegisterAttributeConfigProcessors((IObjectDefinitionRegistry)_context.ObjectFactory); + } + + private CodeConfigApplicationContext _context; + private AssemblyObjectDefinitionScanner _scanner; + + [Test] + public void Can_Ignore_Abstract_Configuration_Types() + { + ScanForAndRegisterSingleType(typeof(ConfigurationClassThatIsAbstract)); + Assert.That(_context.GetObjectNamesForType(typeof(ConfigurationClassThatIsAbstract)).Count, Is.EqualTo(0), "Abstract Type erroneously registered with the Context."); + } + + [Test] + public void Can_Prevent_Methods_With_Parameters() + { + ScanForAndRegisterSingleType(typeof(ConfigurationClassWithMethodHavingParameters)); + Assert.Throws(_context.Refresh); + } + + [Test] + public void Can_Prevent_Static_Methods() + { + ScanForAndRegisterSingleType(typeof(ConfigurationClassWithStaticMethod)); + Assert.Throws(_context.Refresh); + } + + [Test] + public void Can_Prevent_Non_Virtual_Methods() + { + ScanForAndRegisterSingleType(typeof(ConfigurationClassWithNonVirtualMethod)); + Assert.Throws(_context.Refresh); + } + + [Test] + public void Can_Prevent_Sealed_Configuration_Types() + { + ScanForAndRegisterSingleType(typeof(ConfigurationClassThatIsSealed)); + Assert.Throws(_context.Refresh); + } + + [Test] + public void Can_Prevent_Overloaded_Methods() + { + ScanForAndRegisterSingleType(typeof(ConfigurationClassWithOverloadedMethods)); + Assert.Throws(_context.Refresh); + } + + [Test] + public void Can_Prevent_Circular_ConfigurationClass_Refereces() + { + ScanForAndRegisterSingleType(typeof(FirstConfigurationClassWithCircularReference)); + + try + { + _context.Refresh(); + } + catch (ObjectDefinitionStoreException ex) + { + Assert.That(ex.InnerException, Is.TypeOf(typeof(ObjectDefinitionParsingException))); + } + } + + } + + public class SomeType + { + } + + + [Configuration] + public class ConfigurationClassWithNonVirtualMethod + { + [ObjectDef] + public SomeType MethodThatRegistersSomeType() + { + return new SomeType(); + } + } + + [Configuration] + public class ConfigurationClassWithStaticMethod + { + [ObjectDef] + public static SomeType MethodThatRegistersSomeType() + { + return new SomeType(); + } + } + + [Configuration] + public class ConfigurationClassWithOverloadedMethods + { + [ObjectDef] + public virtual SomeType MethodThatRegistersSomeType() + { + return new SomeType(); + } + + [ObjectDef] + public virtual SomeType MethodThatRegistersSomeType(int i) + { + return new SomeType(); + } + } + + [Configuration] + [Import(typeof(SecondConfigurationClassWithCircularReference))] + public class FirstConfigurationClassWithCircularReference + { + [ObjectDef] + public virtual SomeType MethodThatRegistersSomeType() + { + return new SomeType(); + } + } + + [Configuration] + [Import(typeof(FirstConfigurationClassWithCircularReference))] + public class SecondConfigurationClassWithCircularReference + { + [ObjectDef] + public virtual SomeType MethodThatRegistersSomeType() + { + return new SomeType(); + } + } + + + [Configuration] + public class ConfigurationClassWithMethodHavingParameters + { + [ObjectDef] + public virtual SomeType MethodThatRegistersSomeType(int i) + { + return new SomeType(); + } + } + + + [Configuration] + public abstract class ConfigurationClassThatIsAbstract + { + [ObjectDef] + public virtual SomeType MethodThatRegistersSomeType() + { + return new SomeType(); + } + } + + [Configuration] + public sealed class ConfigurationClassThatIsSealed + { + [ObjectDef] + public SomeType MethodThatRegistersSomeType() + { + return new SomeType(); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/ImportResourceAttributeTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/ImportResourceAttributeTests.cs new file mode 100644 index 00000000..14033be5 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/ImportResourceAttributeTests.cs @@ -0,0 +1,65 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; +using Spring.Objects.Factory.Xml; +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class ImportResourceAttributeTests + { + [Test] + public void Uses_XmlObjectDefinitionReader_By_Default() + { + var attrib = new ImportResourceAttribute("the resource"); + + Assert.That(attrib.DefinitionReader, Is.EqualTo(typeof(XmlObjectDefinitionReader))); + } + + [Test] + public void Can_Assign_NonDefault_DefinitionReader() + { + var attrib = new ImportResourceAttribute("the resource"); + attrib.DefinitionReader = typeof(AbstractObjectDefinitionReader); + + Assert.That(attrib.DefinitionReader, Is.EqualTo(typeof(AbstractObjectDefinitionReader))); + } + + [Test] + public void DefinitionReader_Can_Prevent_Improper_Types() + { + ImportResourceAttribute attrib = new ImportResourceAttribute("the resource"); + + try + { + attrib.DefinitionReader = typeof(Object);// <--need to pass *anything* ensured *not* to implement IObjectDefinitionReader + Assert.Fail("Expected Exception of type ArgumentException not thrown!"); + } + catch (ArgumentException) + { + //swallow the expected exception + } + + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefAttributeTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefAttributeTests.cs new file mode 100644 index 00000000..96ccf3f1 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefAttributeTests.cs @@ -0,0 +1,54 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class ObjectDefAttributeTests + { + [Test] + public void Can_Accept_Single_Name() + { + var def = new ObjectDefAttribute(); + + def.Names = "Steve"; + + Assert.That(def.NamesToArray[0], Is.EqualTo("Steve")); + } + + + [Test] + public void Can_Accept_Multiple_Names() + { + var def = new ObjectDefAttribute(); + var names = "Name1,Name2,Name3"; + + def.Names = names; + Assert.That(def.NamesToArray[0], Is.EqualTo("Name1")); + Assert.That(def.NamesToArray[1], Is.EqualTo("Name2")); + Assert.That(def.NamesToArray[2], Is.EqualTo("Name3")); + + } + + + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefinitions.xml b/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefinitions.xml new file mode 100644 index 00000000..d6ef5c57 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefinitions.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefinitionsTwo.xml b/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefinitionsTwo.xml new file mode 100644 index 00000000..26eae23a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/ObjectDefinitionsTwo.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/ScanningConfigurationClassPostProcessorTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/ScanningConfigurationClassPostProcessorTests.cs new file mode 100644 index 00000000..213d2089 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/ScanningConfigurationClassPostProcessorTests.cs @@ -0,0 +1,44 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; +using Spring.Context.Config; +using Spring.Context.Support; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Attributes +{ + [TestFixture] + public class ScanningConfigurationClassPostProcessorTests : AbstractConfigurationClassPostProcessorTests + { + protected override void CreateApplicationContext() + { + NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser)); + _ctx = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("SimpleScanTest.xml", GetType())); + + } + + [Test] + public void ContextNotNull() + { + Assert.That(_ctx, Is.Not.Null); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/SimpleScanTest.xml b/test/Spring/Spring.Core.Tests/Context/Attributes/SimpleScanTest.xml new file mode 100644 index 00000000..1e86ed12 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/SimpleScanTest.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Attributes/SimpleScanTests.cs b/test/Spring/Spring.Core.Tests/Context/Attributes/SimpleScanTests.cs new file mode 100644 index 00000000..c034fe54 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Attributes/SimpleScanTests.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; +using Spring.Context.Config; +using Spring.Context.Support; +using Spring.Example.Scannable; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Attributes +{ + + public class SimpleScanTests + { + private IApplicationContext _applicationContext; + + [SetUp] + public void Setup() + { + NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser)); + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("SimpleScanTest.xml", GetType())); + } + + //[Test] + public void FooService() + { + + IFooService fooService = GetObject(); + + } + + public T GetObject() + { + return (T)DoGetInstance(typeof(T), null); + } + public T GetObject(string name) + { + return (T)DoGetInstance(typeof(T), name); + } + + protected object DoGetInstance(Type serviceType, string key) + { + if (key == null) + { + IEnumerator it = DoGetAllInstances(serviceType).GetEnumerator(); + if (it.MoveNext()) + { + return it.Current; + } + throw new ObjectCreationException(string.Format("no services of type '{0}' defined", serviceType.FullName)); + } + return _applicationContext.GetObject(key, serviceType); + } + + /// + /// Resolves service instances by type. + /// + /// Type of service requested. + /// + /// Sequence of service instance objects matching the . + /// + protected IEnumerable DoGetAllInstances(Type serviceType) + { + foreach (string objectName in _applicationContext.GetObjectNamesForType(serviceType)) + { + yield return _applicationContext.GetObject(objectName); + } + } + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Config/AttributeConfigObjectDefinitionParserTests.cs b/test/Spring/Spring.Core.Tests/Context/Config/AttributeConfigObjectDefinitionParserTests.cs new file mode 100644 index 00000000..d34631a4 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/AttributeConfigObjectDefinitionParserTests.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright 2002-2010 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 NUnit.Framework; +using Spring.Context.Attributes; +using Spring.Context.Support; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Config +{ + [TestFixture] + public class AttributeConfigObjectDefinitionParserTests + { + private XmlApplicationContext _applicationContext; + + [SetUp] + public void Setup() + { + NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser)); + } + + [Test] + public void RegisteredComponents() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.AttributeConfigParser.xml", GetType())); + var objectDefintionNames = _applicationContext.ObjectFactory.GetObjectDefinitionNames(); + + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.True); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.AUTOWIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.True); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.REQUIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.True); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.INITDESTROY_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.True); + } + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserAssemblyFilterTests.cs b/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserAssemblyFilterTests.cs new file mode 100644 index 00000000..9e8a6c09 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserAssemblyFilterTests.cs @@ -0,0 +1,49 @@ +using NUnit.Framework; +using Spring.Context.Support; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Config +{ + [TestFixture] + public class ComponentScanObjectDefinitionParserAssemblyFilterTests + { + private IApplicationContext _applicationContext; + + [SetUp] + public void Setup() + { + NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser)); + } + + [Test] + public void BaseAssembliesAttributeRequired() + { + Assert.That(delegate { _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.BaseAssemblyTestWithout.xml", GetType())); }, + Throws.Exception); + } + + [Test] + public void SingleAssemblyNameProvided() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.BaseAssemblyTestSingle.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.GreaterThan(0)); + } + + [Test] + public void MultipleAssemblyNameProvided() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.BaseAssemblyTestMultiple.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.GreaterThan(0)); + } + [Test] + public void NegativeAssemblyNameProvided() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.BaseAssemblyTestNegative.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(4)); + } + + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserTests.cs b/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserTests.cs new file mode 100644 index 00000000..3a175376 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserTests.cs @@ -0,0 +1,323 @@ +#region License + +/* + * Copyright 2002-2010 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 NUnit.Framework; +using Spring.Context.Attributes; +using Spring.Context.Support; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Stereotype; +using Spring.Objects.Factory.Attributes; +using ComponentScan.Qualifier; + +namespace Spring.Context.Config +{ + [TestFixture] + public class ComponentScanObjectDefinitionParserTests + { + private XmlApplicationContext _applicationContext; + + [SetUp] + public void Setup() + { + NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser)); + } + + [Test] + public void ScanComponentsAndAddToContext() + { + var prefix = "ComponentScan.ScanComponentsAndAddToContext."; + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan1.xml", GetType())); + var objectDefinitionNames = _applicationContext.ObjectFactory.GetObjectDefinitionNames(); + + Assert.That(objectDefinitionNames.Count, Is.EqualTo(5+4)); + Assert.That(_applicationContext.GetObject(prefix + "ComponentImpl"), Is.Not.Null); + Assert.That(_applicationContext.GetObject(prefix + "ServiceImpl"), Is.Not.Null); + Assert.That(_applicationContext.GetObject(prefix + "RepositoryImpl"), Is.Not.Null); + Assert.That(_applicationContext.GetObject(prefix + "ControllerImpl"), Is.Not.Null); + Assert.That(_applicationContext.GetObject(prefix + "ConfigurationImpl"), Is.Not.Null); + } + + [Test] + public void ComponentsUseSpecifiedName() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan2.xml", GetType())); + var objectDefinitionNames = _applicationContext.ObjectFactory.GetObjectDefinitionNames(); + + Assert.That(objectDefinitionNames.Count, Is.EqualTo(5 + 4)); + Assert.That(_applicationContext.GetObject("Component"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("Service"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("Repository"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("Controller"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("Configuration"), Is.Not.Null); + } + + [Test] + public void UseSpecifiedObjectNameGenerator() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan3.xml", GetType())); + var objectDefinitionNames = _applicationContext.ObjectFactory.GetObjectDefinitionNames(); + + Assert.That(objectDefinitionNames.Contains("prototype"), Is.True); + } + + [Test] + public void UseWrongObjectNameGeneratorTypeString() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan31.xml", GetType())); + var objectDefinitionNames = _applicationContext.ObjectFactory.GetObjectDefinitionNames(); + + Assert.That(objectDefinitionNames.Contains("prototype"), Is.False); + Assert.That(objectDefinitionNames.Contains("ComponentScan.NameGenerator.Prototype"), Is.True); + } + [Test] + public void ComponentsLazyLoaded() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan4.xml", GetType())); + var objectDefinition = _applicationContext.ObjectFactory.GetObjectDefinition("LazyInit"); + + Assert.That(objectDefinition.IsLazyInit, Is.True); + } + + [Test] + public void ComponentsInDifferentScope() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan4.xml", GetType())); + var singletonDef = _applicationContext.ObjectFactory.GetObjectDefinition("Singleton"); + var prototypeDef = _applicationContext.ObjectFactory.GetObjectDefinition("Prototype"); + + Assert.That(singletonDef.IsSingleton, Is.True); + Assert.That(singletonDef.Scope, Is.EqualTo(ObjectScope.Singleton.ToString().ToLower())); + + Assert.That(prototypeDef.IsSingleton, Is.False); + Assert.That(prototypeDef.Scope, Is.EqualTo(ObjectScope.Prototype.ToString().ToLower())); + } + + [Test] + public void ComponentsUseDefaults() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan5.xml", GetType())); + var prototypeDef = _applicationContext.ObjectFactory.GetObjectDefinition("Prototype"); + + Assert.That(prototypeDef.IsLazyInit, Is.True); + } + + [Test] + public void ComponentWithQualifier() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan6.xml", GetType())); + var objectDef = _applicationContext.ObjectFactory.GetObjectDefinition("Prototype") as ScannedGenericObjectDefinition; + + Assert.That(objectDef.HasQualifier(typeof(QualifierAttribute).Name), Is.True); + + var attr = objectDef.GetQualifier(typeof (QualifierAttribute).Name).GetAttribute(AutowireCandidateQualifier.VALUE_KEY); + Assert.That(attr, Is.EqualTo("action")); + } + + [Test] + public void ComponentWithQualifierAttributes() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScan6.xml", GetType())); + var objectDef = _applicationContext.ObjectFactory.GetObjectDefinition("Attribute") as ScannedGenericObjectDefinition; + var qualifier = objectDef.GetQualifier(typeof (MyQualifier).Name); + + Assert.That(qualifier, Is.Not.Null); + + var attr = qualifier.GetMetadataAttribute("Foo"); + Assert.That(attr, Is.Not.Null); + Assert.That(attr.Value, Is.EqualTo("Funny")); + } + + [Test] + public void DontRegisterAttributeConfig() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScanAttributeConfigFalse.xml", GetType())); + var objectDefintionNames = _applicationContext.ObjectFactory.GetObjectDefinitionNames(); + + Assert.That(objectDefintionNames.Count, Is.EqualTo(0)); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.False); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.AUTOWIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.False); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.REQUIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.False); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.INITDESTROY_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.False); + } + + [Test] + public void RegisterAttributeConfig() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.ComponentScanAttributeConfigTrue.xml", GetType())); + var objectDefintionNames = _applicationContext.ObjectFactory.GetObjectDefinitionNames(); + + Assert.That(objectDefintionNames.Count, Is.EqualTo(4)); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.CONFIGURATION_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.True); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.AUTOWIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.True); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.REQUIRED_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.True); + Assert.That(objectDefintionNames.Contains(AttributeConfigUtils.INITDESTROY_ATTRIBUTE_PROCESSOR_OBJECT_NAME), Is.True); + } + + } +} + +namespace ComponentScan.ScanComponentsAndAddToContext +{ + public interface IFoo + { + } + + [Component] + public class ComponentImpl : IFoo + { + } + + [Service] + public class ServiceImpl : IFoo + { + } + + [Repository] + public class RepositoryImpl : IFoo + { + } + + [Controller] + public class ControllerImpl : IFoo + { + } + + [Configuration] + public class ConfigurationImpl : IFoo + { + } +} + +namespace ComponentScan.ComponentsUseSpecifiedName +{ + public interface IFoo + { + } + + [Component("Component")] + public class ComponentImpl : IFoo + { + } + + [Service("Service")] + public class ServiceImpl : IFoo + { + } + + [Repository("Repository")] + public class RepositoryImpl : IFoo + { + } + + [Controller("Controller")] + public class ControllerImpl : IFoo + { + } + + [Configuration("Configuration")] + public class ConfigurationImpl : IFoo + { + } +} + +namespace ComponentScan.ComponentsAttributeLoad +{ + public interface IFoo + { + } + + [Component("LazyInit")] + [Lazy] + public class LazyImpl : IFoo + { + } + + [Component("Singleton")] + [Scope(ObjectScope.Singleton)] + public class SingletonImpl : IFoo + { + } + + [Component("Prototype")] + [Scope(ObjectScope.Prototype)] + public class PrototypeImpl : IFoo + { + } +} + +namespace ComponentScan.ComponentsUseDefaults +{ + public interface IFoo + { + } + + [Component("Prototype")] + public class PrototypeImpl : IFoo + { + } +} + +namespace ComponentScan.Qualifier +{ + public interface IFoo + { + } + + public class MyQualifier : QualifierAttribute + { + public string Foo { get; set; } + } + + [Component("Prototype")] + [Qualifier("action")] + public class PrototypeImpl : IFoo + { + } + + [Component("Attribute")] + [MyQualifier(Foo="Funny")] + public class QualifierAttributeImpl : IFoo + { + } + +} + +namespace ComponentScan.NameGenerator +{ + public interface IFoo + { + } + + public class MyGenerator : IObjectNameGenerator + { + public string GenerateObjectName(IObjectDefinition definition, IObjectDefinitionRegistry registry) + { + string typeName = definition.ObjectType.Name; + return typeName.ToLower(); + } + } + + [Component] + public class Prototype : IFoo + { + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserTypeFilterTests.cs b/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserTypeFilterTests.cs new file mode 100644 index 00000000..f02885bd --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ComponentScanObjectDefinitionParserTypeFilterTests.cs @@ -0,0 +1,224 @@ +#region License + +/* + * Copyright 2002-2010 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 NUnit.Framework; +using Spring.Context.Attributes; +using Spring.Context.Attributes.TypeFilters; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Xml; +using Spring.Context.Support; +using Spring.Stereotype; + + +namespace Spring.Context.Config +{ + [TestFixture] + public class ComponentScanObjectDefinitionParserTypeFilterTests + { + private IApplicationContext _applicationContext; + + [SetUp] + public void Setup() + { + NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser)); + } + + [Test] + public void IncludeRegExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestRegExInclude.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(6)); + Assert.That(_applicationContext.GetObject("SomeIncludeType1"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeExcludeType"); }, Throws.Exception.TypeOf()); + } + + [Test] + public void IncludeMultipleRegExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestRegExInclude2.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(8)); + Assert.That(_applicationContext.GetObject("SomeIncludeType1"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("SomeIncludeType2"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeExcludeType"); }, Throws.Exception.TypeOf()); + } + + [Test] + public void ExcludeRegExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestRegExExclude.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(8)); + Assert.That(_applicationContext.GetObject("SomeIncludeType1"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeExcludeType"); }, Throws.Exception.TypeOf()); + } + + [Test] + public void IncludeAttributeExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestAttributeInclude.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(6)); + Assert.That(_applicationContext.GetObject("SomeIncludeType2"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeExcludeType"); }, Throws.Exception.TypeOf()); + } + + [Test] + public void ExcludeAttributeExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestAttributeExclude.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(8)); + Assert.That(_applicationContext.GetObject("SomeIncludeType2"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("SomeExcludeType"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeIncludeType1"); }, Throws.Exception.TypeOf()); + } + + [Test] + public void IncludeAssignableExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestAssignableInclude.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(8)); + Assert.That(_applicationContext.GetObject("SomeIncludeType1"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("SomeIncludeType2"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeExcludeType"); }, Throws.Exception.TypeOf()); + } + + [Test] + public void ExcludeAssignableExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestAssignableExclude.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(8)); + Assert.That(_applicationContext.GetObject("SomeIncludeType1"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("SomeExcludeType"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeIncludeType2"); }, Throws.Exception.TypeOf()); + } + + [Test] + public void IncludeCustomExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestCustomInclude.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(6)); + Assert.That(_applicationContext.GetObject("SomeIncludeType1"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeIncludeType2"); }, Throws.Exception.TypeOf()); + } + + [Test] + public void ExcludeCustomExpressionFilter() + { + _applicationContext = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("ConfigFiles.TypeScannerTestCustomExclude.xml", GetType())); + + Assert.That(_applicationContext.GetObjectDefinitionNames().Count, Is.EqualTo(8)); + Assert.That(_applicationContext.GetObject("SomeIncludeType2"), Is.Not.Null); + Assert.That(_applicationContext.GetObject("SomeExcludeType"), Is.Not.Null); + Assert.That(delegate { _applicationContext.GetObject("SomeIncludeType1"); }, Throws.Exception.TypeOf()); + } + + } +} + +namespace XmlAssemblyTypeScanner.Test.Include1 +{ + [AttributeUsage(AttributeTargets.Class)] + public class DoNotIncludeAttribute : Attribute + { + } + + [Configuration] + [DoNotInclude] + public class SomeIncludeConfiguration1 : IFunny + { + [ObjectDef] + public virtual SomeIncludeType1 SomeIncludeType1() + { + return new SomeIncludeType1(); + } + } + + public class SomeIncludeType1 + { + } + + public interface IFunny + {} + + + public class TestFilter : ITypeFilter + { + public bool Match(Type type) + { + return type.Name.Equals("SomeIncludeConfiguration1"); + } + } + +} + +namespace XmlAssemblyTypeScanner.Test.Include2 +{ + [AttributeUsage(AttributeTargets.Class)] + public class DoIncludeAttribute : Attribute + { + } + + [Configuration] + [DoInclude] + public class SomeIncludeConfiguration2 : FunnyAbstract + { + public override void Test() { } + + [ObjectDef] + public virtual SomeIncludeType2 SomeIncludeType2() + { + return new SomeIncludeType2(); + } + } + + public class SomeIncludeType2 + { + } + + public abstract class FunnyAbstract + { + public abstract void Test(); + } +} + +namespace XmlAssemblyTypeScanner.Test.Include +{ + [Configuration] + public class SomeExcludeConfiguration3 + { + + [ObjectDef] + public virtual SomeExcludeType SomeExcludeType() + { + return new SomeExcludeType(); + } + } + + public class SomeExcludeType + { + } +} diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/AttributeConfigParser.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/AttributeConfigParser.xml new file mode 100644 index 00000000..f4b24465 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/AttributeConfigParser.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestMultiple.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestMultiple.xml new file mode 100644 index 00000000..f0fce716 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestMultiple.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestNegative.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestNegative.xml new file mode 100644 index 00000000..bf2405fc --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestNegative.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestSingle.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestSingle.xml new file mode 100644 index 00000000..ce45156c --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestSingle.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestWithout.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestWithout.xml new file mode 100644 index 00000000..7d781430 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/BaseAssemblyTestWithout.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan1.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan1.xml new file mode 100644 index 00000000..a68cc463 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan1.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan2.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan2.xml new file mode 100644 index 00000000..629573f8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan2.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan3.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan3.xml new file mode 100644 index 00000000..87c3cf85 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan3.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan31.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan31.xml new file mode 100644 index 00000000..1162c903 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan31.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan4.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan4.xml new file mode 100644 index 00000000..5946e407 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan4.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan5.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan5.xml new file mode 100644 index 00000000..85eb51a7 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan5.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan6.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan6.xml new file mode 100644 index 00000000..da0b4171 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScan6.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScanAttributeConfigFalse.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScanAttributeConfigFalse.xml new file mode 100644 index 00000000..740a3e73 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScanAttributeConfigFalse.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScanAttributeConfigTrue.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScanAttributeConfigTrue.xml new file mode 100644 index 00000000..fac0ba27 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/ComponentScanAttributeConfigTrue.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAssignableExclude.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAssignableExclude.xml new file mode 100644 index 00000000..625a311b --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAssignableExclude.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAssignableInclude.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAssignableInclude.xml new file mode 100644 index 00000000..53a660e8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAssignableInclude.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAttributeExclude.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAttributeExclude.xml new file mode 100644 index 00000000..26458303 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAttributeExclude.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAttributeInclude.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAttributeInclude.xml new file mode 100644 index 00000000..bacd9861 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestAttributeInclude.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestCustomExclude.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestCustomExclude.xml new file mode 100644 index 00000000..73a7710a --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestCustomExclude.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestCustomInclude.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestCustomInclude.xml new file mode 100644 index 00000000..8299fb19 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestCustomInclude.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExExclude.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExExclude.xml new file mode 100644 index 00000000..096c39e8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExExclude.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExInclude.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExInclude.xml new file mode 100644 index 00000000..0e929860 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExInclude.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExInclude2.xml b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExInclude2.xml new file mode 100644 index 00000000..0727c5ed --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ConfigFiles/TypeScannerTestRegExInclude2.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Context/Config/ContextNamespaceParserTests.cs b/test/Spring/Spring.Core.Tests/Context/Config/ContextNamespaceParserTests.cs new file mode 100644 index 00000000..9a638046 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Config/ContextNamespaceParserTests.cs @@ -0,0 +1,42 @@ +#region License + +/* + * Copyright © 2010-2011 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 NUnit.Framework; +using Spring.Objects.Factory.Xml; + +namespace Spring.Context.Config +{ + [TestFixture] + public class ContextNamespaceParserTests + { + [SetUp] + public void Setup() + { + NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser)); + } + + [Test] + public void Registered() + { + Assert.IsNotNull(NamespaceParserRegistry.GetParser("http://www.springframework.net/context")); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Context/Support/CodeConfigApplicationContextTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/CodeConfigApplicationContextTests.cs new file mode 100644 index 00000000..8e85f517 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Context/Support/CodeConfigApplicationContextTests.cs @@ -0,0 +1,142 @@ +#region License + +/* + * Copyright © 2010-2011 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.Generic; +using System.Linq; +using NUnit.Framework; +using Spring.Context.Attributes; + +namespace Spring.Context.Support +{ + [TestFixture] + public class CodeConfigApplicationContextTests + { + private CodeConfigApplicationContext _context; + + [SetUp] + public void _TestSetup() + { + _context = new CodeConfigApplicationContext(); + } + + [Test] + public void Can_Filter_For_Assembly_Based_On_Assembly_Metadata() + { + _context.ScanWithAssemblyFilter(a => a.GetName().Name.StartsWith("Spring.Core.")); + _context.Refresh(); + + AssertExpectedObjectsAreRegisteredWith(_context, 45); + } + + [Test] + 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(); + + Assert.That(_context.DefaultListableObjectFactory.ObjectDefinitionCount, Is.EqualTo(4)); + } + + [Test] + public void Can_Filter_For_Assembly_Containing_Specific_Type() + { + _context.ScanWithAssemblyFilter(assy => assy.GetTypes().Any(type => type.FullName.Contains(typeof(MarkerTypeForScannerToFind).Name))); + _context.Refresh(); + + AssertExpectedObjectsAreRegisteredWith(_context, 45); + } + + [Test] + public void Can_Filter_For_Specific_Type() + { + _context.ScanWithTypeFilter(type => type.FullName.Contains(typeof(TheImportedConfigurationClass).Name)); + _context.Refresh(); + + Assert.That(_context.DefaultListableObjectFactory.ObjectDefinitionCount, Is.EqualTo(8)); + } + + [Test] + public void Can_Filter_For_Specific_Types_With_Compound_Predicate() + { + _context.ScanWithTypeFilter(type => type.FullName.Contains(typeof(TheImportedConfigurationClass).Name) || type.FullName.Contains(typeof(TheConfigurationClass).Name)); + _context.Refresh(); + + AssertExpectedObjectsAreRegisteredWith(_context, 19); + } + + [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)); + + _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() + { + _context.ScanAllAssemblies(); + _context.Refresh(); + + AssertExpectedObjectsAreRegisteredWith(_context, 45); + } + + private void AssertExpectedObjectsAreRegisteredWith(GenericApplicationContext context, int expectedDefinitionCount) + { + // only check names that are not part of configuration namespace test + List names = new List(context.DefaultListableObjectFactory.GetObjectDefinitionNames()); + names.RemoveAll(x => x.StartsWith("ConfigurationNameSpace")); + + + if (names.Count != expectedDefinitionCount) + { + Console.WriteLine("Actual types registered with the container:"); + foreach (var name in names) + { + Console.WriteLine(name); + } + } + + + Assert.That(names.Count, Is.EqualTo(expectedDefinitionCount)); + } + + } + + public class MarkerTypeForScannerToFind + { + + } +} diff --git a/test/Spring/Spring.Core.Tests/Example/Scannable/FooService.cs b/test/Spring/Spring.Core.Tests/Example/Scannable/FooService.cs new file mode 100644 index 00000000..9e4238ed --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Example/Scannable/FooService.cs @@ -0,0 +1,51 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Stereotype; + +namespace Spring.Example.Scannable +{ + /// + /// + /// + /// Mark Pollack + [Service] + public class FooService : IFooService + { + private string foo; + + private bool initCalled; + + + + public string Foo + { + get { return foo; } + set { foo = value; } + } + + public bool InitCalled + { + get { return initCalled; } + set { initCalled = value; } + } + } + +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Example/Scannable/IFooDao.cs b/test/Spring/Spring.Core.Tests/Example/Scannable/IFooDao.cs new file mode 100644 index 00000000..de78ead8 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Example/Scannable/IFooDao.cs @@ -0,0 +1,32 @@ +#region License + +/* + * Copyright © 2010-2011 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 + +namespace Spring.Example.Scannable +{ + /// + /// + /// + /// Mark Pollack + public interface IFooDao + { + string FindFoo(string id); + } + +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Example/Scannable/IFooService.cs b/test/Spring/Spring.Core.Tests/Example/Scannable/IFooService.cs new file mode 100644 index 00000000..8b0ed44d --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Example/Scannable/IFooService.cs @@ -0,0 +1,35 @@ +#region License + +/* + * Copyright © 2010-2011 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 + +namespace Spring.Example.Scannable +{ + /// + /// Simple service for testing of component scanning + /// + /// Mark Pollack + public interface IFooService + { + string Foo { get; set; } + + bool InitCalled { get; set; } + + } + +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Example/Scannable/StubFooDao.cs b/test/Spring/Spring.Core.Tests/Example/Scannable/StubFooDao.cs new file mode 100644 index 00000000..3d15ebbc --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Example/Scannable/StubFooDao.cs @@ -0,0 +1,38 @@ +#region License + +/* + * Copyright © 2010-2011 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 Spring.Stereotype; + +namespace Spring.Example.Scannable +{ + /// + /// + /// + /// Mark Pollack + [Repository] + public class StubFooDao : IFooDao + { + public string FindFoo(string id) + { + return "bar"; + } + } + +} \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2010.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2010.csproj index 2cb48597..04a141ef 100644 --- a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2010.csproj +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2010.csproj @@ -152,9 +152,25 @@ Code + + + + + + + + + + + Code + + + + + Code @@ -183,6 +199,7 @@ Code + Code @@ -281,6 +298,10 @@ Code + + + + @@ -804,6 +825,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +