// /*******************************************************************************
// * Copyright 2008-2018 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.
// * *****************************************************************************
// * __ _ _ ___
// * ( )( \/\/ )/ __)
// * /__\ \ / \__ \
// * (_)(_) \/\/ (___/
// *
// * AWS SDK for .NET
// *
// */
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Amazon.Runtime.EventStreams;
using Amazon.Runtime.EventStreams.Internal;
namespace Amazon.S3.Model
{
///
/// The contract for the SelectObjectContentEventStream.
///
[SuppressMessage("Microsoft.Naming", "CA1710", Justification = "ISelectObjectContentEventStreamCollection is not descriptive.")]
public interface ISelectObjectContentEventStream : IEnumerableEventStream
{
///
/// Event that encompasses all IS3Events.
///
new event EventHandler> EventReceived;
///
/// Event that encompasses S3EventStreamExceptions.
///
new event EventHandler> ExceptionReceived;
///
/// Reaised when a Records event is received.
///
event EventHandler> RecordsEventReceived;
///
/// Reaised when a Stats event is received.
///
event EventHandler> StatsEventReceived;
///
/// Reaised when a Progress event is received.
///
event EventHandler> ProgressEventReceived;
///
/// Reaised when a Continuation event is received.
///
event EventHandler> ContinuationEventReceived;
///
/// Reaised when an End event is received.
///
event EventHandler> EndEventReceived;
}
///
/// A Stream of Events returned by the SelectObjectContent operation. Events can be retrieved from this stream by either
///
/// - attaching handlers to listen events, and then call StartProcessing or
/// - enumerating over the events.
///
///
/// These options should be treaded as mutually exclusive.
///
[SuppressMessage("Microsoft.Naming", "CA1710", Justification = "SelectObjectContentEventStreamCollection is not descriptive.")]
[SuppressMessage("Microsoft.Design", "CA1063", Justification = "IDisposable is a transient interface from IEventStream. Users need to be able to call Dispose.")]
public sealed class SelectObjectContentEventStream : EnumerableEventStream,
ISelectObjectContentEventStream
{
///
/// The mapping of event message to a generator function to construct the matching Event Stream event.
///
protected override IDictionary> EventMapping { get; } =
new Dictionary>
{
{UnknownEventKey, payload => new UnknownEventStreamEvent(payload)},
{"Records", payload => new RecordsEvent(payload)},
{"Stats", payload => new StatsEvent(payload)},
{"Progress", payload => new ProgressEvent(payload)},
{"Cont", payload => new ContinuationEvent(payload)},
{"End", payload => new EndEvent(payload)}
};
///
/// The mapping of event message to a generator function to construct the matching Event Stream exception.
///
protected override IDictionary> ExceptionMapping { get;} =
new Dictionary>
{
};
// Backing by a volatile bool. The flag only changes one way, so no need for a lock.
// This is located in the subclass to be CLS compliant.
private volatile bool _isProcessing;
///
/// Whether the backround processing loop is running.
///
protected override bool IsProcessing
{
get { return _isProcessing; }
set { _isProcessing = value; }
}
///
/// Event that encompasses all IS3Events.
///
public override event EventHandler> EventReceived;
///
/// Event that encompasses S3EventStreamExceptions.
///
public override event EventHandler> ExceptionReceived;
///
/// Reaised when a Records event is received.
///
public event EventHandler> RecordsEventReceived;
///
/// Reaised when a Stats event is received.
///
public event EventHandler> StatsEventReceived;
///
/// Reaised when a Progress event is received.
///
public event EventHandler> ProgressEventReceived;
///
/// Reaised when a Continuation event is received.
///
public event EventHandler> ContinuationEventReceived;
///
/// Reaised when an End event is received.
///
public event EventHandler> EndEventReceived;
///
/// Event Stream returned by SelectObjectContentStream.
///
/// Events can be retrieved from this stream by either
///
/// - attaching handlers to listen events, and then call StartProcessing or
/// - enumerating over the events.
///
///
/// These options should be treated as mutually exclusive.
///
/// The network stream which events will be parsed from.
public SelectObjectContentEventStream(Stream selectObjectStream) : this(selectObjectStream, null)
{
}
///
/// Event Stream returned by SelectObjectContentStream.
///
/// Events can be retrieved from this stream by either
///
/// - attaching handlers to listen events, and then call StartProcessing or
/// - enumerating over the events.
///
///
/// These options should be treaded as mutually exclusive.
///
/// The network stream which events will be parsed from.
/// The decoder responsible for parsing events.
public SelectObjectContentEventStream(Stream selectObjectStream, IEventStreamDecoder eventStreamDecoder)
: base(selectObjectStream, eventStreamDecoder)
{
// C# doesn't do event inheritance right. They are effectively treated as new, but not quite.
//
// In this case, EventStream has an event handler reference, and SelectObjectContentEventStream has an event handler reference (like new).
// But it also overrides the accessors, so references will be polymorphically directed (like override).
//
// So when we attach event handlers, they are attached to the SelectObjectContentEventStream.
// But when the EventStream code raises an event, those are raised on the EventStream handler reference.
//
// As a result, we get no events!
//
// Here, we are attaching a handler to raise our event when a subclass event/exception is raised.
// Currently, all 'events' are raised in the subclass, so this is only needed for exceptions. Stubbed for generator use.
base.EventReceived += (sender, args) => EventReceived?.Invoke(this, args);
base.ExceptionReceived += (sender, args) => ExceptionReceived?.Invoke(this, args);
// Mapping the generic event to more specific events.
Decoder.MessageReceived += (sender, args) =>
{
IS3Event ev;
try
{
ev = ConvertMessageToEvent(args.Message);
}
catch (UnknownEventStreamMessageTypeException)
{
// Silence to ensure backwards-compatability with future EventStream specification.
return;
}
EventReceived?.Invoke(this, new EventStreamEventReceivedArgs(ev));
// Call RaiseEvent until it returns true or all calls complete. This way, only a subset of casts is preformed,
// and we can avoid a cascade of nested if/else statements. The result is thrown away.
var _ = RaiseEvent(RecordsEventReceived, ev) ||
RaiseEvent(StatsEventReceived, ev) ||
RaiseEvent(ProgressEventReceived, ev) ||
RaiseEvent(ContinuationEventReceived, ev) ||
RaiseEvent(EndEventReceived, ev);
};
}
private bool RaiseEvent(EventHandler> eventHandler, IS3Event ev) where T : class, IS3Event
{
var convertedEvent = ev as T;
if (convertedEvent != null)
{
eventHandler?.Invoke(this, new EventStreamEventReceivedArgs(convertedEvent));
return true;
}
return false;
}
}
}