From 76d4c8fa85eaa64b7aea803d3f19d97de6e960d5 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Wed, 12 Jan 2022 20:54:30 +0100 Subject: [PATCH 1/5] Use single subscriber tab for replies --- PettingZoo.Core/Connection/ISubscriber.cs | 2 +- .../ExportImport/ImportSubscriber.cs | 7 +- .../ExportImport/StreamProgressDecorator.cs | 2 +- .../RabbitMQClientSubscriber.cs | 5 +- .../TapetiClassLibraryExampleGenerator.cs | 1 - PettingZoo/TODO.md | 3 - PettingZoo/UI/Main/MainWindowViewModel.cs | 21 +++- PettingZoo/UI/Tab/ITabFactory.cs | 9 +- PettingZoo/UI/Tab/ITabHost.cs | 1 + .../UI/Tab/Publisher/IPublishDestination.cs | 2 +- .../UI/Tab/Publisher/PublisherViewModel.cs | 16 +-- .../UI/Tab/Publisher/RawPublisherViewModel.cs | 6 +- .../Tab/Publisher/TapetiPublisherViewModel.cs | 10 +- .../SameMessageVisibilityConverter.cs | 23 ---- .../UI/Tab/Subscriber/SubscriberView.xaml | 8 +- .../UI/Tab/Subscriber/SubscriberViewModel.cs | 58 ++++++++-- .../SubscriberViewStrings.Designer.cs | 9 ++ .../Tab/Subscriber/SubscriberViewStrings.resx | 3 + .../Tab/Undocked/UndockedTabHostViewModel.cs | 9 +- .../Undocked/UndockedTabHostWindow.xaml.cs | 4 +- PettingZoo/UI/Tab/ViewTab.cs | 9 +- PettingZoo/UI/Tab/ViewTabFactory.cs | 106 ++++++++++++++++-- 22 files changed, 216 insertions(+), 98 deletions(-) delete mode 100644 PettingZoo/UI/Tab/Subscriber/SameMessageVisibilityConverter.cs diff --git a/PettingZoo.Core/Connection/ISubscriber.cs b/PettingZoo.Core/Connection/ISubscriber.cs index e18c9e1..34ea83a 100644 --- a/PettingZoo.Core/Connection/ISubscriber.cs +++ b/PettingZoo.Core/Connection/ISubscriber.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace PettingZoo.Core.Connection { - public interface ISubscriber : IAsyncDisposable + public interface ISubscriber : IDisposable { string? QueueName { get; } string? Exchange {get; } diff --git a/PettingZoo.Core/ExportImport/ImportSubscriber.cs b/PettingZoo.Core/ExportImport/ImportSubscriber.cs index 067fee4..49ffaca 100644 --- a/PettingZoo.Core/ExportImport/ImportSubscriber.cs +++ b/PettingZoo.Core/ExportImport/ImportSubscriber.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading.Tasks; using PettingZoo.Core.Connection; namespace PettingZoo.Core.ExportImport @@ -13,7 +12,10 @@ namespace PettingZoo.Core.ExportImport public string? QueueName { get; } public string? Exchange => null; public string? RoutingKey => null; + + #pragma warning disable CS0067 // "The event ... is never used" - it's part of the interface so it's required. public event EventHandler? MessageReceived; + #pragma warning restore CS0067 public ImportSubscriber(string filename, IReadOnlyList messages) @@ -23,10 +25,9 @@ namespace PettingZoo.Core.ExportImport } - public ValueTask DisposeAsync() + public void Dispose() { GC.SuppressFinalize(this); - return default; } diff --git a/PettingZoo.Core/ExportImport/StreamProgressDecorator.cs b/PettingZoo.Core/ExportImport/StreamProgressDecorator.cs index 76ee710..b2abeaa 100644 --- a/PettingZoo.Core/ExportImport/StreamProgressDecorator.cs +++ b/PettingZoo.Core/ExportImport/StreamProgressDecorator.cs @@ -58,7 +58,7 @@ namespace PettingZoo.Core.ExportImport public StreamWrapper(StreamProgressDecorator owner, Stream decoratedStream) { this.owner = owner; - this.DecoratedStream = decoratedStream; + DecoratedStream = decoratedStream; } diff --git a/PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs b/PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs index 9da4068..98fb4f3 100644 --- a/PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs +++ b/PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using PettingZoo.Core.Connection; using RabbitMQ.Client; using RabbitMQ.Client.Events; @@ -29,14 +28,12 @@ namespace PettingZoo.RabbitMQ } - public ValueTask DisposeAsync() + public void Dispose() { GC.SuppressFinalize(this); if (model != null && consumerTag != null && model.IsOpen) model.BasicCancelNoWait(consumerTag); - - return default; } diff --git a/PettingZoo.Tapeti/TapetiClassLibraryExampleGenerator.cs b/PettingZoo.Tapeti/TapetiClassLibraryExampleGenerator.cs index 6cd0eae..60974e3 100644 --- a/PettingZoo.Tapeti/TapetiClassLibraryExampleGenerator.cs +++ b/PettingZoo.Tapeti/TapetiClassLibraryExampleGenerator.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; -using System.Windows.Threading; using PettingZoo.Core.Generator; using PettingZoo.Core.Settings; using PettingZoo.Tapeti.AssemblyLoader; diff --git a/PettingZoo/TODO.md b/PettingZoo/TODO.md index d8689f8..f0312f5 100644 --- a/PettingZoo/TODO.md +++ b/PettingZoo/TODO.md @@ -4,9 +4,6 @@ Should-have ----------- -- Single tab for responses, don't create a new subscriber tab for 1 message each time - Use the CorrelationId in the list for such cases instead of the routing key (which is the name of the dynamic queue for the responses). - Set the CorrelationId to the request routing key for example, so the different responses can be somewhat identified. Nice-to-have diff --git a/PettingZoo/UI/Main/MainWindowViewModel.cs b/PettingZoo/UI/Main/MainWindowViewModel.cs index 5117d11..e7c190a 100644 --- a/PettingZoo/UI/Main/MainWindowViewModel.cs +++ b/PettingZoo/UI/Main/MainWindowViewModel.cs @@ -170,7 +170,7 @@ namespace PettingZoo.UI.Main if (connectionSettings.Subscribe) { var subscriber = connection.Subscribe(connectionSettings.Exchange, connectionSettings.RoutingKey); - AddTab(tabFactory.CreateSubscriberTab(connection, subscriber)); + tabFactory.CreateSubscriberTab(connection, subscriber); } ConnectionChanged(); @@ -214,7 +214,7 @@ namespace PettingZoo.UI.Main subscribeDialogParams = newParams; var subscriber = connection.Subscribe(subscribeDialogParams.Exchange, subscribeDialogParams.RoutingKey); - AddTab(tabFactory.CreateSubscriberTab(connection, subscriber)); + tabFactory.CreateSubscriberTab(connection, subscriber); } @@ -223,7 +223,7 @@ namespace PettingZoo.UI.Main if (connection == null) return; - AddTab(tabFactory.CreatePublisherTab(connection)); + tabFactory.CreatePublisherTab(connection); } @@ -235,7 +235,8 @@ namespace PettingZoo.UI.Main private void CloseTabExecute() { - RemoveActiveTab(); + var tab = RemoveActiveTab(); + (tab as IDisposable)?.Dispose(); } @@ -300,8 +301,7 @@ namespace PettingZoo.UI.Main progressWindow.Close(); progressWindow = null; - AddTab(tabFactory.CreateSubscriberTab(connection, - new ImportSubscriber(filename, messages))); + tabFactory.CreateSubscriberTab(connection, new ImportSubscriber(filename, messages)); }); } catch (OperationCanceledException) @@ -377,6 +377,15 @@ namespace PettingZoo.UI.Main } + public void ActivateTab(ITab tab) + { + if (undockedTabs.TryGetValue(tab, out var window)) + window.Activate(); + else if (Tabs.Contains(tab)) + ActiveTab = tab; + } + + public void DockTab(ITab tab) { if (undockedTabs.Remove(tab, out var tabHostWindow)) diff --git a/PettingZoo/UI/Tab/ITabFactory.cs b/PettingZoo/UI/Tab/ITabFactory.cs index 6a744ae..78f5b8a 100644 --- a/PettingZoo/UI/Tab/ITabFactory.cs +++ b/PettingZoo/UI/Tab/ITabFactory.cs @@ -2,13 +2,10 @@ namespace PettingZoo.UI.Tab { - // Passing the closeTabCommand is necessary because I haven't figured out how to bind the main window's - // context menu items for the tab to the main window's datacontext yet. RelativeSource doesn't seem to work - // because the popup is it's own window. Refactor if a better solution is found. - public interface ITabFactory { - ITab CreateSubscriberTab(IConnection? connection, ISubscriber subscriber); - ITab CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null); + void CreateSubscriberTab(IConnection? connection, ISubscriber subscriber); + string CreateReplySubscriberTab(IConnection connection); + void CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null); } } diff --git a/PettingZoo/UI/Tab/ITabHost.cs b/PettingZoo/UI/Tab/ITabHost.cs index e73ec07..8428244 100644 --- a/PettingZoo/UI/Tab/ITabHost.cs +++ b/PettingZoo/UI/Tab/ITabHost.cs @@ -3,6 +3,7 @@ public interface ITabHost { void AddTab(ITab tab); + void ActivateTab(ITab tab); void DockTab(ITab tab); void UndockedTabClosed(ITab tab); diff --git a/PettingZoo/UI/Tab/Publisher/IPublishDestination.cs b/PettingZoo/UI/Tab/Publisher/IPublishDestination.cs index d4f5788..5d02b5c 100644 --- a/PettingZoo/UI/Tab/Publisher/IPublishDestination.cs +++ b/PettingZoo/UI/Tab/Publisher/IPublishDestination.cs @@ -5,6 +5,6 @@ string Exchange { get; } string RoutingKey { get; } - string? GetReplyTo(); + string? GetReplyTo(ref string? correlationId); } } diff --git a/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs b/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs index 724963c..303da36 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs @@ -21,7 +21,6 @@ namespace PettingZoo.UI.Tab.Publisher private readonly IConnection connection; private readonly IExampleGenerator exampleGenerator; private readonly ITabFactory tabFactory; - private readonly ITabHostProvider tabHostProvider; private bool sendToExchange = true; private string exchange = ""; @@ -156,12 +155,11 @@ namespace PettingZoo.UI.Tab.Publisher string IPublishDestination.RoutingKey => SendToExchange ? RoutingKey : Queue; - public PublisherViewModel(ITabHostProvider tabHostProvider, ITabFactory tabFactory, IConnection connection, IExampleGenerator exampleGenerator, ReceivedMessageInfo? fromReceivedMessage = null) + public PublisherViewModel(ITabFactory tabFactory, IConnection connection, IExampleGenerator exampleGenerator, ReceivedMessageInfo? fromReceivedMessage = null) { this.connection = connection; this.exampleGenerator = exampleGenerator; this.tabFactory = tabFactory; - this.tabHostProvider = tabHostProvider; publishCommand = new DelegateCommand(PublishExecute, PublishCanExecute); @@ -280,17 +278,13 @@ namespace PettingZoo.UI.Tab.Publisher } - public string? GetReplyTo() + public string? GetReplyTo(ref string? correlationId) { if (ReplyToSpecified) return string.IsNullOrEmpty(ReplyTo) ? null : ReplyTo; - var subscriber = connection.Subscribe(); - var tab = tabFactory.CreateSubscriberTab(connection, subscriber); - tabHostProvider.Instance.AddTab(tab); - - subscriber.Start(); - return subscriber.QueueName; + correlationId = SendToExchange ? RoutingKey : Queue; + return tabFactory.CreateReplySubscriberTab(connection); } @@ -305,7 +299,7 @@ namespace PettingZoo.UI.Tab.Publisher public class DesignTimePublisherViewModel : PublisherViewModel { - public DesignTimePublisherViewModel() : base(null!, null!, null!, null!) + public DesignTimePublisherViewModel() : base(null!, null!, null!) { } diff --git a/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs b/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs index 1396c1e..3e247f1 100644 --- a/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/RawPublisherViewModel.cs @@ -244,6 +244,8 @@ namespace PettingZoo.UI.Tab.Publisher var headers = Headers.Where(h => h.IsValid()).ToDictionary(h => h.Key, h => h.Value); + var publishCorrelationId = NullIfEmpty(CorrelationId); + var replyTo = publishDestination.GetReplyTo(ref publishCorrelationId); connection.Publish(new PublishMessageInfo( publishDestination.Exchange, @@ -254,12 +256,12 @@ namespace PettingZoo.UI.Tab.Publisher AppId = NullIfEmpty(AppId), ContentEncoding = NullIfEmpty(ContentEncoding), ContentType = NullIfEmpty(ContentType), - CorrelationId = NullIfEmpty(CorrelationId), + CorrelationId = publishCorrelationId, DeliveryMode = deliveryMode, Expiration = NullIfEmpty(Expiration), MessageId = NullIfEmpty(MessageId), Priority = priorityValue, - ReplyTo = publishDestination.GetReplyTo(), + ReplyTo = replyTo, Timestamp = timestampValue, Type = NullIfEmpty(TypeProperty), UserId = NullIfEmpty(UserId) diff --git a/PettingZoo/UI/Tab/Publisher/TapetiPublisherViewModel.cs b/PettingZoo/UI/Tab/Publisher/TapetiPublisherViewModel.cs index 3ae74b1..a3ce7af 100644 --- a/PettingZoo/UI/Tab/Publisher/TapetiPublisherViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/TapetiPublisherViewModel.cs @@ -2,7 +2,6 @@ using System.Text; using System.Windows; using System.Windows.Input; -using System.Windows.Threading; using PettingZoo.Core.Connection; using PettingZoo.Core.Generator; using PettingZoo.Core.Validation; @@ -151,7 +150,10 @@ namespace PettingZoo.UI.Tab.Publisher { return string.IsNullOrEmpty(value) ? null : value; } - + + var publishCorrelationId = NullIfEmpty(CorrelationId); + var replyTo = publishDestination.GetReplyTo(ref publishCorrelationId); + connection.Publish(new PublishMessageInfo( publishDestination.Exchange, publishDestination.RoutingKey, @@ -162,9 +164,9 @@ namespace PettingZoo.UI.Tab.Publisher }) { ContentType = @"application/json", - CorrelationId = NullIfEmpty(CorrelationId), + CorrelationId = publishCorrelationId, DeliveryMode = MessageDeliveryMode.Persistent, - ReplyTo = publishDestination.GetReplyTo() + ReplyTo = replyTo })); } diff --git a/PettingZoo/UI/Tab/Subscriber/SameMessageVisibilityConverter.cs b/PettingZoo/UI/Tab/Subscriber/SameMessageVisibilityConverter.cs deleted file mode 100644 index 4f6df58..0000000 --- a/PettingZoo/UI/Tab/Subscriber/SameMessageVisibilityConverter.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Data; - -namespace PettingZoo.UI.Tab.Subscriber -{ - public class SameMessageVisibilityConverter : IMultiValueConverter - { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) - { - return ReferenceEquals(values[0], values[1]) - ? Visibility.Visible - : Visibility.Collapsed; - } - - - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } - } -} diff --git a/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml b/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml index 7221422..3f3fd46 100644 --- a/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml +++ b/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml @@ -13,9 +13,6 @@ d:DesignWidth="800" d:DataContext="{d:DesignInstance res:DesignTimeSubscriberViewModel, IsDesignTimeCreatable=True}" Background="White"> - - - @@ -64,7 +61,10 @@ - + + + + diff --git a/PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs b/PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs index f62edab..b2e7044 100644 --- a/PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs +++ b/PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs @@ -21,10 +21,9 @@ using Timer = System.Threading.Timer; namespace PettingZoo.UI.Tab.Subscriber { - public class SubscriberViewModel : BaseViewModel, ITabToolbarCommands, ITabActivate + public class SubscriberViewModel : BaseViewModel, IDisposable, ITabToolbarCommands, ITabActivate { private readonly ILogger logger; - private readonly ITabHostProvider tabHostProvider; private readonly ITabFactory tabFactory; private readonly IConnection? connection; private readonly ISubscriber subscriber; @@ -76,16 +75,43 @@ namespace PettingZoo.UI.Tab.Subscriber set => SetField(ref selectedMessageProperties, value); } - public string Title => - (subscriber.Exchange != null ? $"{subscriber.Exchange} - {subscriber.RoutingKey}" : $"{subscriber.QueueName}") + - (tabActive || unreadCount == 0 ? "" : $" ({unreadCount})"); + public string Title + { + get + { + var title = new StringBuilder(); + + if (IsReplyTab) + title.Append(SubscriberViewStrings.ReplyTabTitle); + else if (subscriber.Exchange != null) + title.Append(subscriber.Exchange).Append(" - ").Append(subscriber.RoutingKey); + else + title.Append(subscriber.QueueName); + + if (!tabActive && unreadCount > 0) + title.Append(" (").Append(unreadCount).Append(')'); + + return title.ToString(); + } + } + + public IEnumerable ToolbarCommands => toolbarCommands; - public SubscriberViewModel(ILogger logger, ITabHostProvider tabHostProvider, ITabFactory tabFactory, IConnection? connection, ISubscriber subscriber, IExportImportFormatProvider exportImportFormatProvider) + public bool IsReplyTab { get; } + + // ReSharper disable UnusedMember.Global - used via BindingProxy + public Visibility StandardTabVisibility => !IsReplyTab ? Visibility.Visible : Visibility.Collapsed; + public Visibility ReplyTabVisibility => IsReplyTab ? Visibility.Visible : Visibility.Collapsed; + // ReSharper restore UnusedMember.Global + + + public SubscriberViewModel(ILogger logger, ITabFactory tabFactory, IConnection? connection, ISubscriber subscriber, IExportImportFormatProvider exportImportFormatProvider, bool isReplyTab) { + IsReplyTab = isReplyTab; + this.logger = logger; - this.tabHostProvider = tabHostProvider; this.tabFactory = tabFactory; this.connection = connection; this.subscriber = subscriber; @@ -111,6 +137,15 @@ namespace PettingZoo.UI.Tab.Subscriber subscriber.Start(); } + + public void Dispose() + { + GC.SuppressFinalize(this); + newMessageTimer?.Dispose(); + subscriber.Dispose(); + } + + private void ClearExecute() { Messages.Clear(); @@ -222,8 +257,7 @@ namespace PettingZoo.UI.Tab.Subscriber if (connection == null) return; - var publisherTab = tabFactory.CreatePublisherTab(connection, SelectedMessage); - tabHostProvider.Instance.AddTab(publisherTab); + tabFactory.CreatePublisherTab(connection, SelectedMessage); } @@ -320,7 +354,7 @@ namespace PettingZoo.UI.Tab.Subscriber public class DesignTimeSubscriberViewModel : SubscriberViewModel { - public DesignTimeSubscriberViewModel() : base(null!, null!, null!, null!, new DesignTimeSubscriber(), null!) + public DesignTimeSubscriberViewModel() : base(null!, null!, null!, new DesignTimeSubscriber(), null!, false) { for (var i = 1; i <= 5; i++) (i > 2 ? UnreadMessages : Messages).Add(new ReceivedMessageInfo( @@ -340,9 +374,9 @@ namespace PettingZoo.UI.Tab.Subscriber private class DesignTimeSubscriber : ISubscriber { - public ValueTask DisposeAsync() + public void Dispose() { - return default; + GC.SuppressFinalize(this); } diff --git a/PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.Designer.cs b/PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.Designer.cs index d8371cf..84084b6 100644 --- a/PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.Designer.cs +++ b/PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.Designer.cs @@ -185,5 +185,14 @@ namespace PettingZoo.UI.Tab.Subscriber { return ResourceManager.GetString("PropertyValue", resourceCulture); } } + + /// + /// Looks up a localized string similar to Replies. + /// + public static string ReplyTabTitle { + get { + return ResourceManager.GetString("ReplyTabTitle", resourceCulture); + } + } } } diff --git a/PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.resx b/PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.resx index 3e3a4a1..1af6fd8 100644 --- a/PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.resx +++ b/PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.resx @@ -159,4 +159,7 @@ Value + + Replies + \ No newline at end of file diff --git a/PettingZoo/UI/Tab/Undocked/UndockedTabHostViewModel.cs b/PettingZoo/UI/Tab/Undocked/UndockedTabHostViewModel.cs index 750d638..2f72da3 100644 --- a/PettingZoo/UI/Tab/Undocked/UndockedTabHostViewModel.cs +++ b/PettingZoo/UI/Tab/Undocked/UndockedTabHostViewModel.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows; @@ -13,6 +14,7 @@ namespace PettingZoo.UI.Tab.Undocked private readonly ITabHostProvider tabHostProvider; private readonly ITab tab; private readonly DelegateCommand dockCommand; + private bool docked; public string Title => tab.Title; @@ -43,13 +45,18 @@ namespace PettingZoo.UI.Tab.Undocked private void DockCommandExecute() { + docked = true; tabHostProvider.Instance.DockTab(tab); } public void WindowClosed() { + if (docked) + return; + tabHostProvider.Instance.UndockedTabClosed(tab); + (tab as IDisposable)?.Dispose(); } diff --git a/PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml.cs b/PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml.cs index d7c1572..d6fcdcb 100644 --- a/PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml.cs +++ b/PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml.cs @@ -1,7 +1,5 @@ -using System; -using System.Windows; +using System.Windows; using System.Windows.Controls; -using System.Windows.Threading; namespace PettingZoo.UI.Tab.Undocked { diff --git a/PettingZoo/UI/Tab/ViewTab.cs b/PettingZoo/UI/Tab/ViewTab.cs index 2ed0097..8fac45b 100644 --- a/PettingZoo/UI/Tab/ViewTab.cs +++ b/PettingZoo/UI/Tab/ViewTab.cs @@ -8,7 +8,7 @@ using System.Windows.Controls; namespace PettingZoo.UI.Tab { - public class ViewTab : ITab, ITabToolbarCommands, ITabActivate, ITabHostWindowNotify where TView : ContentControl where TViewModel : INotifyPropertyChanged + public class ViewTab : IDisposable, ITab, ITabToolbarCommands, ITabActivate, ITabHostWindowNotify where TView : ContentControl where TViewModel : INotifyPropertyChanged { public string Title => getTitle(viewModel); public ContentControl Content { get; } @@ -63,5 +63,12 @@ namespace PettingZoo.UI.Tab { (viewModel as ITabHostWindowNotify)?.HostWindowChanged(hostWindow); } + + + public void Dispose() + { + GC.SuppressFinalize(this); + (viewModel as IDisposable)?.Dispose(); + } } } diff --git a/PettingZoo/UI/Tab/ViewTabFactory.cs b/PettingZoo/UI/Tab/ViewTabFactory.cs index 2a82aeb..31e6e42 100644 --- a/PettingZoo/UI/Tab/ViewTabFactory.cs +++ b/PettingZoo/UI/Tab/ViewTabFactory.cs @@ -1,4 +1,6 @@ -using PettingZoo.Core.Connection; +using System; +using System.Collections.Generic; +using PettingZoo.Core.Connection; using PettingZoo.Core.ExportImport; using PettingZoo.Core.Generator; using PettingZoo.UI.Tab.Publisher; @@ -14,6 +16,10 @@ namespace PettingZoo.UI.Tab private readonly IExampleGenerator exampleGenerator; private readonly IExportImportFormatProvider exportImportFormatProvider; + // Not the cleanest way, but this factory itself can't be singleton without (justifyable) upsetting SimpleInjector + private static ISubscriber? replySubscriber; + private static ITab? replySubscriberTab; + public ViewTabFactory(ILogger logger, ITabHostProvider tabHostProvider, IExampleGenerator exampleGenerator, IExportImportFormatProvider exportImportFormatProvider) { @@ -24,23 +30,101 @@ namespace PettingZoo.UI.Tab } - public ITab CreateSubscriberTab(IConnection? connection, ISubscriber subscriber) + public void CreateSubscriberTab(IConnection? connection, ISubscriber subscriber) { - var viewModel = new SubscriberViewModel(logger, tabHostProvider, this, connection, subscriber, exportImportFormatProvider); - return new ViewTab( - new SubscriberView(viewModel), - viewModel, - vm => vm.Title); + InternalCreateSubscriberTab(connection, subscriber, false); } - - public ITab CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null) + + public string CreateReplySubscriberTab(IConnection connection) { - var viewModel = new PublisherViewModel(tabHostProvider, this, connection, exampleGenerator, fromReceivedMessage); - return new ViewTab( + if (replySubscriber?.QueueName != null && replySubscriberTab != null) + { + tabHostProvider.Instance.ActivateTab(replySubscriberTab); + return replySubscriber.QueueName; + } + + replySubscriber = new SubscriberDecorator(connection.Subscribe(), () => + { + replySubscriber = null; + replySubscriberTab = null; + }); + + replySubscriber.Start(); + + replySubscriberTab = InternalCreateSubscriberTab(connection, replySubscriber, true); + return replySubscriber.QueueName!; + } + + + public void CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null) + { + var viewModel = new PublisherViewModel(this, connection, exampleGenerator, fromReceivedMessage); + var tab = new ViewTab( new PublisherView(viewModel), viewModel, vm => vm.Title); + + tabHostProvider.Instance.AddTab(tab); + } + + + private ITab InternalCreateSubscriberTab(IConnection? connection, ISubscriber subscriber, bool isReplyTab) + { + var viewModel = new SubscriberViewModel(logger, this, connection, subscriber, exportImportFormatProvider, isReplyTab); + var tab = new ViewTab( + new SubscriberView(viewModel), + viewModel, + vm => vm.Title); + + tabHostProvider.Instance.AddTab(tab); + return tab; + } + + + + private class SubscriberDecorator : ISubscriber + { + private readonly ISubscriber decoratedSubscriber; + private readonly Action onDispose; + + + public string? QueueName => decoratedSubscriber.QueueName; + public string? Exchange => decoratedSubscriber.Exchange; + public string? RoutingKey => decoratedSubscriber.RoutingKey; + + public event EventHandler? MessageReceived; + + + public SubscriberDecorator(ISubscriber decoratedSubscriber, Action onDispose) + { + this.decoratedSubscriber = decoratedSubscriber; + this.onDispose = onDispose; + + decoratedSubscriber.MessageReceived += (sender, args) => + { + MessageReceived?.Invoke(sender, args); + }; + } + + + public void Dispose() + { + GC.SuppressFinalize(this); + decoratedSubscriber.Dispose(); + onDispose(); + } + + + public IEnumerable GetInitialMessages() + { + return decoratedSubscriber.GetInitialMessages(); + } + + public void Start() + { + decoratedSubscriber.Start(); + } } } } From 6bcc23a5d06dabf6fb2c7684e80f1e939ae2a7a4 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Wed, 12 Jan 2022 20:57:30 +0100 Subject: [PATCH 2/5] Add prefix to CorrelationId to better indicate it's a reply --- PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs | 2 +- .../UI/Tab/Publisher/PublisherViewStrings.Designer.cs | 9 +++++++++ PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx | 7 +++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs b/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs index 303da36..d26b84f 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs +++ b/PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs @@ -283,7 +283,7 @@ namespace PettingZoo.UI.Tab.Publisher if (ReplyToSpecified) return string.IsNullOrEmpty(ReplyTo) ? null : ReplyTo; - correlationId = SendToExchange ? RoutingKey : Queue; + correlationId = PublisherViewStrings.ReplyToCorrelationIdPrefix + (SendToExchange ? RoutingKey : Queue); return tabFactory.CreateReplySubscriberTab(connection); } diff --git a/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs b/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs index e26fe49..99ff390 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs +++ b/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs @@ -168,6 +168,15 @@ namespace PettingZoo.UI.Tab.Publisher { } } + /// + /// Looks up a localized string similar to Re: . + /// + public static string ReplyToCorrelationIdPrefix { + get { + return ResourceManager.GetString("ReplyToCorrelationIdPrefix", resourceCulture); + } + } + /// /// Looks up a localized string similar to Publish: {0}. /// diff --git a/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx b/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx index d28e7b3..9c10050 100644 --- a/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx +++ b/PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx @@ -112,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Publish @@ -153,6 +153,9 @@ Tapeti message + + Re: + Publish: {0} From 91725b2c0ddf1151a237527790515559a1e13ddf Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Fri, 14 Jan 2022 08:09:59 +0100 Subject: [PATCH 3/5] Fixed correlation Id overlapping routing key in subscriber view --- PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml b/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml index 3f3fd46..a6b3016 100644 --- a/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml +++ b/PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml @@ -61,10 +61,8 @@ - - - - + + From a4f85b0f07090ef5876740acc4c99efb1ef77a2f Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Fri, 14 Jan 2022 09:13:55 +0100 Subject: [PATCH 4/5] Update exchange/routingKey when selecting a Tapeti message class Slightly improved Raw/Tapeti switching UI --- .../Generator/IExampleGenerator.cs | 10 ++-- .../AssemblyParser/AssemblyParser.cs | 19 +++++++ PettingZoo.Tapeti/PettingZoo.Tapeti.csproj | 1 + .../ClassSelection/ClassSelectionViewModel.cs | 8 +++ PettingZoo.WPF/Style.xaml | 6 +++ PettingZoo/Images/RabbitMQ.svg | 6 +++ PettingZoo/Images/Tapeti.png | Bin 0 -> 5620 bytes PettingZoo/PettingZoo.csproj | 4 ++ .../UI/Tab/Publisher/IPublishDestination.cs | 1 + .../UI/Tab/Publisher/PublisherView.xaml | 50 +++++++++++------- .../UI/Tab/Publisher/PublisherViewModel.cs | 7 +++ .../Tab/Publisher/TapetiPublisherViewModel.cs | 3 ++ 12 files changed, 92 insertions(+), 23 deletions(-) create mode 100644 PettingZoo/Images/RabbitMQ.svg create mode 100644 PettingZoo/Images/Tapeti.png diff --git a/PettingZoo.Core/Generator/IExampleGenerator.cs b/PettingZoo.Core/Generator/IExampleGenerator.cs index f8fdf44..b39e313 100644 --- a/PettingZoo.Core/Generator/IExampleGenerator.cs +++ b/PettingZoo.Core/Generator/IExampleGenerator.cs @@ -17,11 +17,13 @@ namespace PettingZoo.Core.Generator public interface IClassTypeExample : IExample { - public string AssemblyName { get; } - public string? Namespace { get; } - public string ClassName { get; } + string AssemblyName { get; } + string? Namespace { get; } + string ClassName { get; } - public string FullClassName => (!string.IsNullOrEmpty(Namespace) ? Namespace + "." : "") + ClassName; + string FullClassName => (!string.IsNullOrEmpty(Namespace) ? Namespace + "." : "") + ClassName; + + bool TryGetPublishDestination(out string exchange, out string routingKey); } diff --git a/PettingZoo.Tapeti/AssemblyParser/AssemblyParser.cs b/PettingZoo.Tapeti/AssemblyParser/AssemblyParser.cs index 50c5168..14be17f 100644 --- a/PettingZoo.Tapeti/AssemblyParser/AssemblyParser.cs +++ b/PettingZoo.Tapeti/AssemblyParser/AssemblyParser.cs @@ -7,6 +7,7 @@ using System.Runtime.Loader; using Newtonsoft.Json; using PettingZoo.Core.Generator; using PettingZoo.Core.Validation; +using Tapeti.Default; namespace PettingZoo.Tapeti.AssemblyParser { @@ -83,6 +84,24 @@ namespace PettingZoo.Tapeti.AssemblyParser } + public bool TryGetPublishDestination(out string exchange, out string routingKey) + { + try + { + // Assume default strategies are used + exchange = new NamespaceMatchExchangeStrategy().GetExchange(type); + routingKey = new TypeNameRoutingKeyStrategy().GetRoutingKey(type); + return true; + } + catch + { + exchange = ""; + routingKey = ""; + return false; + } + } + + public bool CanValidate() { return InitializeValidation(); diff --git a/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj b/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj index 3bf7adf..68c879d 100644 --- a/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj +++ b/PettingZoo.Tapeti/PettingZoo.Tapeti.csproj @@ -17,6 +17,7 @@ + diff --git a/PettingZoo.Tapeti/UI/ClassSelection/ClassSelectionViewModel.cs b/PettingZoo.Tapeti/UI/ClassSelection/ClassSelectionViewModel.cs index 51f8623..f17c497 100644 --- a/PettingZoo.Tapeti/UI/ClassSelection/ClassSelectionViewModel.cs +++ b/PettingZoo.Tapeti/UI/ClassSelection/ClassSelectionViewModel.cs @@ -300,6 +300,14 @@ namespace PettingZoo.Tapeti.UI.ClassSelection { return ""; } + + + public bool TryGetPublishDestination(out string exchange, out string routingKey) + { + exchange = ""; + routingKey = ""; + return false; + } } } } diff --git a/PettingZoo.WPF/Style.xaml b/PettingZoo.WPF/Style.xaml index 96fa4f5..4a53851 100644 --- a/PettingZoo.WPF/Style.xaml +++ b/PettingZoo.WPF/Style.xaml @@ -67,6 +67,12 @@ + +