/*
* 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
}
}