/// <summary>
        /// Prepares and sets up the observables and subjects used, particularly
        /// <see cref="ListChanges"/>, <see cref="INotifyObservableCountChanges.CountChanges"/> and <see cref="INotifyObserverExceptions.ObserverExceptions"/>.
        /// </summary>
        private void SetupObservablesAndObserversAndSubjects()
        {
            ListChangesObserver = _listChangesSubject.NotifyOn(Scheduler);

            // then connect to InnerList's ListChanged Event
            _innerListChangedRelevantListChangedEventsForwader = System.Reactive.Linq.Observable.FromEventPattern <ListChangedEventHandler, ListChangedEventArgs>(
                handler => InnerList.ListChanged += handler,
                handler => InnerList.ListChanged -= handler)
                                                                 .TakeWhile(_ => !IsDisposing && !IsDisposed)
                                                                 .SkipContinuouslyWhile(_ => !IsTrackingChanges)
                                                                 .Where(eventPattern => eventPattern?.EventArgs != null)
                                                                 .Select(eventPattern => eventPattern.EventArgs.ToObservableListChange(InnerList, this))
                                                                 .ObserveOn(Scheduler)
                                                                 .Subscribe(
                NotifySubscribersAboutListChanges,
                exception =>
            {
                // ToDo: at this point this instance is practically doomed / no longer forwarding any events & therefore further usage of the instance itself should be prevented, or the observable stream should re-connect/signal-and-swallow exceptions. Either way.. not ideal.
                var observerException = new ObserverException(
                    $"An error occured notifying observers of this {this.GetType().Name} - consistency and future notifications are no longer guaranteed.",
                    exception);

                ObserverExceptionsObserver.OnNext(observerException);
            });
        }
Example #2
0
        private void OnNext(ProtoMessage protoMessage)
        {
            foreach (var(_, observer) in _observers)
            {
                try
                {
                    var message = MessageFactory.GetMessage(protoMessage);

                    if (protoMessage.HasClientMsgId || message == null)
                    {
                        observer.OnNext(protoMessage);
                    }

                    if (message != null)
                    {
                        observer.OnNext(message);
                    }
                }
                catch (Exception ex)
                {
                    var observerException = new ObserverException(ex, observer);

                    OnError(observerException);
                }
            }
        }
        public void ValidateConstructor()
        {
            Exception         exception = new Exception("randomMessage");
            ObserverException ex        = new ObserverException(exception);

            Assert.AreEqual(exception.Message, ex.InnerException.Message);
            Assert.AreEqual(exception, ex.InnerException);
        }
        public void ValidateSerialization_NullFields()
        {
            var originalException = new ObserverException(null);
            var buffer            = new byte[4096];
            var formatter         = new BinaryFormatter();
            var stream1           = new MemoryStream(buffer);
            var stream2           = new MemoryStream(buffer);

            formatter.Serialize(stream1, originalException);
            var deserializedException = (ObserverException)formatter.Deserialize(stream2);

            Assert.Equal(originalException.Message, deserializedException.Message);
            Assert.Null(deserializedException.InnerException);
        }
        public void ValidateSerialization_AllFields()
        {
            Exception         exception         = new Exception("randomMessage");
            ObserverException originalException = new ObserverException(exception);

            byte[]          buffer    = new byte[4096];
            BinaryFormatter formatter = new BinaryFormatter();
            MemoryStream    stream1   = new MemoryStream(buffer);
            MemoryStream    stream2   = new MemoryStream(buffer);

            formatter.Serialize(stream1, originalException);
            ObserverException deserializedException = (ObserverException)formatter.Deserialize(stream2);

            Assert.AreEqual(originalException.Message, deserializedException.Message);
            Assert.AreEqual(originalException.InnerException.Message, deserializedException.InnerException.Message);
        }
        public async Task ThrowsOnFailedCustomSerializer()
        {
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(1000);

            Mock <ChangeFeedObserver <MyDocument> > mockObserver     = new Mock <ChangeFeedObserver <MyDocument> >();
            Mock <PartitionCheckpointer>            mockCheckpointer = new Mock <PartitionCheckpointer>();
            Mock <FeedIterator> mockIterator = new Mock <FeedIterator>();

            mockIterator.Setup(i => i.FetchNextSetAsync(It.IsAny <CancellationToken>())).ReturnsAsync(GetResponse(HttpStatusCode.OK, true));
            mockIterator.SetupSequence(i => i.HasMoreResults).Returns(true).Returns(false);

            CustomSerializerFails          serializer = new CustomSerializerFails();
            FeedProcessorCore <MyDocument> processor  = new FeedProcessorCore <MyDocument>(mockObserver.Object, mockIterator.Object, FeedProcessorCoreTests.DefaultSettings, mockCheckpointer.Object, serializer);

            ObserverException caughtException = await Assert.ThrowsExceptionAsync <ObserverException>(() => processor.RunAsync(cancellationTokenSource.Token));

            Assert.IsInstanceOfType(caughtException.InnerException, typeof(CustomException));
        }
Example #7
0
        public async Task ThrowsOnFailedCustomSerializer()
        {
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(1000);

            Mock <PartitionCheckpointer> mockCheckpointer = new Mock <PartitionCheckpointer>();
            Mock <FeedIterator>          mockIterator     = new Mock <FeedIterator>();

            mockIterator.Setup(i => i.ReadNextAsync(It.IsAny <CancellationToken>())).ReturnsAsync(GetResponse(HttpStatusCode.OK, true));
            mockIterator.SetupSequence(i => i.HasMoreResults).Returns(true).Returns(false);

            CustomSerializerFails    serializer             = new CustomSerializerFails();
            ChangesHandler <dynamic> handler                = (changes, cancelationToken) => Task.CompletedTask;
            ChangeFeedObserverFactoryCore <dynamic> factory = new ChangeFeedObserverFactoryCore <dynamic>(handler, new CosmosSerializerCore(serializer));
            FeedProcessorCore processor = new FeedProcessorCore(factory.CreateObserver(), mockIterator.Object, FeedProcessorCoreTests.DefaultSettings, mockCheckpointer.Object);

            ObserverException caughtException = await Assert.ThrowsExceptionAsync <ObserverException>(() => processor.RunAsync(cancellationTokenSource.Token));

            Assert.IsInstanceOfType(caughtException.InnerException, typeof(CustomException));
        }
Example #8
0
        /// <summary>
        /// Prepares and sets up the observables and subjects used, particularly
        /// <see cref="_collectionChangesSubject"/>, <see cref="_countChangesSubject"/> and <see cref="_observerExceptionsSubject"/>
        /// but also internally used RX subscriptions for <see cref="IBindingList.ListChanged"/> and somewhat hack-ish
        /// 'Count' and 'Items[]' <see cref="INotifyPropertyChanged"/> events on <see cref="CountChanges"/> and <see cref="CollectionChanges"/>
        /// occurrences (for WPF / Binding)
        /// </summary>
        private void SetupObservablesAndObserversAndSubjects()
        {
            ObserverExceptionsObserver = _observerExceptionsSubject.NotifyOn(Scheduler);
            CollectionChangesObserver  = _collectionChangesSubject.NotifyOn(Scheduler);
            CountChangesObserver       = _countChangesSubject.NotifyOn(Scheduler);

            // then connect to InnerList's ListChanged Event
            _innerListChangedRelevantCollectionChangedEventsForwader = System.Reactive.Linq.Observable.FromEventPattern <ListChangedEventHandler, ListChangedEventArgs>(
                handler => InnerList.ListChanged += handler,
                handler => InnerList.ListChanged -= handler)
                                                                       .TakeWhile(_ => !IsDisposing && !IsDisposed)
                                                                       .SkipContinuouslyWhile(_ => !IsTrackingChanges)
                                                                       .Where(eventPattern => eventPattern?.EventArgs != null)
                                                                       .SelectMany(eventPattern => eventPattern.EventArgs.ToObservableCollectionChanges(InnerList))
                                                                       .ObserveOn(Scheduler)
                                                                       .Subscribe(
                NotifyObserversAboutCollectionChanges,
                exception =>
            {
                // ToDo: at this point this instance is practically doomed / no longer forwarding any events & therefore further usage of the instance itself should be prevented, or the observable stream should re-connect/signal-and-swallow exceptions. Either way.. not ideal.
                var observerException = new ObserverException(
                    $"An error occured notifying observers of this {this.GetType().Name} - consistency and future notifications are no longer guaranteed.",
                    exception);
                ObserverExceptionsObserver.OnNext(observerException);
            });


            // 'Count' and 'Item[]' PropertyChanged events are used by WPF typically via / for ObservableCollections, see
            // http://referencesource.microsoft.com/#System/compmod/system/collections/objectmodel/observablecollection.cs,421
            _countChangesCountPropertyChangedForwarder = CountChanges
                                                         .ObserveOn(Scheduler)
                                                         .Subscribe(_ => RaisePropertyChanged(nameof(Count)));

            _collectionChangesItemIndexerPropertyChangedForwarder = CollectionChanges
                                                                    .ObserveOn(Scheduler)
                                                                    .Subscribe(_ => RaisePropertyChanged(ItemIndexerName));
        }
Example #9
0
        /// <summary>
        ///     Notifies all <see cref="CollectionChanges" /> and <see cref="Resets" /> subscribers and
        ///     raises the (observable)collection changed events.
        /// </summary>
        /// <param name="observableCollectionChange">The observable collection change.</param>
        protected virtual void NotifyObserversAboutCollectionChanges(IObservableCollectionChange <T> observableCollectionChange)
        {
            if (observableCollectionChange == null)
            {
                throw new ArgumentNullException(nameof(observableCollectionChange));
            }

            CheckForAndThrowIfDisposed();

            // go ahead and check whether a Reset or item add, -change, -move or -remove shall be signaled
            // .. based on the ThresholdAmountWhenChangesAreNotifiedAsReset value
            var actualObservableCollectionChange =
                (observableCollectionChange.ChangeType == ObservableCollectionChangeType.Reset ||
                 IsItemsChangedAmountGreaterThanResetThreshold(1, ThresholdAmountWhenChangesAreNotifiedAsReset))
                    ? ObservableCollectionChange <T> .Reset
                    : observableCollectionChange;

            // raise events and notify about collection changes

            if (actualObservableCollectionChange.ChangeType == ObservableCollectionChangeType.ItemAdded ||
                actualObservableCollectionChange.ChangeType == ObservableCollectionChangeType.ItemRemoved ||
                actualObservableCollectionChange.ChangeType == ObservableCollectionChangeType.Reset)
            {
                try
                {
                    CountChangesObserver.OnNext(Count);
                }
                catch (Exception exception)
                {
                    var observerException = new ObserverException(
                        $"An error occured notifying {nameof(CountChanges)} Observers of this {this.GetType().Name}.",
                        exception);

                    ObserverExceptionsObserver.OnNext(observerException);

                    if (observerException.Handled == false)
                    {
                        throw;
                    }
                }
            }

            try
            {
                CollectionChangesObserver.OnNext(actualObservableCollectionChange);
            }
            catch (Exception exception)
            {
                var observerException = new ObserverException(
                    $"An error occured notifying {nameof(CollectionChanges)} Observers of this {this.GetType().Name}.",
                    exception);

                ObserverExceptionsObserver.OnNext(observerException);

                if (observerException.Handled == false)
                {
                    throw;
                }
            }

            try
            {
                RaiseCollectionChanged(actualObservableCollectionChange.ToNotifyCollectionChangedEventArgs());
            }
            catch (Exception exception)
            {
                var observerException = new ObserverException(
                    $"An error occured notifying CollectionChanged Subscribers of this {this.GetType().Name}.",
                    exception);

                ObserverExceptionsObserver.OnNext(observerException);

                if (observerException.Handled == false)
                {
                    throw;
                }
            }
        }
        /// <summary>
        /// Stops an ongoing expiration timer (if any) and uses the <paramref name="expirationScheduler" /> to schedule a new expiration
        /// notification on the given <paramref name="expirationObserver" /> after the given <paramref name="expiry" />.
        /// </summary>
        /// <param name="expiry">The expiry.</param>
        /// <param name="expirationObserver">The expiration observer to notify upon this instance's expiration.</param>
        /// <param name="observerExceptionsObserver">The <see cref="ObserverException"/> observer to notify, well.. observer exceptions on.</param>
        /// <param name="expirationScheduler">The expiration scheduler to schedule the expiration notification on.</param>
        /// <param name="isUpdate">if set to <c>true</c> indicats that this is an update, rather than an initial call (via .ctor).</param>
        private void CreateOrUpdateExpiration(
            TimeSpan expiry,
            IObserver <ObservableCachedElement <TKey, TValue> > expirationObserver,
            IObserver <ObserverException> observerExceptionsObserver,
            IScheduler expirationScheduler,
            bool isUpdate)
        {
            if (expirationObserver == null)
            {
                throw new ArgumentNullException(nameof(expirationObserver));
            }
            if (observerExceptionsObserver == null)
            {
                throw new ArgumentNullException(nameof(observerExceptionsObserver));
            }
            if (expirationScheduler == null)
            {
                throw new ArgumentNullException(nameof(expirationScheduler));
            }

            if (expiry < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(expiry), $"{nameof(expiry)} cannot be negative");
            }

            CheckForAndThrowIfDisposed();

            lock (_expiryModificationLocker)
            {
                // cancel an existing, scheduled expiration
                try
                {
                    _expirySchedulerCancellationDisposable?.Dispose();
                    _expirySchedulerCancellationDisposable = null;
                }
                catch (Exception exception)
                {
                    var observerException = new ObserverException($"An error occured cancelling a scheduled expiration of a {this.GetType().Name} instance.", exception);

                    observerExceptionsObserver.OnNext(observerException);

                    if (observerException.Handled == false)
                    {
                        throw;
                    }
                }

                try
                {
                    ExpirationScheduler = expirationScheduler;

                    // set 'new' expiry datetime
                    ExpiryDateTime = CalculateExpiryDateTime(expiry);
                    OriginalExpiry = expiry;

                    if (isUpdate)
                    {
                        // and...
                        if (HasExpired)
                        {
                            // reset hasexpired flag
                            HasExpired = false;

                            // and re-add to value / property changed handling and forwarding
                            AddKeyAndValueToPropertyChangedHandling();
                        }
                    }
                    else
                    {
                        // otherwise, if this is basically the first call to this method & thereby initial start
                        // the value needs to be added to value / property changed handling and forwarding, too
                        AddKeyAndValueToPropertyChangedHandling();
                    }

                    // and finally schedule expiration on scheduler for given time
                    // IF TimeSpan.MaxValue hasn't been specified
                    if (expiry < TimeSpan.MaxValue)
                    {
                        _expirySchedulerCancellationDisposable = ExpirationScheduler.Schedule(expiry,
                                                                                              () =>
                        {
                            try
                            {
                                lock (_expiryModificationLocker)
                                {
                                    RemoveKeyAndValueFromPropertyChangedHandling();
                                    HasExpired = true;
                                }

                                expirationObserver.OnNext(this);
                            }
                            catch (Exception exception)
                            {
                                var observerException = new ObserverException($"An error occured notifying about the expiration of a {this.GetType().Name} instance.", exception);

                                observerExceptionsObserver.OnNext(observerException);

                                if (observerException.Handled == false)
                                {
                                    throw;
                                }
                            }
                        });
                    }
                }
                catch (Exception exception)
                {
                    var observerException = new ObserverException($"An error occured updating expiration data of a {this.GetType().Name} instance.", exception);

                    observerExceptionsObserver.OnNext(observerException);

                    if (observerException.Handled == false)
                    {
                        throw;
                    }
                }
                finally
                {
                    Interlocked.Increment(ref _expirationChangesCount);
                }
            }
        }
        /// <summary>
        ///     Notifies all <see cref="ListChanges" /> and <see cref="INotifyObservableResets.Resets" /> subscribers and
        ///     raises the (observable)collection changed events.
        /// </summary>
        /// <param name="observableListChange">The observable list change.</param>
        protected virtual void NotifySubscribersAboutListChanges(IObservableListChange <T> observableListChange)
        {
            // This is similar to what ObservableCollection implements via its NotifyObserversAboutCollectionChanges method,
            // however:
            // - no need to handle count-relevant changes because the underlying ObservableCollection takes care of this
            // - no (extra) (Raise)CollectionChanged call here, again.. already done by the ObservableCollection
            // - however as 'Move's are only possible for / with ObservableLists, we also raise a PropertyChangedEvent for 'Item[]' (for wpf) in case of a item move(s)

            if (observableListChange == null)
            {
                throw new ArgumentNullException(nameof(observableListChange));
            }

            CheckForAndThrowIfDisposed();

            // go ahead and check whether a Reset or item add, -change, -move or -remove shall be signaled
            // .. based on the ThresholdAmountWhenChangesAreNotifiedAsReset value
            var actualObservableListChange =
                (observableListChange.ChangeType == ObservableListChangeType.Reset ||
                 IsItemsChangedAmountGreaterThanResetThreshold(1, ThresholdAmountWhenChangesAreNotifiedAsReset))
                    ? ObservableListChange <T> .Reset(this)
                    : observableListChange;

            // raise events and notify about list changes
            try
            {
                ListChangesObserver.OnNext(actualObservableListChange);
            }
            catch (Exception exception)
            {
                var observerException = new ObserverException(
                    $"An error occured notifying {nameof(ListChanges)} observers of this {this.GetType().Name}.",
                    exception);

                ObserverExceptionsObserver.OnNext(observerException);

                if (observerException.Handled == false)
                {
                    throw;
                }
            }

            if (actualObservableListChange.ChangeType == ObservableListChangeType.ItemMoved)
            {
                try
                {
                    RaisePropertyChanged(ItemIndexerName);
                }
                catch (Exception exception)
                {
                    var observerException = new ObserverException(
                        $"An error occured notifying {nameof(PropertyChanged)} subscribers of this {this.GetType().Name}.",
                        exception);

                    ObserverExceptionsObserver.OnNext(observerException);

                    if (observerException.Handled == false)
                    {
                        throw;
                    }
                }
            }
        }