Пример #1
0
        /// <remarks>
        /// Fire and forget mode (async void).
        /// </remarks>
        public async void ExecuteAndAwaitInBackground(ExecuteRoutineIntent intent, Task proxyTask)
        {
            // Tell platform to track the completion.
            // Do it before commit as a routine may complete immediately.

            var tcs = new TaskCompletionSource <TaskResult>();

            _routineCompletionNotifier.NotifyCompletion(intent.ServiceId, intent.MethodId, intent.Id, tcs, default);

            // Commit intent

            var options = new TransitionCommitOptions
            {
                // Set the hint about the synchronous call mode.
                NotifyOnRoutineCompletion = true
            };

            var actions = new ScheduledActions
            {
                ExecuteRoutineIntents = new List <ExecuteRoutineIntent>
                {
                    intent
                }
            };
            await _transitionCommitter.CommitAsync(actions, transitionCarrier : null, options : options, ct : default);

            // Await for completion and set the result.

            var routineResult = await tcs.Task;

            proxyTask.TrySetResult(routineResult);
        }
Пример #2
0
        /// <remarks>
        /// Fire and forget mode (async void).
        /// </remarks>
        public async void ExecuteAndAwaitInBackground(ExecuteRoutineIntent intent, Task proxyTask)
        {
            // Commit intent

            // Set the hint about the synchronous call mode.
            intent.NotifyOnCompletion = true;
            var actions = new ScheduledActions
            {
                ExecuteRoutineIntents = new List <ExecuteRoutineIntent>
                {
                    intent
                }
            };
            await _transitionCommitter.CommitAsync(actions, transitionCarrier : null, ct : default(CancellationToken));

            // Tell platform to track the completion.

            var tcs = new TaskCompletionSource <TaskResult>();

            _routineCompletionNotifier.NotifyCompletion(intent.Id, tcs);

            // Await for completion and set the result.

            var routineResult = await tcs.Task;

            proxyTask.TrySetResult(routineResult);
        }
Пример #3
0
        public async Task <ActiveRoutineInfo> ScheduleRoutineAsync(
            ExecuteRoutineIntent intent, CancellationToken ct)
        {
            var pregeneratedRoutineId = intent.Id.ToString();

            var routineDescriptor = new RoutineDescriptor
            {
                IntentId  = intent.Id,
                MethodId  = intent.MethodId,
                RoutineId = pregeneratedRoutineId
            };

            var eventData = new RoutineEventData
            {
                ServiceId    = intent.ServiceId,
                Routine      = routineDescriptor,
                Caller       = intent.Caller,
                Continuation = intent.Continuation,
                Parameters   = _serializer.SerializeToString(intent.Parameters)
            };

            var eventEnvelope = new RoutineEventEnvelope
            {
                CloudEventsVersion = CloudEventsEnvelope.Version,
                EventType          = DasyncCloudEventsTypes.InvokeRoutine.Name,
                EventTypeVersion   = DasyncCloudEventsTypes.InvokeRoutine.Version,
                Source             = "/" + (intent.Caller?.ServiceId.ServiceName ?? ""),
                EventID            = intent.Id.ToString(),
                EventTime          = DateTimeOffset.Now,
                ContentType        = "application/json",
                Data = CloudEventsSerialization.Serialize(eventData)
            };

            var message = new CloudQueueMessage(
                JsonConvert.SerializeObject(eventEnvelope,
                                            CloudEventsSerialization.JsonSerializerSettings));

            while (true)
            {
                try
                {
                    await _transitionsQueue.AddMessageAsync(message, null, null, ct);

                    break;
                }
                catch (QueueDoesNotExistException)
                {
                    await _transitionsQueue.CreateAsync(ct);
                }
            }

            return(new ActiveRoutineInfo
            {
                RoutineId = pregeneratedRoutineId
            });
        }
Пример #4
0
        public void RegisterIntent(ExecuteRoutineIntent intent, Task proxyTask)
        {
            var transitionContext = Context;
            var actions           = transitionContext.ScheduledActions;

            if (actions.ExecuteRoutineIntents == null)
            {
                actions.ExecuteRoutineIntents = new List <ExecuteRoutineIntent>();
            }
            actions.ExecuteRoutineIntents.Add(intent);
            _taskContinuationTracker.StartTracking(proxyTask, _onRoutineContinuationSetCallback, intent);
        }
Пример #5
0
        public Task <ActiveRoutineInfo> ScheduleRoutineAsync(
            ExecuteRoutineIntent intent, CancellationToken ct)
        {
#warning Need to send message first, then create routine record.

            var routineId = Interlocked.Increment(ref _dataStore.RoutineCounter);

            var routineRecord = new RoutineStateRecord
            {
                ETag         = DateTime.UtcNow.Ticks.ToString("X16"),
                Id           = routineId.ToString(),
                Completion   = new TaskCompletionSource <string>(),
                Continuation = intent.Continuation == null ? null : _serializer.SerializeToString(intent.Continuation)
            };

            lock (_dataStore.Routines)
            {
                _dataStore.Routines.Add(routineRecord.Id, routineRecord);
            }

            var transitionDescriptor = new TransitionDescriptor
            {
                Type = TransitionType.InvokeRoutine,
                ETag = routineRecord.ETag
            };

            var routineDescriptor = new RoutineDescriptor
            {
                MethodId  = intent.MethodId,
                IntentId  = intent.Id,
                RoutineId = routineRecord.Id,
                ETag      = routineRecord.ETag
            };

            var message = new Message
            {
                //["IntentId"] = _serializer.Serialize(intent.Id),
                [nameof(TransitionDescriptor)] = _serializer.SerializeToString(transitionDescriptor),
                [nameof(ServiceId)]            = _serializer.SerializeToString(intent.ServiceId),
                [nameof(RoutineDescriptor)]    = _serializer.SerializeToString(routineDescriptor),
                ["Parameters"] = _serializer.SerializeToString(intent.Parameters)
            };

            _dataStore.ScheduleMessage(message);

            var info = new ActiveRoutineInfo
            {
                RoutineId = routineRecord.Id
            };

            return(Task.FromResult(info));
        }
Пример #6
0
 public static MethodInvocationData CreateMethodInvocationData(ExecuteRoutineIntent intent, ITransitionContext context)
 {
     return(new MethodInvocationData
     {
         IntentId = intent.Id,
         Service = intent.Service,
         Method = intent.Method,
         Parameters = intent.Parameters,
         Continuation = intent.Continuation,
         Caller = context?.CurrentAsCaller(),
         FlowContext = context?.FlowContext
     });
 }
Пример #7
0
        /// <remarks>
        /// Fire and forget mode (async void).
        /// </remarks>
        public async void ExecuteAndAwaitInBackground(ExecuteRoutineIntent intent, Task proxyTask)
        {
#warning Add infrastructure exception handling

            try
            {
#warning need to associate a routine with a cancellaion token, and abandon polling when canceled
                var ct = CancellationToken.None;

                var fabricConnector = _fabricConnectorSelector.Select(intent.ServiceId);

                var routineInfo = await fabricConnector.ScheduleRoutineAsync(intent, ct);

                for (var i = 0; ; i++)
                {
#warning Ideally need to handle 'fire an forget' cases - the continuation is never set?
                    //if (!proxyTask.continuation == null after 1 min?)
                    //    return;

                    routineInfo = await fabricConnector.PollRoutineResultAsync(routineInfo, ct);

                    if (routineInfo.Result != null)
                    {
                        break;
                    }

                    TimeSpan delayInterval;

                    if (routineInfo is IRoutinePollInterval routinePollInterval)
                    {
                        delayInterval = routinePollInterval.Suggest(i);
                    }
                    else
                    {
                        delayInterval = TimeSpan.FromSeconds(0.5);
                    }

                    await Task.Delay(delayInterval);
                }

                if (!proxyTask.TrySetResult(routineInfo.Result))
                {
                    throw new InvalidOperationException("Critical error - cannot set result to the proxy task.");
                }
            }
            catch (Exception ex)
            {
                //Console.WriteLine(ex.ToString());
                throw;
            }
        }
Пример #8
0
        public Task <ActiveRoutineInfo> ScheduleRoutineAsync(
            ExecuteRoutineIntent intent, CancellationToken ct)
        {
            var pregeneratedRoutineId = intent.Id.ToString();

            var routineDescriptor = new RoutineDescriptor
            {
                IntentId  = intent.Id,
                MethodId  = intent.MethodId,
                RoutineId = pregeneratedRoutineId
            };

            var eventData = new RoutineEventData
            {
                ServiceId    = intent.ServiceId,
                Routine      = routineDescriptor,
                Caller       = intent.Caller,
                Continuation = intent.Continuation,
                Parameters   = _serializer.SerializeToString(intent.Parameters)
            };

            var eventEnvelope = new RoutineEventEnvelope
            {
                CloudEventsVersion = CloudEventsEnvelope.Version,
                EventType          = DasyncCloudEventsTypes.InvokeRoutine.Name,
                EventTypeVersion   = DasyncCloudEventsTypes.InvokeRoutine.Version,
                Source             = "/" + (intent.Caller?.ServiceId.ServiceName ?? ""),
                EventID            = intent.Id.ToString(),
                EventTime          = DateTimeOffset.Now,
                ContentType        = "application/json",
                Data = CloudEventsSerialization.Serialize(eventData)
            };

            var fileName = intent.Id.ToString() + ".json";
            var filePath = Path.Combine(_transitionsDirectory, fileName);
            var content  = CloudEventsSerialization.Serialize(eventEnvelope);

            File.WriteAllText(filePath, content, Encoding.UTF8);

            var info = new ActiveRoutineInfo
            {
                RoutineId = pregeneratedRoutineId
            };

            return(Task.FromResult(info));
        }
Пример #9
0
        /// <remarks>
        /// Fire and forget mode (async void).
        /// </remarks>
        public async void ExecuteAndAwaitInBackground(ExecuteRoutineIntent intent, Task proxyTask)
        {
            // TODO: exception handling
            var result = await _singleMethodInvoker.InvokeAsync(intent);

            if (result.Outcome == InvocationOutcome.Complete)
            {
                proxyTask.TrySetResult(result.Result);
            }
            else
            {
                var tcs = new TaskCompletionSource <ITaskResult>();
                _routineCompletionNotifier.NotifyOnCompletion(intent.Service, intent.Method, intent.Id, tcs, default);
                var taskResult = await tcs.Task;
                proxyTask.TrySetResult(taskResult);
            }
        }
Пример #10
0
        public Task Execute <TParameters>(IProxy proxy, MethodInfo methodInfo, ref TParameters parameters)
            where TParameters : IValueContainer
        {
            var serviceProxyContext = (ServiceProxyContext)proxy.Context;

            var intent = new ExecuteRoutineIntent
            {
                Id         = _numericIdGenerator.NewId(),
                ServiceId  = serviceProxyContext.Descriptor.Id,
                MethodId   = _routineMethodIdProvider.GetId(methodInfo),
                Parameters = parameters
            };

            var taskResultType =
                // Dispose() does not return a task, and is the only exception.
#warning check if it's really IDisposable.Dispose
                methodInfo.ReturnType == typeof(void)
                ? TaskAccessor.VoidTaskResultType
                : TaskAccessor.GetTaskResultType(methodInfo.ReturnType);

            var taskState = new RoutineReference
            {
                IntentId = intent.Id
#warning must have id of actual routine for dynamic subscription (subscribe after a routine already scheduled).
            };

            var proxyTask = TaskAccessor.CreateTask(taskState, taskResultType);

            bool executeInline = !_transitionScope.IsActive || !IsCalledByRoutine(
                _transitionScope.CurrentMonitor.Context,
                // Skip 2 stack frames: current method and dynamically-generated proxy.
                // WARNING! DO NOT PUT 'new StackFrame()' into a helper method!
                new StackFrame(skipFrames: 2, fNeedFileInfo: false));

            if (executeInline)
            {
                ExecuteAndAwaitInBackground(intent, proxyTask);
            }
            else
            {
                _transitionScope.CurrentMonitor.RegisterIntent(intent, proxyTask);
            }

            return(proxyTask);
        }
Пример #11
0
        public async Task <RoutineInfo> ScheduleRoutineAsync(ExecuteRoutineIntent intent, NameValueCollection context, CancellationToken ct)
        {
            var uri = string.Concat(_serviceHttpConfigurator.GetUrl(_serviceDefinition), "/", intent.MethodId.MethodName);

            var json = _dasyncJsonSerializer.SerializeToString(intent);

            while (true)
            {
                try
                {
                    var content = new StringContent(json);
                    content.Headers.ContentType = new MediaTypeHeaderValue("application/dasync+json");

                    if (context?.Count > 0)
                    {
                        content.Headers.TryAddWithoutValidation(DasyncHttpHeaders.Context, context.Serialize());
                    }

                    var response = await _httpClient.PutAsync(uri, content, ct);

                    var statusCode = (int)response.StatusCode;
                    if (statusCode == DasyncHttpCodes.Succeeded || statusCode == DasyncHttpCodes.Faulted || statusCode == DasyncHttpCodes.Canceled)
                    {
                        TaskResult taskResult;
                        using (var stream = await response.Content.ReadAsStreamAsync())
                        {
                            taskResult = _dasyncJsonSerializer.Deserialize <TaskResult>(stream);
                        }

                        return(new RoutineInfo
                        {
                            Result = taskResult
                        });
                    }
                    else
                    {
                        throw new InvalidOperationException($"Unexpected HTTP {statusCode} response:\r\n{await response.Content.ReadAsStringAsync()}");
                    }
                }
                catch (Exception)
                {
                    await Task.Delay(5_000);
                }
            }
        }
Пример #12
0
        public Task Execute <TParameters>(IProxy proxy, MethodInfo methodInfo, ref TParameters parameters)
            where TParameters : IValueContainer
        {
            var serviceProxyContext = (ServiceProxyContext)proxy.Context;

            var intent = new ExecuteRoutineIntent
            {
                Id         = _numericIdGenerator.NewId(),
                ServiceId  = serviceProxyContext.Service.Id,
                MethodId   = _routineMethodIdProvider.GetId(methodInfo),
                Parameters = parameters
            };

            var taskResultType =
                // Dispose() does not return a task, and is the only exception.
#warning check if it's really IDisposable.Dispose
                methodInfo.ReturnType == typeof(void)
                ? TaskAccessor.VoidTaskResultType
                : TaskAccessor.GetTaskResultType(methodInfo.ReturnType);

            var taskState = new RoutineReference
            {
                IntentId = intent.Id
#warning must have id of actual routine for dynamic subscription (subscribe after a routine already scheduled).
            };

            var proxyTask = TaskAccessor.CreateTask(taskState, taskResultType);

            if (_transitionScope.IsActive)
            {
                _transitionScope.CurrentMonitor.RegisterIntent(intent, proxyTask);
            }
            else
            {
                ExecuteAndAwaitInBackground(intent, proxyTask);
            }

            return(proxyTask);
        }
Пример #13
0
        public async Task HandleAsync(PathString basePath, HttpContext context, CancellationToken ct)
        {
            var headers = context.Response.Headers;

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

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

            if (pathSegments.Length == 0 && context.Request.Query.ContainsKey("react"))
            {
                await HandleEventReactionAsync(null, context, ct);

                return;
            }

            if (pathSegments.Length == 0)
            {
                context.Response.StatusCode  = 404;
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("Empty request URL"));

                return;
            }

            var serviceName       = pathSegments[0];
            var serviceDefinition = _communicationModelProvider.Model.FindServiceByName(serviceName);

            if (serviceDefinition == null)
            {
                context.Response.StatusCode  = 404;
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes($"Service '{serviceName}' is not registered"));

                return;
            }

            if (serviceDefinition.Type == ServiceType.External)
            {
                context.Response.StatusCode  = 403; // Forbidden
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes($"Cannot invoke external service '{serviceName}' on your behalf"));

                return;
            }

            if (_eventDispatcher != null && pathSegments.Length == 1 && context.Request.Query.ContainsKey("react"))
            {
                await HandleEventReactionAsync(serviceDefinition, context, ct);

                return;
            }

            if (pathSegments.Length == 1)
            {
                context.Response.StatusCode  = 404;
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("The request URL does not contain a service method"));

                return;
            }

            if (_eventDispatcher != null && context.Request.Query.ContainsKey("subscribe"))
            {
                await HandleEventSubsciptionAsync(serviceDefinition, pathSegments[1], context, ct);

                return;
            }

            var methodName = pathSegments[1];

            if (pathSegments.Length == 3)
            {
                await HandleResultPoll(context, serviceName, methodName, intentId : pathSegments[2]);

                return;
            }

            if (pathSegments.Length > 3)
            {
                context.Response.StatusCode  = 404;
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("The request URL contains extra segments"));

                return;
            }

            var isQueryRequest      = context.Request.Method == "GET";
            var isCommandRequest    = context.Request.Method == "PUT" || context.Request.Method == "POST";
            var contentType         = context.Request.GetContentType();
            var isJsonRequest       = string.Equals(contentType.MediaType, "application/json", StringComparison.OrdinalIgnoreCase);
            var isDasyncJsonRequest = string.Equals(contentType.MediaType, "application/dasync+json", StringComparison.OrdinalIgnoreCase);

            if (!isQueryRequest && !isJsonRequest && !isDasyncJsonRequest)
            {
                context.Response.StatusCode  = 406; // Not Acceptable
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("The request content type is either not specified or not supported"));

                return;
            }

            if (!isQueryRequest && !isCommandRequest)
            {
                context.Response.StatusCode  = 405; // Method Not Allowed
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("The service method invocation must use one of these HTTP verbs: GET, POST, PUT"));

                return;
            }

            var routineMethodId = new RoutineMethodId
            {
                MethodName = methodName
            };

#warning Use model for method resolution instead
            MethodInfo routineMethod;
            try
            {
                routineMethod = _routineMethodResolver.Resolve(serviceDefinition, routineMethodId);
            }
            catch
            {
                context.Response.StatusCode  = 404;
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes($"The service '{serviceDefinition.Name}' does not have method '{methodName}'"));

                return;
            }

#warning Use model to determine if the method is command or query
            var isQueryMethod = GetWordAndSynonyms.Contains(GetFirstWord(methodName));

            if (isQueryRequest && !isQueryMethod)
            {
                context.Response.StatusCode  = 404;
                context.Response.ContentType = "text/plain";
                await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes($"The method '{methodName}' of service '{serviceDefinition.Name}' is a command, but not a query, thus must be invoked with POST or PUT verb"));

                return;
            }

            string parametersJson = null;
            ContinuationDescriptor continuationDescriptor = null;

            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);
                    }
                }

                parametersJson = inputObject.ToString();
            }
            else if (isJsonRequest)
            {
                parametersJson = await context.Request.ReadBodyAsStringAsync(contentType);
            }
            else
            {
                var inputJson = await context.Request.ReadBodyAsStringAsync(contentType);

                var envelope = JsonConvert.DeserializeObject <CommandEnvelope>(inputJson, JsonSettings);
                parametersJson         = envelope.Parameters;
                continuationDescriptor = envelope.Continuation;
            }

            var methodInvoker      = _methodInvokerFactory.Create(routineMethod);
            var parameterContainer = methodInvoker.CreateParametersContainer();

            if (!string.IsNullOrWhiteSpace(parametersJson))
            {
                if (isDasyncJsonRequest)
                {
                    _dasyncJsonSerializer.Populate(parametersJson, parameterContainer);
                }
                else
                {
                    JsonConvert.PopulateObject(parametersJson, parameterContainer, JsonSettings);
                }
            }

            var externalRequestId     = context.Request.Headers.TryGetValue(DasyncHttpHeaders.RequestId, out var requestIdValues) && requestIdValues.Count > 0 ? requestIdValues[0] : null;
            var externalCorrelationId = context.Request.Headers.TryGetValue(DasyncHttpHeaders.CorrelationId, out var correlationIdValues) && correlationIdValues.Count > 0 ? correlationIdValues[0] : null;

            var rfc7240Preferences = GetPreferences(context.Request.Headers);

            var isHttpRequestBlockingExecution = !(rfc7240Preferences.RespondAsync == true);

            _transitionUserContext.Current = GetUserContext(context);

            _intentPreprocessor.PrepareContext(context);
            if (await _intentPreprocessor.PreprocessAsync(context, serviceDefinition, routineMethodId, parameterContainer).ConfigureAwait(false))
            {
                return;
            }

            var serviceId = new ServiceId {
                ServiceName = serviceDefinition.Name
            };
            var intentId = _idGenerator.NewId();

            if (isQueryMethod)
            {
                var serviceInstance = _domainServiceProvider.GetService(serviceDefinition.Implementation);

                foreach (var postAction in _transitionActions)
                {
                    await postAction.OnRoutineStartAsync(serviceDefinition, serviceId, routineMethodId, intentId);
                }

                Task task;
                try
                {
                    task = methodInvoker.Invoke(serviceInstance, parameterContainer);
                    if (routineMethod.ReturnType != typeof(void))
                    {
                        try { await task; } catch { }
                    }
                }
                catch (Exception ex)
                {
                    if (ex is TargetInvocationException)
                    {
                        ex = ex.InnerException;
                    }
                    task = Task.FromException(ex);
                }
                var taskResult = task?.ToTaskResult() ?? new TaskResult();

                foreach (var postAction in _transitionActions)
                {
                    await postAction.OnRoutineCompleteAsync(serviceDefinition, serviceId, routineMethodId, intentId, taskResult);
                }

                await RespondWithRoutineResult(context, taskResult, isDasyncJsonRequest);
            }
            else
            {
                var intent = new ExecuteRoutineIntent
                {
                    Id        = intentId,
                    ServiceId = new ServiceId {
                        ServiceName = serviceDefinition.Name
                    },
                    MethodId     = routineMethodId,
                    Parameters   = parameterContainer,
                    Continuation = continuationDescriptor
                };

                var actions = new ScheduledActions
                {
                    ExecuteRoutineIntents = new List <ExecuteRoutineIntent> {
                        intent
                    }
                };

                var options = new TransitionCommitOptions
                {
                    NotifyOnRoutineCompletion = isHttpRequestBlockingExecution,
                    RequestId     = externalRequestId,
                    CorrelationId = externalCorrelationId ?? externalRequestId
                };

                await _transitionCommitter.CommitAsync(actions, null, options, default);

                var waitTime = rfc7240Preferences.Wait;
                if (isHttpRequestBlockingExecution || waitTime > MaxLongPollTime)
                {
                    waitTime = MaxLongPollTime;
                }

                if (waitTime > TimeSpan.Zero)
                {
                    var cts            = new CancellationTokenSource();
                    var completionSink = new TaskCompletionSource <TaskResult>();
                    _routineCompletionNotifier.NotifyCompletion(intent.ServiceId, intent.MethodId, intent.Id, completionSink, cts.Token);
                    try
                    {
                        var taskResult = await completionSink.Task.WithTimeout(waitTime);
                        await RespondWithRoutineResult(context, taskResult, isDasyncJsonRequest);

                        return;
                    }
                    catch (TaskCanceledException)
                    {
                        cts.Cancel();
                    }
                }

                var location = context.Request.Path.ToString() + "/" + intent.Id;
                context.Response.Headers.Add("Location", location);
                context.Response.StatusCode = DasyncHttpCodes.Scheduled;
                return;
            }
        }
Пример #14
0
        public Task Execute <TParameters>(IProxy proxy, MethodInfo methodInfo, ref TParameters parameters)
            where TParameters : IValueContainer
        {
            var serviceProxyContext = (ServiceProxyContext)proxy.Context;
            var methodDefinition    = serviceProxyContext.Definition.FindMethod(methodInfo);

            if (methodDefinition == null || methodDefinition.IsIgnored)
            {
                var invoker = _methodInvokerFactory.Create(methodInfo);
                return(invoker.Invoke(proxy, parameters));
            }

            var intent = new ExecuteRoutineIntent
            {
                Id         = _idGenerator.NewId(),
                Service    = serviceProxyContext.Descriptor.Id,
                Method     = _routineMethodIdProvider.GetId(methodInfo),
                Parameters = parameters
            };

            Type taskResultType =
                methodInfo.ReturnType == typeof(void)
                ? TaskAccessor.VoidTaskResultType
                : TaskAccessor.GetTaskResultType(methodInfo.ReturnType);

            var taskState = new RoutineReference
            {
                ServiceId = intent.Service,
                MethodId  = intent.Method,
                IntentId  = intent.Id
#warning must have id of actual routine for dynamic subscription (subscribe after a routine already scheduled).
            };

            var proxyTask = TaskAccessor.CreateTask(taskState, taskResultType);

            bool invokedByRunningMethod = _transitionScope.IsActive &&
                                          IsCalledByRoutine(
                _transitionScope.CurrentMonitor.Context,
                // Skip 2 stack frames: current method and dynamically-generated proxy.
                // WARNING! DO NOT PUT 'new StackFrame()' into a helper method!
                new StackFrame(skipFrames: 2, fNeedFileInfo: false));

            bool ignoreTransaction = !invokedByRunningMethod;

            if (!ignoreTransaction && _transitionScope.IsActive)
            {
                var runningMethodSettings = _communicationSettingsProvider.GetMethodSettings(
                    _transitionScope.CurrentMonitor.Context.MethodRef.Definition);

                if (!runningMethodSettings.Transactional)
                {
                    ignoreTransaction = true;
                }
            }

            if (!ignoreTransaction)
            {
                var methodSettings = _communicationSettingsProvider.GetMethodSettings(methodDefinition);
                if (methodSettings.IgnoreTransaction)
                {
                    ignoreTransaction = true;
                }
            }

            if (ignoreTransaction)
            {
                ExecuteAndAwaitInBackground(intent, proxyTask);
            }
            else
            {
                _transitionScope.CurrentMonitor.RegisterIntent(intent, proxyTask);
            }

            return(proxyTask);
        }
Пример #15
0
        public void HandleWhenAllAsDetectedContinuation(
            Task whenAllTask, Task[] awaitedTasks,
            ExecuteRoutineIntent awaitedRoutineIntent,
            ITransitionMonitor monitor)
        {
            ExecuteRoutineIntent executeWhenAllIntent;

            lock (whenAllTask)
            {
                var state = whenAllTask.AsyncState as WhenAllProxyTaskState;
                if (state == null)
                {
                    var whenAllIntentId = _numericIdGenerator.NewId();

                    Type itemType   = null;
                    var  resultType = whenAllTask.GetResultType();
                    if (resultType.IsArray)
                    {
                        itemType = resultType.GetElementType();
                    }

                    executeWhenAllIntent = new ExecuteRoutineIntent
                    {
                        Id = whenAllIntentId,

                        ServiceId = new ServiceId
                        {
                            ServiceName = awaitedRoutineIntent.ServiceId.ServiceName,
                            ProxyName   = nameof(IntrinsicRoutines)
                        },

                        MethodId = _routineMethodIdProvider.GetId(
                            IntrinsicRoutines.WhenAllMethodInfo),

                        Parameters = new WhenAllInputParameters
                        {
                            tasks   = awaitedTasks,
                            intents = new ExecuteRoutineIntent[awaitedTasks.Length],
                            // The task result must be an Array, e.g. string[]
                            itemType = itemType
                        }
                    };

                    state = new WhenAllProxyTaskState
                    {
                        IntentId             = whenAllIntentId,
                        ExecuteWhenAllIntent = executeWhenAllIntent
                    };
                    whenAllTask.SetAsyncState(state);

                    monitor.RegisterIntent(executeWhenAllIntent, whenAllTask);
                }
                else
                {
                    executeWhenAllIntent = state.ExecuteWhenAllIntent;
                }
            }

            var index = -1;

            for (var i = 0; i < awaitedTasks.Length; i++)
            {
                if (awaitedTasks[i].AsyncState is ProxyTaskState state &&
                    state.IntentId == awaitedRoutineIntent.Id)
                {
                    index = i;
                    break;
                }
            }
            var parameters = (WhenAllInputParameters)executeWhenAllIntent.Parameters;

            parameters.intents[index] = awaitedRoutineIntent;

            monitor.Context.ScheduledActions.ExecuteRoutineIntents.Remove(awaitedRoutineIntent);

            if (parameters.intents.All(i => i != null))
            {
                whenAllTask.SetAsyncState(new ProxyTaskState
                {
                    IntentId = executeWhenAllIntent.Id
                });
            }
        }
Пример #16
0
        private async void RunRoutineInBackground(IServiceDefinition serviceDefinition, ExecuteRoutineIntent intent)
        {
            try
            {
                var serviceInstance = _domainServiceProvider.GetService(serviceDefinition.Implementation);
                var routineMethod   = _routineMethodResolver.Resolve(serviceDefinition, intent.MethodId);
                var methodInvoker   = _methodInvokerFactory.Create(routineMethod);

                foreach (var postAction in _transitionActions)
                {
                    await postAction.OnRoutineStartAsync(serviceDefinition, intent.ServiceId, intent.MethodId, intent.Id);
                }

                Task task;
                try
                {
                    task = methodInvoker.Invoke(serviceInstance, intent.Parameters);
                    if (routineMethod.ReturnType != typeof(void))
                    {
                        try { await task; } catch { }
                    }
                }
                catch (Exception ex)
                {
                    if (ex is TargetInvocationException)
                    {
                        ex = ex.InnerException;
                    }
                    task = Task.FromException(ex);
                }
                var taskResult = task?.ToTaskResult() ?? new TaskResult();

                foreach (var postAction in _transitionActions)
                {
                    await postAction.OnRoutineCompleteAsync(serviceDefinition, intent.ServiceId, intent.MethodId, intent.Id, taskResult);
                }

                _routineCompletionSink.OnRoutineCompleted(intent.Id, taskResult);
            }
            catch
            {
            }
        }
Пример #17
0
        public async Task <HttpResponseMessage> ProcessRequestAsync(
            HttpRequestMessage request,
            FunctionExecutionContext context,
            DateTimeOffset requestStartTime,
            ILogger logger,
            CancellationToken ct)
        {
            _currentFunction.Value = new ExecutingFuncionInfo
            {
                FunctionName = context.FunctionName
            };

            try
            {
#warning Create an HTTP connector
                if (request.Method == HttpMethod.Post)
                {
                    string   serviceName = null;
                    string   routineName = null;
                    long?    intentId    = null;
                    bool     @volatile   = false;
                    TimeSpan pollTime    = TimeSpan.Zero;

                    foreach (var parameter in request.GetQueryNameValuePairs())
                    {
                        switch (parameter.Key)
                        {
                        case "service":
                            serviceName = parameter.Value;
                            break;

                        case "routine":
                            routineName = parameter.Value;
                            break;

                        case "intent":
                            if (long.TryParse(parameter.Value, out var parsedIntentId))
                            {
                                intentId = parsedIntentId;
                            }
                            break;
                        }
                    }

                    foreach (var header in request.Headers)
                    {
                        var firstValue = header.Value.FirstOrDefault();

                        switch (header.Key)
                        {
                        case "Volatile":
                            if (string.IsNullOrEmpty(firstValue) ||
                                !bool.TryParse(firstValue, out @volatile))
                            {
                                @volatile = true;
                            }
                            break;

                        case "Poll-Time":
                            if (!string.IsNullOrEmpty(firstValue))
                            {
                                if (double.TryParse(firstValue, out var pollTimeInSeconds))
                                {
                                    pollTime = TimeSpan.FromSeconds(pollTimeInSeconds);
                                }
                                else if (TimeSpan.TryParse(firstValue, out var pollTimeTimeSpan))
                                {
                                    pollTime = pollTimeTimeSpan;
                                }
                            }
                            break;
                        }
                    }

                    var serviceId = new ServiceId
                    {
                        ServiceName = serviceName
                    };

                    var routineMethodId = new RoutineMethodId
                    {
                        MethodName = routineName
                    };

                    var registration = _serviceRegistry.AllRegistrations
                                       .SingleOrDefault(r => r.ServiceName == serviceId.ServiceName);

                    if (registration == null || registration.IsExternal)
                    {
                        return new HttpResponseMessage(HttpStatusCode.BadRequest)
                               {
                                   Content = new StringContent(
                                       registration == null
                                ? "Service not found"
                                : "Cannot invoke external service")
                               }
                    }
                    ;

                    IValueContainer parameterContainer = null;
                    if (request.Content != null)
                    {
                        var routineMethod = _routineMethodResolver.Resolve(registration.ServiceType, routineMethodId);

                        var methodInvoker = _methodInvokerFactory.Create(routineMethod);
                        parameterContainer = methodInvoker.CreateParametersContainer();

#warning change to use a serializer based on the format
                        var paramsJson = await request.Content.ReadAsStringAsync();

                        if (!string.IsNullOrWhiteSpace(paramsJson))
                        {
                            JsonConvert.PopulateObject(paramsJson, parameterContainer);
                        }
                    }

                    var intent = new ExecuteRoutineIntent
                    {
#warning Generate intent ID from function request ID? any benefit (like on re-try)?
                        Id           = intentId ?? _idGenerator.NewId(),
                        ServiceId    = serviceId,
                        MethodId     = routineMethodId,
                        Parameters   = parameterContainer,
                        Continuation = null
                    };

                    var connector   = GetConnector(serviceId);
                    var routineInfo = await connector.ScheduleRoutineAsync(intent, ct);

                    if (pollTime > TimeSpan.Zero)
                    {
                        routineInfo = await PollRoutineAsync(routineInfo, connector, requestStartTime, pollTime, ct);
                    }

                    if (routineInfo.Result != null)
                    {
                        return(CreateResponseFromRoutineResult(routineInfo.Result));
                    }

                    var response = new HttpResponseMessage(HttpStatusCode.Accepted);

                    var location = $"{request.RequestUri.AbsolutePath}?service={serviceId.ServiceName}&routineId={routineInfo.RoutineId}";
                    response.Headers.Add("Location", location);

#warning ETag must be in certain format
                    //if (!string.IsNullOrEmpty(routineInfo.ETag))
                    //    response.Headers.ETag = new EntityTagHeaderValue(routineInfo.ETag);

                    return(response);
                }
                else if (request.Method == HttpMethod.Get)
                {
                    string   serviceName = null;
                    string   routineId   = null;
                    string   routineETag = null;
                    TimeSpan pollTime    = TimeSpan.Zero;

                    foreach (var parameter in request.GetQueryNameValuePairs())
                    {
                        switch (parameter.Key)
                        {
                        case "service":
                            serviceName = parameter.Value;
                            break;

                        case "routineId":
                            routineId = parameter.Value;
                            break;

                        case "routineTag":
                            routineETag = parameter.Value;
                            break;
                        }
                    }

                    foreach (var header in request.Headers)
                    {
                        var firstValue = header.Value.FirstOrDefault();

                        switch (header.Key)
                        {
                        case "Poll-Time":
                            if (!string.IsNullOrEmpty(firstValue))
                            {
                                if (double.TryParse(firstValue, out var pollTimeInSeconds))
                                {
                                    pollTime = TimeSpan.FromSeconds(pollTimeInSeconds);
                                }
                                else if (TimeSpan.TryParse(firstValue, out var pollTimeTimeSpan))
                                {
                                    pollTime = pollTimeTimeSpan;
                                }
                            }
                            break;
                        }
                    }

                    var serviceId = new ServiceId
                    {
                        ServiceName = serviceName
                    };

                    var routineInfo = new ActiveRoutineInfo
                    {
                        RoutineId = routineId,
                        ETag      = routineETag
                    };

                    var connector = GetConnector(serviceId);

                    routineInfo = await PollRoutineAsync(routineInfo, connector, requestStartTime, pollTime, ct);

                    if (routineInfo.Result != null)
                    {
                        return(CreateResponseFromRoutineResult(routineInfo.Result));
                    }

                    var response = new HttpResponseMessage(HttpStatusCode.NoContent);

#warning ETag must be in certain format
                    //if (!string.IsNullOrEmpty(routineInfo.ETag))
                    //    response.Headers.ETag = new EntityTagHeaderValue(routineInfo.ETag);

                    return(response);
                }
            }
            catch (Exception ex)
            {
                return(CreateInfrastructureErrorResponse(ex));
            }
            finally
            {
                _currentFunction.Value = default;
            }

            return(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
Пример #18
0
        public async Task <InvokeRoutineResult> InvokeAsync(ExecuteRoutineIntent intent)
        {
            var serviceRef = _serviceResolver.Resolve(intent.Service);
            var methodRef  = _methodResolver.Resolve(serviceRef.Definition, intent.Method);

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

            ICommunicator communicator = _communicatorProvider.GetCommunicator(serviceRef.Id, methodRef.Id);

            bool preferToRunInPlace = behaviorSettings.RunInPlace && serviceRef.Definition.Type != ServiceType.External;
            bool runInPlace         = preferToRunInPlace && (!behaviorSettings.Persistent ||
                                                             communicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish));

            var invocationData = InvocationDataUtils.CreateMethodInvocationData(intent,
                                                                                _transitionScope.IsActive ? _transitionScope.CurrentMonitor.Context : null);

            var resultValueType = methodRef.Definition.MethodInfo.ReturnType;

            if (resultValueType != typeof(void))
            {
                resultValueType = TaskAccessor.GetTaskResultType(resultValueType);
                if (resultValueType == TaskAccessor.VoidTaskResultType)
                {
                    resultValueType = typeof(void);
                }
            }

            if (runInPlace)
            {
                IMessageHandle messageHandle = null;
                if (behaviorSettings.Persistent)
                {
                    var preferences = new InvocationPreferences
                    {
                        LockMessage     = true,
                        ResultValueType = resultValueType
                    };

                    var invocationResult = await communicator.InvokeAsync(invocationData, preferences);

                    if (invocationResult.Outcome == InvocationOutcome.Complete)
                    {
                        return(invocationResult);
                    }
                    messageHandle = invocationResult.MessageHandle;
                }

                try
                {
                    var result = await _localMethodRunner.RunAsync(
                        invocationData,
                        RuntimeCommunicatorMessage.Instance);

                    if (messageHandle != null)
                    {
                        await messageHandle.Complete();
                    }

                    return(result);
                }
                catch (Exception ex) // TODO: infra exceptions? should not be there, right?
                {
                    if (messageHandle != null)
                    {
                        messageHandle.ReleaseLock();
                    }

                    return(new InvokeRoutineResult
                    {
                        Outcome = InvocationOutcome.Scheduled,
                        MessageHandle = messageHandle
                    });
                }
            }
            else
            {
                var preferences = new InvocationPreferences
                {
                    // TODO: check this option
                    //LockMessage = behaviorSettings.Resilient && communicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish),

                    Synchronous     = communicator.Traits.HasFlag(CommunicationTraits.SyncReplies),
                    ResultValueType = resultValueType
                };

                return(await communicator.InvokeAsync(invocationData, preferences));
            }
        }