/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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. */ using Amazon.Runtime.Internal.Util; using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; namespace Amazon.Runtime.Internal { /// /// A runtime pipeline contains a collection of handlers which represent /// different stages of request and response processing. /// public partial class RuntimePipeline : IDisposable { #region Private members bool _disposed; ILogger _logger; // The top-most handler in the pipeline. IPipelineHandler _handler; #endregion #region Properties /// /// The top-most handler in the pipeline. /// public IPipelineHandler Handler { get { return _handler; } } #endregion #region Constructors /// /// Constructor for RuntimePipeline. /// /// The handler with which the pipeline is initialized. public RuntimePipeline(IPipelineHandler handler) { if (handler == null) throw new ArgumentNullException("handler"); var logger = Amazon.Runtime.Internal.Util.Logger.GetLogger(typeof(RuntimePipeline)); _handler = handler; _handler.Logger = logger; _logger = logger; } /// /// Constructor for RuntimePipeline. /// /// List of handlers with which the pipeline is initialized. public RuntimePipeline(IList handlers) : this(handlers, Amazon.Runtime.Internal.Util.Logger.GetLogger(typeof(RuntimePipeline))) { } /// /// Constructor for RuntimePipeline. /// /// List of handlers with which the pipeline is initialized. /// The logger used to log messages. public RuntimePipeline(IList handlers, ILogger logger) { if (handlers == null || handlers.Count == 0) throw new ArgumentNullException("handlers"); if (logger == null) throw new ArgumentNullException("logger"); _logger = logger; foreach (var handler in handlers) { this.AddHandler(handler); } } /// /// Constructor for RuntimePipeline. /// /// The handler with which the pipeline is initialized. /// The logger used to log messages. public RuntimePipeline(IPipelineHandler handler, ILogger logger) { if (handler == null) throw new ArgumentNullException("handler"); if (logger == null) throw new ArgumentNullException("logger"); _handler = handler; _handler.Logger = logger; _logger = logger; } #endregion #region Invoke methods /// /// Invokes the pipeline synchronously. /// /// Request context /// Response context public IResponseContext InvokeSync(IExecutionContext executionContext) { ThrowIfDisposed(); _handler.InvokeSync(executionContext); return executionContext.ResponseContext; } #if AWS_ASYNC_API /// /// Invokes the pipeline asynchronously. /// /// Request context /// Response context public System.Threading.Tasks.Task InvokeAsync(IExecutionContext executionContext) where T : AmazonWebServiceResponse, new() { ThrowIfDisposed(); return _handler.InvokeAsync(executionContext); } #elif AWS_APM_API /// /// Invokes the pipeline asynchronously. /// /// Request context /// IAsyncResult which represents the in progress asynchronous operation. public IAsyncResult InvokeAsync(IAsyncExecutionContext executionContext) { ThrowIfDisposed(); return _handler.InvokeAsync(executionContext); } #endif #endregion #region Handler methods /// /// Adds a new handler to the top of the pipeline. /// /// The handler to be added to the pipeline. public void AddHandler(IPipelineHandler handler) { if (handler == null) throw new ArgumentNullException("handler"); ThrowIfDisposed(); var innerMostHandler = GetInnermostHandler(handler); if (_handler != null) { innerMostHandler.InnerHandler = _handler; _handler.OuterHandler = innerMostHandler; } _handler = handler; SetHandlerProperties(handler); } /// /// Adds a handler after the first instance of handler of type T. /// /// Type of the handler after which the given handler instance is added. /// The handler to be added to the pipeline. public void AddHandlerAfter(IPipelineHandler handler) where T : IPipelineHandler { if (handler == null) throw new ArgumentNullException("handler"); ThrowIfDisposed(); var type = typeof(T); var current = _handler; while (current != null) { if (current.GetType() == type) { InsertHandler(handler, current); SetHandlerProperties(handler); return; } current = current.InnerHandler; } throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "Cannot find a handler of type {0}", type.Name)); } /// /// Adds a handler before the first instance of handler of type T. /// /// Type of the handler before which the given handler instance is added. /// The handler to be added to the pipeline. public void AddHandlerBefore(IPipelineHandler handler) where T : IPipelineHandler { if (handler == null) throw new ArgumentNullException("handler"); ThrowIfDisposed(); var type = typeof(T); if (_handler.GetType() == type) { // Add the handler to the top of the pipeline AddHandler(handler); SetHandlerProperties(handler); return; } var current = _handler; while (current != null) { if (current.InnerHandler != null && current.InnerHandler.GetType() == type) { InsertHandler(handler, current); SetHandlerProperties(handler); return; } current = current.InnerHandler; } throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "Cannot find a handler of type {0}", type.Name)); } /// /// Removes the first occurance of a handler of type T. /// /// Type of the handler which will be removed. public void RemoveHandler() { ThrowIfDisposed(); var type = typeof(T); IPipelineHandler previous = null; var current = _handler; while (current != null) { if (current.GetType() == type) { // Cannot remove the handler if it's the only one in the pipeline if (current == _handler && _handler.InnerHandler == null) { throw new InvalidOperationException( "The pipeline contains a single handler, cannot remove the only handler in the pipeline."); } // current is the top, point top to current's inner handler if (current == _handler) _handler = current.InnerHandler; // Wireup outer handler to current's inner handler if (current.OuterHandler != null) current.OuterHandler.InnerHandler = current.InnerHandler; // Wireup inner handler to current's outer handler if (current.InnerHandler != null) current.InnerHandler.OuterHandler = current.OuterHandler; // Cleanup current current.InnerHandler = null; current.OuterHandler = null; return; } previous = current; current = current.InnerHandler; } throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "Cannot find a handler of type {0}", type.Name)); } /// /// Replaces the first occurance of a handler of type T with the given handler. /// /// Type of the handler which will be replaced. /// The handler to be added to the pipeline. public void ReplaceHandler(IPipelineHandler handler) where T : IPipelineHandler { if (handler == null) throw new ArgumentNullException("handler"); ThrowIfDisposed(); var type = typeof(T); IPipelineHandler previous = null; var current = _handler; while (current != null) { if (current.GetType() == type) { // Replace current with handler. handler.InnerHandler = current.InnerHandler; handler.OuterHandler = current.OuterHandler; if(previous != null) { // Wireup previous handler previous.InnerHandler = handler; } else { // Current is the top, replace it. _handler = handler; } if (current.InnerHandler != null) { // Wireup next handler current.InnerHandler.OuterHandler = handler; } // Cleanup current current.InnerHandler = null; current.OuterHandler = null; SetHandlerProperties(handler); return; } previous = current; current = current.InnerHandler; } throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "Cannot find a handler of type {0}", type.Name)); } /// /// Inserts the given handler after current handler in the pipeline. /// /// Handler to be inserted in the pipeline. /// Handler after which the given handler is inserted. private static void InsertHandler(IPipelineHandler handler, IPipelineHandler current) { var next = current.InnerHandler; current.InnerHandler = handler; handler.OuterHandler = current; if (next!=null) { var innerMostHandler = GetInnermostHandler(handler); innerMostHandler.InnerHandler = next; next.OuterHandler = innerMostHandler; } } /// /// Gets the innermost handler by traversing the inner handler till /// it reaches the last one. /// private static IPipelineHandler GetInnermostHandler(IPipelineHandler handler) { var current = handler; while (current.InnerHandler != null) { current = current.InnerHandler; } return current; } private void SetHandlerProperties(IPipelineHandler handler) { ThrowIfDisposed(); handler.Logger = _logger; } /// /// Retrieves a list of handlers, in the order of their execution. /// /// Handlers in the current pipeline. public List Handlers { get { return EnumerateHandlers().ToList(); } } /// /// Retrieves current handlers, in the order of their execution. /// /// Handlers in the current pipeline. public IEnumerable EnumerateHandlers() { var handler = this.Handler; while(handler != null) { yield return handler; handler = handler.InnerHandler; } } #endregion #region Dispose methods public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { var handler = this.Handler; while (handler != null) { var innerHandler = handler.InnerHandler; var disposable = handler as IDisposable; if (disposable != null) { disposable.Dispose(); } handler = innerHandler; } _disposed = true; } } private void ThrowIfDisposed() { if (this._disposed) throw new ObjectDisposedException(GetType().FullName); } #endregion } }