#region License
/*
* Copyright 2002-2005 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 Imports
using System;
using System.Collections.Specialized;
using System.Globalization;
using Common.Logging;
using Spring.Core;
using Spring.Core.IO;
#endregion
namespace Spring.Objects.Factory.Config{
/// <summary>
/// Allows for the configuration of individual object property values from
/// a .NET .config file.
/// </summary>
/// <remarks>
/// <p>
/// Useful for custom .NET .config files targetted at system administrators
/// that override object properties configured in the application context.
/// </p>
/// <p>
/// Two concrete implementations are provided in the Spring.NET core library:
/// <list type="bullet">
/// <item>
/// <description>
/// <see cref="Spring.Objects.Factory.Config.PropertyOverrideConfigurer"/>
/// for <c><add key="placeholderKey" value="..."/></c> style
/// overriding (pushing values from a .NET .config file into object
/// definitions).
/// </description>
/// </item>
/// <item>
/// <description>
/// <see cref="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer"/>
/// for replacing "${...}" placeholders (pulling values from a .NET .config
/// file into object definitions).
/// </description>
/// </item>
/// </list>
/// </p>
/// <p>
/// Please refer to the API documentation for the concrete implementations
/// listed above for example usage.
/// </p>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Simon White (.NET)</author>
/// <seealso cref="Spring.Objects.Factory.Config.PropertyOverrideConfigurer"/>
/// <seealso cref="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer"/>
[Serializable]
public abstract class PropertyResourceConfigurer
: IObjectFactoryPostProcessor, IOrdered
{
/// <summary>
/// The default configuration section name to use if none is explictly supplied.
/// </summary>
/// <seealso cref="Spring.Objects.Factory.Config.PropertyResourceConfigurer.ConfigSections"/>
public static readonly string DefaultConfigSectionName = "spring-config";
#region Fields
private readonly ILog _log;
private int _order = Int32.MaxValue; // default: same as non-Ordered
private NameValueCollection _defaultProperties;
private IResource[] _locations;
private string[] _configSections;
private bool _ignoreResourceNotFound = false;
private bool _lastLocationOverrides = true;
#endregion
#region Constructor (s) / Destructor
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Objects.Factory.Config.PropertyResourceConfigurer"/>
/// class.
/// </summary>
/// <remarks>
/// <p>
/// This is an <see langword="abstract"/> class, and as such exposes no
/// public constructors.
/// </p>
/// </remarks>
protected PropertyResourceConfigurer()
{
_log = LogManager.GetLogger(this.GetType());
}
#endregion
#region Properties
/// <summary>
/// The policy for resolving conflicting property overrides from
/// several resources.
/// </summary>
/// <remarks>
/// <p>
/// When merging conflicting property overrides from several resources,
/// should append an override with the same key be appended to the
/// current value, or should the property override from the last resource
/// processed override previous values?
/// </p>
/// <p>
/// The default value is <see langword="true"/>; i.e. a property
/// override from the last resource to be processed overrides previous
/// values.
/// </p>
/// </remarks>
/// <value>
/// <see langword="true"/> if the property override from the last resource
/// processed overrides previous values.
/// </value>
public bool LastLocationOverrides
{
set { _lastLocationOverrides = value; }
}
/// <summary>
/// Return the order value of this object, where a higher value means greater in
/// terms of sorting.
/// </summary>
/// <returns>The order value.</returns>
/// <seealso cref="Spring.Core.IOrdered.Order"/>
public int Order
{
get { return _order; }
set { _order = value; }
}
/// <summary>
/// The default properties to be applied.
/// </summary>
/// <remarks>
/// <p>
/// These are to be considered defaults, to be overridden by values
/// loaded from other resources.
/// </p>
/// </remarks>
public NameValueCollection Properties
{
get { return _defaultProperties; }
set { _defaultProperties = value; }
}
/// <summary>
/// The location of the .NET .config file that contains the property
/// overrides that are to be applied.
/// </summary>
public IResource Location
{
set { _locations = new IResource[] {value}; }
}
/// <summary>
/// The locations of the .NET .config files containing the property
/// overrides that are to be applied.
/// </summary>
public IResource[] Locations
{
set { _locations = value; }
}
/// <summary>
/// The configuration sections to look for within the .config files.
/// </summary>
/// <seealso cref="Spring.Objects.Factory.Config.PropertyResourceConfigurer.Location"/>
/// <seealso cref="Spring.Objects.Factory.Config.PropertyResourceConfigurer.Locations"/>
public string[] ConfigSections
{
get
{
if (_configSections == null
|| _configSections.Length == 0)
{
_configSections = new string[] {DefaultConfigSectionName};
}
return _configSections;
}
set { _configSections = value; }
}
/// <summary>
/// Should a failure to find a .config file be ignored?
/// </summary>
/// <remarks>
/// <p>
/// <see langword="true"/> is only appropriate if the .config file is
/// completely optional. The default is <see langword="false"/>.
/// </p>
/// </remarks>
/// <value>
/// <see langword="true"/> if a failure to find a .config file is to be
/// ignored.
/// </value>
public bool IgnoreResourceNotFound
{
set { _ignoreResourceNotFound = value; }
}
#endregion
/// <summary>
/// Modify the application context's internal object factory after its
/// standard initialization.
/// </summary>
/// <param name="factory">
/// The object factory used by the application context.
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of errors.
/// </exception>
/// <seealso cref="Spring.Objects.Factory.Config.IObjectFactoryPostProcessor.PostProcessObjectFactory(IConfigurableListableObjectFactory)"/>
public void PostProcessObjectFactory(IConfigurableListableObjectFactory factory)
{
try
{
NameValueCollection properties = new NameValueCollection();
InitializeWithDefaultProperties(properties);
LoadProperties(properties);
ProcessProperties(factory, properties);
}
catch (Exception ex)
{
if (typeof(ObjectsException).IsInstanceOfType(ex))
{
throw;
}
else
{
throw new ObjectsException(
"Errored while postprocessing an object factory.", ex);
}
}
}
/// <summary>
/// Loads properties from the configuration sections
/// specified in <see cref="ConfigSections"/> into <paramref name="properties"/>.
/// </summary>
/// <param name="properties">The <see cref="NameValueCollection"/> instance to be filled with properties.</param>
protected virtual void LoadProperties(NameValueCollection properties)
{
string[] configSections = ConfigSections;
if (_locations != null)
{
ValidateConfigSections(configSections);
bool usingMultipleConfigSections = configSections.Length > 1;
int sectionNameIndex = 0;
foreach (IResource resource in _locations)
{
#region Instrumentation
if (_log.IsDebugEnabled)
{
_log.Debug(string.Format(
CultureInfo.InvariantCulture,
"Loading configuration from '{0}'.", resource));
}
#endregion
string sectionName = configSections[sectionNameIndex];
if (resource is ConfigSectionResource)
{
ConfigurationReader.PopulateFromAppConfig(
properties, sectionName, _lastLocationOverrides);
}
else
{
if (resource.Exists)
{
ConfigurationReader.Read(
resource, sectionName, properties, _lastLocationOverrides);
}
else
{
string errorMessage = "Could not load configuration from " + resource;
if (_ignoreResourceNotFound)
{
#region Instrumentation
if (_log.IsWarnEnabled)
{
_log.Warn(errorMessage);
}
#endregion
}
else
{
throw new ObjectInitializationException(errorMessage);
}
}
}
if (usingMultipleConfigSections)
{
++sectionNameIndex;
}
}
}
else
{
foreach (string sectionName in configSections)
{
ConfigurationReader.PopulateFromAppConfig(
properties, sectionName, _lastLocationOverrides);
}
}
}
/// <summary>
/// Apply the given properties to the supplied
/// <see cref="Spring.Objects.Factory.Config.IConfigurableListableObjectFactory"/>.
/// </summary>
/// <param name="factory">
/// The <see cref="Spring.Objects.Factory.Config.IConfigurableListableObjectFactory"/>
/// used by the application context.
/// </param>
/// <param name="props">The properties to apply.</param>
/// <exception cref="Spring.Objects.ObjectsException">
/// If an error occured.
/// </exception>
protected abstract void ProcessProperties(
IConfigurableListableObjectFactory factory,
NameValueCollection props);
/// <summary>
/// Validates the supplied <paramref name="configSections"/>.
/// </summary>
/// <remarks>
/// <p>
/// Basically, if external locations are specified, ensure that either
/// one or a like number of config sections are also specified.
/// </p>
/// </remarks>
/// <param name="configSections">
/// The <paramref name="configSections"/> to be validated.
/// </param>
private void ValidateConfigSections(string[] configSections)
{
if (_locations.Length != configSections.Length)
{
// if only one config section is specified for all locations that's cool...
if (configSections.Length != 1)
{
throw new ObjectInitializationException(
"Invalid number of config sections specified.");
}
}
}
/// <summary>
/// Simply initializes the supplied <paramref name="properties"/>
/// collection with this instances default <see cref="Properties"/>
/// (if any).
/// </summary>
/// <param name="properties">
/// The collection to be so initialized.
/// </param>
private void InitializeWithDefaultProperties(NameValueCollection properties)
{
if (Properties != null)
{
properties.Add(Properties);
}
}
}
}
|