/// <summary> /// Analyzes the response path of a two way send port to build the route back to the topic channel. /// </summary> /// <param name="intermediaryKeyPrefix">The prefix for the intermediary key.</param> /// <param name="sourceApplication">The application object in the source model.</param> /// <param name="targetApplication">The application object in the target model.</param> /// <param name="sendPortSource">The send port source.</param> /// <param name="sendPort">The send port resource item.</param> /// <param name="endpointAdapter">The endpoint adapter which triggers the response.</param> private void AnalyzeSendPortResponse(string intermediaryKeyPrefix, ResourceItem sourceApplication, Application targetApplication, SendPort sendPortSource, ResourceItem sendPort, MessagingObject endpointAdapter) { _logger.LogDebug(TraceMessages.SendPortIsTwoWay, RuleName, sendPort.Name); // Format the keys. var applicationName = targetApplication.Name.FormatKey(); var sendPortName = sendPort.Name.FormatKey(); // Set the scenario name on the endpoint adapter. endpointAdapter.Properties.Add(ModelConstants.ScenarioName, $"{applicationName}.{sendPortName}.Response"); var route = new List <MessagingObject> { endpointAdapter }; // Create the intermediaries for the pipeline route.AddRange(CreateReceivePipelineIntermediaries(intermediaryKeyPrefix, sourceApplication, sendPortSource.ReceivePipeline, sendPortSource.ReceivePipelineCustomConfiguration)); // If there is a map, create the intermediary if (sendPortSource.InboundTransforms != null && sendPortSource.InboundTransforms.Any()) { // Find map resource items var transforms = sendPort.FindRelatedResourcesByType(Model, ResourceRelationshipType.ReferencesTo, ModelConstants.ResourceMap); // Add to route var intermediary = CreateMapIntermediary(intermediaryKeyPrefix, targetApplication, transforms); if (intermediary != null) { route.Add(intermediary); } } else { _logger.LogTrace(TraceMessages.NoReceiveMapSpecifiedOnSendPort, RuleName, sendPortSource.Name); } // Create the message agent intermediaries route.AddRange(CreateMessageAgentIntermediaries(intermediaryKeyPrefix, _messageBoxChannelKey, false, null)); // Binds the route by adding routing slip router intermediaries between the intermediaries in the response from the send port var boundRoute = BindResponseRoute(intermediaryKeyPrefix, targetApplication, sendPortSource, route); // Binds the channels between the endpoint and intermediaries up to the message box (topic channel) BindResponseChannels(intermediaryKeyPrefix, targetApplication, sendPortSource, boundRoute); }
protected override async Task AnalyzePortsAsync(ResourceItem sourceApplication, Application targetApplication, CancellationToken token) { _logger.LogDebug(TraceMessages.AnalyzingSendPortScenarios, RuleName, sourceApplication.Name); var scenarios = 0; var applicationName = targetApplication.Name.FormatKey(); // TODO: Handle distribution lists // Find send ports in application var sendPorts = sourceApplication.FindRelatedResourcesByType(Model, ResourceRelationshipType.Child, ModelConstants.ResourceSendPort); foreach (var sendPort in sendPorts) { var sendPortSource = (SendPort)sendPort.SourceObject; var sendPortName = sendPort.Name.FormatKey(); // Is there a pipeline? if (sendPortSource.TransmitPipeline != null) { var route = new List <MessagingObject>(); var keyPrefix = $"{Model.MigrationTarget.MessageBus.Key}:{applicationName}:{sendPortName}"; // Create message subscriber route.Add(CreateMessageSubscriberIntermediary(keyPrefix, targetApplication, sendPortSource)); // If there is a map, create the intermediary if (sendPortSource.Transforms != null && sendPortSource.Transforms.Any()) { // Find map resource items var transforms = sendPort.FindRelatedResourcesByType(Model, ResourceRelationshipType.ReferencesTo, ModelConstants.ResourceMap); // Add to route var intermediary = CreateMapIntermediary(keyPrefix, targetApplication, transforms); if (intermediary != null) { route.Add(intermediary); } } else { _logger.LogTrace(TraceMessages.NoSendMapSpecifiedOnSendPort, RuleName, sendPortSource.Name); } // Create the intermediaries for the pipeline route.AddRange(CreateSendPipelineIntermediaries(keyPrefix, sourceApplication, sendPortSource.TransmitPipeline, sendPortSource.SendPipelineCustomConfiguration)); // Create endpoint adapter var endpointAdapter = CreateSendEndpoint(keyPrefix, sendPortSource); route.Add(endpointAdapter); // Binds the route by adding routing slip router intermediaries between the intermediaries and the endpoint in the send port var boundRoute = BindRoute(keyPrefix, targetApplication, sendPortSource, route); // Binds the channels between the message box (topic channel), intermediaries up to the endpoint (send adapter) var topicChannelKey = _messageBoxChannelKey; BindChannels(keyPrefix, topicChannelKey, targetApplication, sendPortSource, boundRoute); // Add a new route for interchange batch completion handling, if required var handleBatches = route.Any(s => s.Properties.ContainsKey(ModelConstants.HandleBatches) && (bool)s.Properties[ModelConstants.HandleBatches]); if (handleBatches) { // Need to add interchange aggregation for individual messages that are to be sent as a batch, as would be // done in an orchestration by building a batch of messages and calling send pipeline inline. Instead, this // adds an aggregator to the model which receives messages from an interchange queue and which then feeds the // batch to the start of the send pipeline intermediaries. BuildInterchangeAggregationRoute(keyPrefix, sourceApplication, targetApplication, sendPortSource); } // If port is two way, check for reverse path if (sendPortSource.IsTwoWay) { AnalyzeSendPortResponse(keyPrefix, sourceApplication, targetApplication, sendPortSource, sendPort, endpointAdapter); } scenarios++; } else { _logger.LogError(ErrorMessages.TransmitPipelineNotSetInSendPort, sendPort.Name); Context.Errors.Add(new ErrorMessage(string.Format(CultureInfo.CurrentCulture, ErrorMessages.TransmitPipelineNotSetInSendPort, sendPort.Name))); } } if (scenarios > 0) { _logger.LogDebug(TraceMessages.FoundSendPortScenariosInApplication, RuleName, scenarios, sourceApplication.Name); } else { _logger.LogDebug(TraceMessages.NoSendPortsFoundInApplication, RuleName, sourceApplication.Name); } await Task.CompletedTask.ConfigureAwait(false); }
protected override async Task AnalyzePortsAsync(ResourceItem sourceApplication, Application targetApplication, CancellationToken token) { _logger.LogDebug(TraceMessages.AnalyzingReceivePortScenarios, RuleName, sourceApplication.Name); var scenarios = 0; var applicationName = targetApplication.Name.FormatKey(); // Find receive ports in application var receivePorts = sourceApplication.FindRelatedResourcesByType(Model, ResourceRelationshipType.Child, ModelConstants.ResourceReceivePort); foreach (var receivePort in receivePorts) { var receivePortSource = (ReceivePort)receivePort.SourceObject; var receivePortName = receivePort.Name.FormatKey(); // Find receive locations in receive port var receiveLocations = receivePort.Resources.Where(r => r.Type == ModelConstants.ResourceReceiveLocation); if (receiveLocations != null && receiveLocations.Any()) { foreach (var receiveLocation in receiveLocations) { var receiveLocationSource = (ReceiveLocation)receiveLocation.SourceObject; var receiveLocationName = receiveLocation.Name.FormatKey(); // Is there a pipeline? if (receiveLocationSource.ReceivePipeline != null) { var route = new List <MessagingObject>(); var keyPrefix = $"{Model.MigrationTarget.MessageBus.Key}:{applicationName}:{receivePortName}:{receiveLocationName}"; // Create endpoint adapter route.Add(CreateReceiveEndpoint(keyPrefix, sourceApplication, targetApplication, receivePortSource, receiveLocationSource)); // Create the intermediaries for the pipeline route.AddRange(CreateReceivePipelineIntermediaries(keyPrefix, sourceApplication, receiveLocationSource.ReceivePipeline, receiveLocationSource.ReceivePipelineCustomConfiguration)); // If there is a map, create the intermediary if (receivePortSource.Transforms != null && receivePortSource.Transforms.Any()) { // Find map resource items var transforms = receivePort.FindRelatedResourcesByType(Model, ResourceRelationshipType.ReferencesTo, ModelConstants.ResourceMap); // Add to route var intermediary = CreateMapIntermediary(keyPrefix, targetApplication, transforms); if (intermediary != null) { route.Add(intermediary); } } else { _logger.LogTrace(TraceMessages.NoReceiveMapSpecifiedOnReceivePort, RuleName, receivePortSource.Name, receiveLocationSource.Name); } // Check the route for interchange batch handling var handleBatches = route.Any(s => s.Properties.ContainsKey(ModelConstants.HandleBatches) && (bool)s.Properties[ModelConstants.HandleBatches]); if (handleBatches) { // Need to add intermediaries for batch handling (regardless of recoverable interchange processing, all routes will // go through the content based router, but if batch failures must be handled as a unit, then messages in an // interchange will go via a queue used to aggregate messages back into a batch to see if the batch failed, before // being split again and sent to the message box topic. route.AddRange(CreateInterchangeHandlingIntermediaries(keyPrefix)); } // Create the message agent intermediaries route.AddRange(CreateMessageAgentIntermediaries(keyPrefix, _messageBoxChannelKey, false, null)); // Binds the route by adding routing slip router intermediaries between the intermediaries in the receive port var boundRoute = BindRoute(keyPrefix, targetApplication, receivePortSource, route); // Binds the channels between the endpoint and intermediaries up to the message box (topic channel) BindChannels(keyPrefix, targetApplication, receivePortSource, boundRoute); // Add a new route for interchange batch completion handling, if required if (handleBatches) { // Need to add interchange aggregation for batch failure handling as an atomic unit (recoverable interchange // processing = false). If batch succeeds and the messages in the batch can be published, then it goes back through // a splitter for the messages to be published individually to the message box, otherwise the aggregated message is // sent to the suspend queue channel. The aggregator is an Activator intermediary as it triggers off the interchange // channel, so this is treated as its own route with routing slip. In this instance, the content based router won't // route to the content promoter and the route ends with the main scenario route before it gets to the content promoter. BuildInterchangeAggregationRoute(keyPrefix, targetApplication, receivePortSource, receiveLocationSource); } // If port is two way, check for reverse path and add a new route if required. if (receivePortSource.IsTwoWay) { AnalyzeReceivePortResponse(keyPrefix, sourceApplication, targetApplication, receivePortSource, receiveLocation); } scenarios++; } else { _logger.LogError(ErrorMessages.ReceivePipelineNotSetInReceiveLocation, receiveLocation.Name); Context.Errors.Add(new ErrorMessage(string.Format(CultureInfo.CurrentCulture, ErrorMessages.ReceivePipelineNotSetInReceiveLocation, receiveLocation.Name))); } } } } if (scenarios > 0) { _logger.LogDebug(TraceMessages.FoundReceivePortScenariosInApplication, RuleName, scenarios, sourceApplication.Name); } else { _logger.LogDebug(TraceMessages.NoReceivePortsFoundInApplication, RuleName, sourceApplication.Name); } await Task.CompletedTask.ConfigureAwait(false); }