Ejemplo n.º 1
0
        /// <summary>
        /// Checks whether the provided exception message is the end message or an exception message for the awaited <see cref="SendAndAwait"/> operation.
        /// </summary>
        /// <param name="message">The message to check.</param>
        /// <returns><c>true</c>, if the message was the end message or an exception message for the <see cref="SendAndAwait"/> operation.</returns>
        /// <remarks>For an example how to use this class see the type documentation.</remarks>
        public bool Check(Message message)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (!IsActive ||
                !(message.Is <TEnd>() ||
                  message.Is <ExceptionMessage>()))
            {
                return(false);
            }

            MessageDomain domain = message.MessageDomain;

            while (domain?.IsTerminated == true)
            {
                domain = domain.Parent;
            }

            if (domain != null &&
                continueActions.TryGetValue(domain, out OneTimeAction action))
            {
                action.Execute(message);
                return(true);
            }
            return(false);
        }
Ejemplo n.º 2
0
        public static MessageDomain GetMessageDomain(this Message[] domainSourceMessages)
        {
            if (domainSourceMessages.Length == 0)
            {
                return(MessageDomain.DefaultMessageDomain);
            }

            if (!domainSourceMessages[0].MessageDomain.IsTerminated &&
                (domainSourceMessages.Length == 1 ||
                 domainSourceMessages.All(m => m.MessageDomain == domainSourceMessages[0].MessageDomain)))
            {
                return(domainSourceMessages[0].MessageDomain);
            }

            HashSet <MessageDomain> remainingDomains = new HashSet <MessageDomain>(GetDomainsFromMessages());
            List <MessageDomain>    visitedDomains   = new List <MessageDomain>();

            while (remainingDomains.Except(visitedDomains).Any())
            {
                MessageDomain current = remainingDomains.Except(visitedDomains).First();
                visitedDomains.Add(current);
                IEnumerable <MessageDomain> parentTree = FlattenParents(current);
                remainingDomains.ExceptWith(parentTree);
            }

            if (remainingDomains.Count != 1)
            {
                throw new InvalidOperationException("Cannot determine message domain from sibling domains.");
            }

            return(remainingDomains.Single());

            IEnumerable <MessageDomain> FlattenParents(MessageDomain current)
            {
                MessageDomain parentDomain = current.Parent;

                while (parentDomain != null)
                {
                    yield return(parentDomain);

                    parentDomain = parentDomain.Parent;
                }
            }

            IEnumerable <MessageDomain> GetDomainsFromMessages()
            {
                foreach (MessageDomain messageDomain in domainSourceMessages.Select(m => m.MessageDomain))
                {
                    MessageDomain current = messageDomain;
                    while (current?.IsTerminated == true)
                    {
                        current = messageDomain.Parent;
                    }

                    yield return(current ?? MessageDomain.DefaultMessageDomain);
                }
            }
        }
Ejemplo n.º 3
0
        private static IEnumerable <MessageDomain> ThisAndFlattenedChildren(MessageDomain messageDomain)
        {
            yield return(messageDomain);

            foreach (MessageDomain child in messageDomain.Children.SelectMany(ThisAndFlattenedChildren))
            {
                yield return(child);
            }
        }
Ejemplo n.º 4
0
 internal void SwitchDomain(MessageDomain newDomain)
 {
     if (this != HeadMessage)
     {
         HeadMessage.SwitchDomain(newDomain);
     }
     MessageDomain = newDomain;
     foreach (Message descendant in Descendants)
     {
         descendant.MessageDomain = newDomain;
     }
 }
Ejemplo n.º 5
0
        /// <summary>
        /// Overridden by inheriting classes to see if there is a completed set for the specified domain.
        /// </summary>
        /// <param name="domain">The domain which should be completed.</param>
        /// <param name="messageCollection">The message collection with the complete set.</param>
        /// <returns><c>true</c> if there is a completed set; otherwise <c>false</c></returns>
        protected virtual bool IsCompleted(MessageDomain domain, out MessageCollection messageCollection)
        {
            if (TryGetMessageFittingDomain(domain, Messages1, out MessageStore <T1> message1) &&
                TryGetMessageFittingDomain(domain, Messages2, out MessageStore <T2> message2))
            {
                messageCollection = new MessageCollection <T1, T2>(message1, message2, this);
                return(true);
            }

            messageCollection = null;
            return(false);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Adds the message to this instance.
        /// </summary>
        /// <param name="message">The message to add to this instance.</param>
        /// <exception cref="ArgumentNullException">Thrown if the message is <c>null</c>.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the <paramref name="message"/> is does not contain the type <typeparamref name="T"/>.</exception>
        public void Aggregate(Message message)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (!message.TryGet(out T aggregatedMessage))
            {
                throw new InvalidOperationException($"Cannot aggregate the message {message}. Aggregated type is {typeof(T)}");
            }

            IReadOnlyCollection <Message> root = aggregatedMessage.MessageDomain.SiblingDomainRootMessages;

            if (aggregatedMessage.MessageDomain.IsTerminated)
            {
                return;
            }

            HashSet <MessageStore <T> > completedMessageBatch = null;

            lock (dictionaryLock)
            {
                if (!aggregatedMessages.ContainsKey(root))
                {
                    aggregatedMessages.Add(root, new HashSet <MessageStore <T> >());
                }
                aggregatedMessages[root].Add(aggregatedMessage);
                if (aggregatedMessages[root].Count == root.Count)
                {
                    completedMessageBatch = aggregatedMessages[root];
                    aggregatedMessages.Remove(root);
                }
            }

            if (completedMessageBatch != null)
            {
                T[] messages = completedMessageBatch.Select <MessageStore <T>, T>(m => m).ToArray();
                if (autoTerminate)
                {
                    MessageDomain.TerminateDomainsOf(messages);
                }
                onAggregated(messages);
                foreach (MessageStore <T> messageStore in completedMessageBatch)
                {
                    messageStore.Dispose();
                }
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Overridden by inheriting classes to get all sets of message for a specific domain without specific type.
        /// </summary>
        /// <param name="domain">The domain for which sets should be found.</param>
        /// <returns>An enumeration of all completed sets for the domain.</returns>
        /// <exception cref="ArgumentNullException">If the domain is null.</exception>
        protected IEnumerable <MessageCollection> GetCompleteSets(MessageDomain domain)
        {
            if (domain == null)
            {
                throw new ArgumentNullException(nameof(domain));
            }
            HashSet <MessageCollection> sets = new HashSet <MessageCollection>(new MessageSetIdComparer());

            foreach (MessageDomain messageDomain in ThisAndFlattenedChildren(domain).Where(d => !d.IsTerminated))
            {
                if (IsCompleted(messageDomain, out MessageCollection set))
                {
                    sets.Add(set);
                }
            }

            return(sets);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Used to get a message from the message dictionaries.
        /// </summary>
        /// <param name="domain">The message domain to get the message for.</param>
        /// <param name="messagePool">The dictionary for the specific message type.</param>
        /// <param name="message">The message for the domain.</param>
        /// <typeparam name="T">The type of the message.</typeparam>
        /// <returns><c>true</c> if the dictionary contains a message for the specific domain; otherwise <c>false</c>.</returns>
        /// <exception cref="ArgumentNullException">If the dictionary is null.</exception>
        protected bool TryGetMessageFittingDomain <T>(MessageDomain domain, ConcurrentDictionary <MessageDomain, MessageStore <T> > messagePool, out MessageStore <T> message)
            where T : Message
        {
            if (messagePool == null)
            {
                throw new ArgumentNullException(nameof(messagePool));
            }
            MessageDomain current = domain;

            while (current != null)
            {
                if (messagePool.TryGetValue(current, out message))
                {
                    return(true);
                }
                current = current.Parent;
            }

            message = null;
            return(false);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// The method to send a start message and wait for the end message.
        /// </summary>
        /// <param name="startMessage">The start message.</param>
        /// <param name="onMessage">The action to send the message.</param>
        /// <param name="continueAction">The action to execute once a <see cref="MessageGateResult{TEnd}"/> was created.</param>
        /// <param name="timeout">
        /// Optionally a timeout after which the method will return, without sending the result.
        /// By default the timeout is <see cref="NoTimout"/>
        /// </param>
        /// <param name="cancellationToken">
        /// Optionally a cancellation token to cancel the continue operation. By default no CancellationToken will be used.
        /// </param>
        /// <remarks>
        /// <para>For an example how to use this class see the type documentation.</para>
        /// </remarks>
        public void SendAndContinue(TStart startMessage, Action <Message> onMessage, Action <MessageGateResult <TEnd> > continueAction, int timeout = NoTimout,
                                    CancellationToken cancellationToken = default)
        {
            if (startMessage == null)
            {
                throw new ArgumentNullException(nameof(startMessage));
            }

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

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

            CancellationToken userCancelToken      = cancellationToken;
            CancellationToken timeoutCancelToken   = default;
            List <CancellationTokenSource> sources = new List <CancellationTokenSource>();

            if (timeout != NoTimout)
            {
                CancellationTokenSource timeoutSource = new CancellationTokenSource(timeout);
                sources.Add(timeoutSource);
                timeoutCancelToken = timeoutSource.Token;
            }
            CancellationTokenSource combinedSource = CancellationTokenSource.CreateLinkedTokenSource(userCancelToken, timeoutCancelToken);

            cancellationToken = combinedSource.Token;
            sources.Add(combinedSource);

            MessageDomain.CreateNewDomainsFor(startMessage);
            CancellationTokenRegistration register = default;
            OneTimeAction action = new OneTimeAction(OnReceived);

            continueActions.TryAdd(startMessage.MessageDomain, action);
            register = cancellationToken.Register(OnCancel);

            onMessage(startMessage);

            void Dispose()
            {
                foreach (CancellationTokenSource tokenSource in sources)
                {
                    tokenSource.Dispose();
                }

                register.Dispose();
            }

            void OnReceived(Message message)
            {
                if (!continueActions.TryRemove(startMessage.MessageDomain, out _))
                {
                    return;
                }

                MessageDomain.TerminateDomainsOf(startMessage);
                if (message.TryGet(out TEnd endMessage))
                {
                    continueAction(new MessageGateResult <TEnd>(MessageGateResultKind.Success, endMessage,
                                                                Enumerable.Empty <ExceptionMessage>()));
                }
                else
                {
                    ExceptionMessage exceptionMessage = message.Get <ExceptionMessage>();
                    continueAction(
                        new MessageGateResult <TEnd>(MessageGateResultKind.Exception, null, new[] { exceptionMessage }));
                }

                Dispose();
            }

            void OnCancel()
            {
                if (!continueActions.TryRemove(startMessage.MessageDomain, out _))
                {
                    return;
                }

                MessageDomain.TerminateDomainsOf(startMessage);
                MessageGateResultKind resultKind = MessageGateResultKind.Success;

                if (userCancelToken.IsCancellationRequested)
                {
                    resultKind = MessageGateResultKind.Canceled;
                }
                else if (timeoutCancelToken.IsCancellationRequested)
                {
                    resultKind = MessageGateResultKind.Timeout;
                }

                MessageGateResult <TEnd> result =
                    new MessageGateResult <TEnd>(resultKind, null, Enumerable.Empty <ExceptionMessage>());

                continueAction(result);
                Dispose();
            }
        }