using Amazon.Runtime.Internal.Util; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace Amazon.Runtime.Internal { /// /// A registry of object that will manipulate the runtime pipeline used by service clients. /// public class RuntimePipelineCustomizerRegistry : IDisposable { public static RuntimePipelineCustomizerRegistry Instance { get; } = new RuntimePipelineCustomizerRegistry(); private RuntimePipelineCustomizerRegistry() { } Logger _logger = Logger.GetLogger(typeof(RuntimePipelineCustomizerRegistry)); ReaderWriterLockSlim _rwlock = new ReaderWriterLockSlim(); // List is used instead of a dictionary to maintain order IList _customizers = new List(); /// /// Registers a customizer that will be applied for all service clients created. Each customizer has a unique name associated with it. If a customizer is registered more /// than once with the same unique name then the calls after the first will be ignored. /// /// public void Register(IRuntimePipelineCustomizer customizer) { _rwlock.EnterWriteLock(); try { // If the customizer is already registered then skip adding it again. This could happen if 2 separate libraries are added // to a project and they both one to turn on a third party customizer if (_customizers.FirstOrDefault(x => string.Equals(x.UniqueName, customizer.UniqueName)) != null) { _logger.InfoFormat("Skipping registration because runtime pipeline customizer {0} already registered", customizer.UniqueName); return; } _logger.InfoFormat("Registering runtime pipeline customizer {0}", customizer.UniqueName); _customizers.Add(customizer); } finally { _rwlock.ExitWriteLock(); } } /// /// Deregistered the runtime pipeline customizer /// /// public void Deregister(IRuntimePipelineCustomizer customizer) { Deregister(customizer.UniqueName); } /// /// Deregistered the runtime pipeline customizer /// /// public void Deregister(string uniqueName) { _rwlock.EnterWriteLock(); try { int pos = -1; for(int i = 0; i < _customizers.Count; i++) { if(string.Equals(uniqueName, _customizers[i].UniqueName, StringComparison.Ordinal)) { pos = i; break; } } if(pos != -1) { _logger.InfoFormat("Deregistering runtime pipeline customizer {0}", uniqueName); _customizers.RemoveAt(pos); } else { _logger.InfoFormat("Runtime pipeline customizer {0} not found to deregister", uniqueName); } } finally { _rwlock.ExitWriteLock(); } } /// /// Applies all of the registered customizers on the runtime pipeline /// /// The service clients runtime pipeline. /// Type object for the service client being created internal void ApplyCustomizations(Type type, RuntimePipeline pipeline) { _rwlock.EnterReadLock(); try { foreach (var customizer in _customizers) { _logger.InfoFormat("Applying runtime pipeline customization {0}", customizer.UniqueName); customizer.Customize(type, pipeline); } } finally { _rwlock.ExitReadLock(); } } #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (_rwlock != null) { _rwlock.Dispose(); _rwlock = null; } } } #endregion } /// /// Interface for objects that will customize the runtime pipleine for newly created service clients. /// public interface IRuntimePipelineCustomizer { /// /// The unique name for the customizer that identifies the customizer in the registry. The name is also used to identify the customizer on the SDK logs. /// string UniqueName { get; } /// /// Called on service clients as they are being constructed to customize their runtime pipeline. /// /// /// Type object for the service client being created void Customize(Type type, RuntimePipeline pipeline); } }