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