protected override void ExecuteBeforeMethodInvocation(IMethodInvocation input)
        {
            MethodInvocationData methodInvocationData = input.GetMethodInvocationData();
            string preMethodMessage = string.Format("{0}{1}.{2}({3})", methodInvocationData.ClassName, methodInvocationData.Generic, methodInvocationData.MethodName, methodInvocationData.Arguments);

            _logger.LogMessage(preMethodMessage);
        }
        protected override void ExecuteAfterMethodInvocation(IMethodInvocation input, IMethodReturn methodReturn)
        {
            MethodInvocationData methodInvocationData = input.GetMethodInvocationData();
            string postMethodMessage = string.Format("{0}{1}.{2}({3})", methodInvocationData.ClassName, methodInvocationData.Generic, methodInvocationData.MethodName, methodReturn.ReturnValue);

            _logger.LogMessage(postMethodMessage);
        }
示例#3
0
        public async Task PublishAsync(EventPublishData data, PublishPreferences preferences)
        {
            var eventDesc = new EventDescriptor {
                Service = data.Service, Event = data.Event
            };
            var subscribers = _eventSubscriber.GetSubscribers(eventDesc).ToList();

            if (subscribers.Count == 0)
            {
                return;
            }

            foreach (var subscriber in subscribers)
            {
                var invokeData = new MethodInvocationData
                {
                    IntentId    = _idGenerator.NewId(),
                    Service     = subscriber.Service,
                    Method      = subscriber.Method,
                    Parameters  = data.Parameters,
                    FlowContext = data.FlowContext,
                    Caller      = new CallerDescriptor(data.Service, data.Event, data.IntentId)
                };

                InvokeInBackground(invokeData, preferences);
            }
        }
        protected override void ExecuteAfterMethodInvocation(IMethodInvocation input, IMethodReturn methodReturn)
        {
            stopWatch.Stop();
            string elapsedTime = stopWatch.Elapsed.ToString();
            MethodInvocationData methodInvocationData = input.GetMethodInvocationData();
            string executionTimeSpanMessage           = string.Format(" The method {0} within class {1} finsished execution in time : {2}", methodInvocationData.MethodName, methodInvocationData.ClassName, elapsedTime);

            _logger.LogMessage(executionTimeSpanMessage);
        }
示例#5
0
 public TransitionCarrier(
     MethodInvocationData methodInvocationData,
     IValueContainerCopier valueContainerCopier,
     ICommunicatorMessage message)
 {
     _methodInvocationData = methodInvocationData;
     Caller = methodInvocationData.Caller;
     _valueContainerCopier = valueContainerCopier;
     Message = message;
 }
 public static void Write(Message message, MethodInvocationData data, ISerializer serializer)
 {
     message.Data["IntentId"]            = data.IntentId;
     message.Data["Service"]             = data.Service.Clone();
     message.Data["Method"]              = data.Method.Clone();
     message.Data["Continuation"]        = data.Continuation;
     message.Data["Continuation:Format"] = data.ContinuationState?.Format;
     message.Data["Continuation:State"]  = data.ContinuationState?.State;
     message.Data["Format"]              = serializer.Format;
     message.Data["Parameters"]          = serializer.SerializeToString(data.Parameters);
     message.Data["Caller"]              = data.Caller?.Clone();
     message.Data["FlowContext"]         = data.FlowContext;
 }
示例#7
0
 private async void InvokeInBackground(MethodInvocationData data, PublishPreferences preferences)
 {
     if (!_serviceResolver.TryResolve(data.Service, out var service) ||
         service.Definition.Type == Modeling.ServiceType.External)
     {
         var communicator = _communicatorProvider.GetCommunicator(data.Service, data.Method, assumeExternal: true);
         await communicator.InvokeAsync(data, default);
     }
     else if (!preferences.SkipLocalSubscribers)
     {
         var message = new HttpCommunicatorMessage {
             IsRetry = false
         };
         await _localMethodRunner.RunAsync(data, message);
     }
 }
示例#8
0
        public async Task <InvokeRoutineResult> InvokeAsync(
            MethodInvocationData data,
            InvocationPreferences preferences)
        {
            var message = new Message
            {
                Type = MessageType.InvokeMethod,
            };

            MethodInvocationDataTransformer.Write(message, data, _serializer);

            var result = new InvokeRoutineResult
            {
                Outcome = InvocationOutcome.Scheduled
            };

            if (preferences.Synchronous)
            {
                var completionNotification = new TaskCompletionSource <InvokeRoutineResult>();
                message.Data["Notification"] = completionNotification;

                _messageHub.Schedule(message);

                result = await completionNotification.Task;

                if (result.Result != null)
                {
                    result.Result = TaskResult.CreateEmpty(preferences.ResultValueType);
                    _serializer.Populate(_serializer.SerializeToString(result.Result), (IValueContainer)result.Result);
                }
            }
            else if (preferences.LockMessage)
            {
                result.MessageHandle = new MessageHandle(message, _messageHub);
            }
            else
            {
                _messageHub.Schedule(message);
            }

            return(result);
        }
        public async Task <InvokeRoutineResult> InvokeAsync(
            MethodInvocationData data,
            InvocationPreferences preferences)
        {
            var queueName = _settings.QueueName
                            .Replace("{serviceName}", data.Service.Name)
                            .Replace("{methodName}", data.Method.Name);

            // TODO: declare once? what's the penalty?
            _channel.QueueDeclare(
                queueName,
                durable: true,
                exclusive: false,
                autoDelete: false,
                arguments: null);

            var properties = CreateMessageProperties(_channel);

            properties.Type = MessageTypes.Invoke;
            SetIntentId(properties, data.IntentId);
            SetFormat(properties, _serializer);
            properties.Headers.Add("X-Service-Name", data.Service.Name);
            properties.Headers.Add("X-Method-Name", data.Method.Name);

            var payload = SerializePayload(data);

            _channel.BasicPublish(
                exchange: "",
                routingKey: queueName,
                basicProperties: properties,
                body: payload);

            _channel.WaitForConfirms(); // no async version :(

            return(new InvokeRoutineResult
            {
                Outcome = InvocationOutcome.Scheduled
            });
        }
示例#10
0
        public async Task ReactAsync(EventPublishData data, ICommunicatorMessage message)
        {
            var eventDesc = new EventDescriptor {
                Service = data.Service, Event = data.Event
            };
            var subscribers = _eventSubscriber.GetSubscribers(eventDesc).ToList();

            if (subscribers.Count == 0)
            {
                return;
            }

            var publisherServiceReference = _serviceResolver.Resolve(data.Service);
            var publisherEventReference   = _eventResolver.Resolve(publisherServiceReference.Definition, data.Event);

            var behaviorSettings = _communicationSettingsProvider.GetEventSettings(publisherEventReference.Definition, external: false);

            //-------------------------------------------------------------------------------
            // MESSGE DE-DUPLICATION
            //-------------------------------------------------------------------------------

            if (behaviorSettings.Deduplicate &&
                !message.CommunicatorTraits.HasFlag(CommunicationTraits.MessageDeduplication) &&
                (message.IsRetry != false || string.IsNullOrEmpty(message.RequestId)))
            {
                // TODO: if has message de-dup'er, check if a dedup
                // return new InvokeRoutineResult { Outcome = InvocationOutcome.Deduplicated };
            }

            //-------------------------------------------------------------------------------
            // UNIT OF WORK
            //-------------------------------------------------------------------------------

            // TODO: Unit of Work - check if there is cached/stored data
            if (message.IsRetry != false)
            {
                // TODO: If has entities in transitions and they have been already committed,
                // skip method transition and re-try sending commands and events.
            }

            foreach (var subscriber in subscribers)
            {
                // Skip external services
                if (!_serviceResolver.TryResolve(subscriber.Service, out var subscriberServiceReference) ||
                    subscriberServiceReference.Definition.Type == ServiceType.External)
                {
                    continue;
                }

                var invokeData = new MethodInvocationData
                {
                    IntentId    = _idGenerator.NewId(),
                    Service     = subscriber.Service,
                    Method      = subscriber.Method,
                    Parameters  = data.Parameters,
                    FlowContext = data.FlowContext,
                    Caller      = new CallerDescriptor(data.Service, data.Event, data.IntentId)
                };

                // TODO: think about fanning out depending on the communication mechanism, number of subscribers per event, and settings.
                await RunAsync(invokeData, message);
            }
        }
示例#11
0
        public async Task <InvokeRoutineResult> RunAsync(MethodInvocationData data, ICommunicatorMessage message)
        {
            var serviceReference = _serviceResolver.Resolve(data.Service);
            var methodReference  = _methodResolver.Resolve(serviceReference.Definition, data.Method);

            var behaviorSettings = _communicationSettingsProvider.GetMethodSettings(methodReference.Definition);

            //-------------------------------------------------------------------------------
            // MESSGE DE-DUPLICATION
            //-------------------------------------------------------------------------------

            bool needsDeduplication = behaviorSettings.Deduplicate;

            if (methodReference.Definition.IsQuery)
            {
                // NOTE: you can run queries using message passing (non-volatile),
                // or the volatile method uses a callback instead of re-trying.
                if (message.CommunicatorTraits.HasFlag(CommunicationTraits.Volatile) && data.Continuation == null)
                {
                    needsDeduplication = false;
                }
            }
            if (needsDeduplication &&
                !message.CommunicatorTraits.HasFlag(CommunicationTraits.MessageDeduplication) &&
                (message.IsRetry != false || string.IsNullOrEmpty(message.RequestId)))
            {
                // TODO: if has message de-dup'er, check if a dedup
                // return new InvokeRoutineResult { Outcome = InvocationOutcome.Deduplicated };
            }

            //-------------------------------------------------------------------------------
            // UNIT OF WORK
            //-------------------------------------------------------------------------------

            // TODO: Unit of Work - check if there is cached/stored data
            if (message.IsRetry != false)
            {
                // TODO: If has entities in transitions and they have been already committed,
                // skip method transition and re-try sending commands and events.
            }

            //-------------------------------------------------------------------------------
            // DELEGATION TO RESILIENT COMMUNICATOR
            //-------------------------------------------------------------------------------

            IMessageHandle messageHandle = null;

            if (behaviorSettings.Persistent && message.CommunicatorTraits.HasFlag(CommunicationTraits.Volatile))
            {
                // TODO: check if can poll for result! (think about continuation and sync invocation)

                var preferredCommunicator = _communicatorProvider.GetCommunicator(data.Service, data.Method);
                if (message.CommunicatorType != preferredCommunicator.Type &&
                    !preferredCommunicator.Traits.HasFlag(CommunicationTraits.Volatile))
                {
                    var resultValueType = methodReference.Definition.MethodInfo.ReturnType;
                    if (resultValueType != typeof(void))
                    {
                        resultValueType = TaskAccessor.GetTaskResultType(resultValueType);
                        if (resultValueType == TaskAccessor.VoidTaskResultType)
                        {
                            resultValueType = typeof(void);
                        }
                    }

                    var preferences = new InvocationPreferences
                    {
                        LockMessage = behaviorSettings.RunInPlace &&
                                      preferredCommunicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish),
                        ResultValueType = resultValueType
                    };

                    var invocationResult = await preferredCommunicator.InvokeAsync(data, preferences);

                    if (invocationResult.Outcome == InvocationOutcome.Complete && !string.IsNullOrEmpty(data.IntentId))
                    {
                        _routineCompletionSink.OnRoutineCompleted(data.Service, data.Method, data.IntentId, invocationResult.Result);
                    }

                    if (invocationResult.Outcome == InvocationOutcome.Scheduled && invocationResult.MessageHandle != null)
                    {
                        // NOTE: will run synchronously below
                        messageHandle = invocationResult.MessageHandle;
                    }
                    else
                    {
                        return(invocationResult);
                    }
                }
            }

            //-------------------------------------------------------------------------------
            // RUN METHOD TRANSITION
            //-------------------------------------------------------------------------------

            try
            {
                var adapter = new TransitionCarrier(data, _valueContainerCopier, message);
                var transitionDescriptor = new TransitionDescriptor {
                    Type = TransitionType.InvokeRoutine
                };
                var result = await RunRoutineAsync(adapter, transitionDescriptor, default);

                if (result.Outcome == InvocationOutcome.Complete && !string.IsNullOrEmpty(data.IntentId))
                {
                    _routineCompletionSink.OnRoutineCompleted(data.Service, data.Method, data.IntentId, result.Result);
                }
                if (messageHandle != null)
                {
                    await messageHandle.Complete();
                }
                return(result);
            }
            catch
            {
                messageHandle?.ReleaseLock();
                throw;
            }
        }
示例#12
0
        public async Task HandleAsync(PathString basePath, HttpContext context, CancellationToken ct)
        {
            var isQueryRequest   = context.Request.Method == "GET";
            var isCommandRequest = context.Request.Method == "POST";

            if (!isQueryRequest && !isCommandRequest)
            {
                await ReplyWithTextError(context.Response, 405, "Only GET and POST verbs are allowed");

                return;
            }

            var headers = context.Response.Headers;

            headers.Add(DasyncHttpHeaders.PoweredBy, "D-ASYNC");

            var basePathSegmentCount = basePath.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length;
            var pathSegments         = context.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Skip(basePathSegmentCount).ToArray();

            if (pathSegments.Length == 0)
            {
                await ReplyWithTextError(context.Response, 404, "Empty request URL");

                return;
            }

            var serviceId = new ServiceId {
                Name = pathSegments[0]
            };

            if (!_serviceResolver.TryResolve(serviceId, out var serviceReference))
            {
                await ReplyWithTextError(context.Response, 404, $"Service '{serviceId.Name}' is not registered");

                return;
            }

            if (serviceReference.Definition.Type == ServiceType.External)
            {
                await ReplyWithTextError(context.Response, 404, $"Cannot invoke external service '{serviceReference.Definition.Name}' on your behalf");

                return;
            }

            if (pathSegments.Length == 1)
            {
                await ReplyWithTextError(context.Response, 404, "The request URL does not contain a service method");

                return;
            }

            var methodId = new MethodId {
                Name = pathSegments[1]
            };
            string methodIntentId = null;

            if (pathSegments.Length == 3)
            {
                methodIntentId = pathSegments[2];

                if (isQueryRequest)
                {
                    await HandleResultPoll(context, serviceReference.Id, methodId, methodIntentId);

                    return;
                }
            }

            if (pathSegments.Length > 3)
            {
                await ReplyWithTextError(context.Response, 404, "The request URL contains extra segments");

                return;
            }

            var         contentType = context.Request.GetContentType();
            ISerializer serializer;

            try
            {
                serializer = GetSerializer(contentType, isQueryRequest);
            }
            catch (ArgumentException)
            {
                await ReplyWithTextError(context.Response, 406, $"The Content-Type '{contentType.MediaType}' is not supported");

                return;
            }

            if (!_methodResolver.TryResolve(serviceReference.Definition, methodId, out var methodReference))
            {
                await ReplyWithTextError(context.Response, 404, $"The service '{serviceReference.Definition.Name}' does not have method '{methodId.Name}'");

                return;
            }

            if (isQueryRequest && !methodReference.Definition.IsQuery)
            {
                await ReplyWithTextError(context.Response, 404, $"The method '{methodId.Name}' of service '{serviceReference.Definition.Name}' is a command, but not a query, thus must be invoked with the POST verb");

                return;
            }

            MethodInvocationData   invokeData   = null;
            MethodContinuationData continueData = null;
            IValueContainer        parametersContainer;
            bool compressResponse    = false;
            bool respondWithEnvelope = false;

            if (isQueryRequest)
            {
                var inputObject = new JObject();

                foreach (var kvPair in context.Request.Query)
                {
                    if (kvPair.Value.Count == 0)
                    {
                        continue;
                    }

                    var parameterName = kvPair.Key;

                    if (kvPair.Value.Count == 1)
                    {
                        var parameterValue = kvPair.Value[0];
                        inputObject.Add(parameterName, parameterValue);
                    }
                    else
                    {
                        var values = new JArray();
                        foreach (var value in kvPair.Value)
                        {
                            values.Add(value);
                        }
                        inputObject.Add(parameterName, values);
                    }
                }

                var parametersJson = inputObject.ToString();

                parametersContainer = new SerializedValueContainer(parametersJson, _jsonSerializer);

                invokeData = new MethodInvocationData
                {
                    Service    = serviceId,
                    Method     = methodId,
                    Parameters = parametersContainer,
                    Caller     = GetCaller(context.Request.Headers)
                };
            }
            else
            {
                var payloadStream = context.Request.Body;

                var encoding = context.Request.GetContentEncoding();
                if (!string.IsNullOrEmpty(encoding))
                {
                    if ("gzip".Equals(encoding, StringComparison.OrdinalIgnoreCase))
                    {
                        compressResponse = true;
                        payloadStream    = new GZipStream(payloadStream, CompressionMode.Decompress, leaveOpen: true);
                    }
                    else if ("deflate".Equals(encoding, StringComparison.OrdinalIgnoreCase))
                    {
                        payloadStream = new DeflateStream(payloadStream, CompressionMode.Decompress, leaveOpen: true);
                    }
                    else
                    {
                        await ReplyWithTextError(context.Response, 406, $"The Content-Encoding '{encoding}' is not supported");

                        return;
                    }
                }

                var envelopeType = context.Request.Headers.GetValue(DasyncHttpHeaders.Envelope);

                if (string.IsNullOrEmpty(envelopeType))
                {
                    var payload = await payloadStream.ToBytesAsync();

                    parametersContainer = new SerializedValueContainer(payload, serializer);

                    if (string.IsNullOrEmpty(methodIntentId))
                    {
                        invokeData = new MethodInvocationData
                        {
                            Service    = serviceId,
                            Method     = methodId,
                            Parameters = parametersContainer,
                            Caller     = GetCaller(context.Request.Headers)
                        };
                    }
                    else
                    {
                        continueData = new MethodContinuationData
                        {
                            Service = serviceId,
                            Method  = methodId.CopyTo(new PersistedMethodId()),
                            Result  = parametersContainer,
                            Caller  = GetCaller(context.Request.Headers)
                        };
                        continueData.Method.IntentId = methodIntentId;
                        // TODO: get ETag from the query string
                    }
                }
                else if (envelopeType.Equals("invoke", StringComparison.OrdinalIgnoreCase))
                {
                    respondWithEnvelope = true;
                    invokeData          = await DeserializeAsync <MethodInvocationData>(serializer, payloadStream);
                }
                else if (envelopeType.Equals("continue", StringComparison.OrdinalIgnoreCase))
                {
                    respondWithEnvelope = true;
                    continueData        = await DeserializeAsync <MethodContinuationData>(serializer, payloadStream);
                }
                else
                {
                    await ReplyWithTextError(context.Response, 406, $"Unknown envelope type '{envelopeType}'");

                    return;
                }
            }

            var intentId              = context.Request.Headers.GetValue(DasyncHttpHeaders.IntentId) ?? _idGenerator.NewId();
            var externalRequestId     = context.Request.Headers.GetValue(DasyncHttpHeaders.RequestId);
            var externalCorrelationId = context.Request.Headers.GetValue(DasyncHttpHeaders.CorrelationId);
            var isRetry = context.Request.Headers.IsRetry();

            var rfc7240Preferences             = context.Request.Headers.GetRFC7240Preferences();
            var isHttpRequestBlockingExecution = !(rfc7240Preferences.RespondAsync == true);
            var waitTime = rfc7240Preferences.Wait;

            if (waitTime > MaxLongPollTime)
            {
                waitTime = MaxLongPollTime;
            }
            var waitForResult = isHttpRequestBlockingExecution || waitTime > TimeSpan.Zero;

            var communicatorMessage = new HttpCommunicatorMessage
            {
                IsRetry       = isRetry,
                RequestId     = externalRequestId,
                WaitForResult = waitForResult
            };

            if (invokeData != null)
            {
                if (invokeData.IntentId == null)
                {
                    invokeData.IntentId = intentId;
                }

                var invokeTask = _localTransitionRunner.RunAsync(invokeData, communicatorMessage);

                if (isHttpRequestBlockingExecution)
                {
                    var invocationResult = await invokeTask;

                    if (invocationResult.Outcome == InvocationOutcome.Complete)
                    {
                        await RespondWithMethodResult(context.Response, invocationResult.Result, serializer, respondWithEnvelope, compressResponse);

                        return;
                    }

                    if (waitForResult)
                    {
                        var taskResult = await TryWaitForResultAsync(
                            serviceReference.Id,
                            methodId, intentId, waitTime);

                        if (taskResult != null)
                        {
                            await RespondWithMethodResult(context.Response, taskResult, serializer, respondWithEnvelope, compressResponse);

                            return;
                        }
                    }
                }
                else
                {
                    // TODO: continue 'invokeTask' and handle exceptions in background
                }

                communicatorMessage.WaitForResult = false;

                var location = string.Concat(context.Request.Path, "/", intentId);
                context.Response.Headers.Add("Location", location);
                context.Response.Headers.Add(DasyncHttpHeaders.IntentId, intentId);
                context.Response.StatusCode = DasyncHttpCodes.Scheduled;
            }
            else if (continueData != null)
            {
                if (continueData.IntentId == null)
                {
                    continueData.IntentId = intentId;
                }

                var continueTask = _localTransitionRunner.ContinueAsync(continueData, communicatorMessage);
                // TODO: continue 'continueTask' in backgraound to handle exceptions

                context.Response.Headers.Add("Location", context.Request.Path.ToString());
                context.Response.Headers.Add(DasyncHttpHeaders.IntentId, continueData.Method.IntentId);
                context.Response.StatusCode = DasyncHttpCodes.Scheduled;
            }
        }
示例#13
0
        public async Task <InvokeRoutineResult> InvokeAsync(
            MethodInvocationData data,
            InvocationPreferences preferences)
        {
            HttpResponseMessage response;

            using (var requestContent = CreateContent(data))
            {
                requestContent.Headers.TryAddWithoutValidation(DasyncHttpHeaders.Envelope, "invoke");
                requestContent.Headers.TryAddWithoutValidation(DasyncHttpHeaders.IntentId, data.IntentId);
                AddAsyncHeader(requestContent.Headers, preferAsync: !preferences.Synchronous);
                AddCallerHeaders(requestContent.Headers, data.Caller);
                AddRetryHeader(requestContent.Headers, retryCount: 0);

                var url = _urlTemplate
                          .Replace("{serviceName}", data.Service.Name)
                          .Replace("{methodName}", data.Method.Name);

                response = await _httpClient.PostAsync(url, requestContent);
            }
            using (response)
            {
                if ((int)response.StatusCode == DasyncHttpCodes.Scheduled)
                {
                    return(new InvokeRoutineResult
                    {
                        Outcome = InvocationOutcome.Scheduled
                    });
                }

                if ((int)response.StatusCode == DasyncHttpCodes.Deduplicated)
                {
                    return(new InvokeRoutineResult
                    {
                        Outcome = InvocationOutcome.Deduplicated
                    });
                }

                var methodOutcome = response.Headers.GetValue(DasyncHttpHeaders.TaskResult);
                if (string.IsNullOrEmpty(methodOutcome))
                {
                    throw new Exception(); // TODO: add info
                }

                var responseStream = await response.Content.ReadAsStreamAsync();

                var encoding = response.Headers.GetContentEncoding();
                if (!string.IsNullOrEmpty(encoding))
                {
                    if ("gzip".Equals(encoding, StringComparison.OrdinalIgnoreCase))
                    {
                        responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
                    }
                    else if ("deflate".Equals(encoding, StringComparison.OrdinalIgnoreCase))
                    {
                        responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
                    }
                    else
                    {
                        throw new Exception($"Unknown content encoding '{encoding}'.");
                    }
                }

                using (responseStream)
                {
                    var taskResult = TaskResult.CreateEmpty(preferences.ResultValueType);
                    _serializer.Populate(responseStream, (IValueContainer)taskResult);

                    return(new InvokeRoutineResult
                    {
                        Outcome = InvocationOutcome.Complete,
                        Result = taskResult
                    });
                }
            }
        }