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