From 36f9d10ecec82648ad86a94d317a9cc7249e4b45 Mon Sep 17 00:00:00 2001 From: Jeesu Choi Date: Sat, 15 Jun 2024 08:16:34 +0900 Subject: [PATCH] Revert "Apply code style" --- .editorconfig | 99 +---- .vscode/settings.json | 5 - Directory.Build.props | 26 -- Menees.Analyzers.Settings.xml | 5 - .../ClientContext.cs | 29 +- src/JSSoft.Communication.Client/Program.cs | 25 +- .../Services/DataService.cs | 33 +- .../Services/UserService.cs | 115 +++--- src/JSSoft.Communication.Server/Program.cs | 25 +- .../ServerContext.cs | 27 +- .../Services/DataService.cs | 53 ++- .../Services/User.cs | 22 -- .../Services/UserInfo.cs | 38 ++ .../Services/UserService.cs | 322 +++++++-------- src/JSSoft.Communication/AdaptorProvider.cs | 45 ++- src/JSSoft.Communication/ClientContext.cs | 27 +- src/JSSoft.Communication/ClientService.cs | 42 +- src/JSSoft.Communication/EndPointUtility.cs | 32 +- .../Extensions/ISerializerExtensions.cs | 34 +- .../Extensions/IServiceContextExtensions.cs | 30 +- .../Grpc/AdaptorClient.cs | 369 +++++++----------- .../Grpc/AdaptorClientImpl.cs | 38 +- .../Grpc/AdaptorServer.cs | 224 +++++------ .../Grpc/AdaptorServerImpl.cs | 34 +- src/JSSoft.Communication/Grpc/CallbackData.cs | 8 - src/JSSoft.Communication/Grpc/Peer.cs | 34 +- .../Grpc/PeerCollection.cs | 33 +- src/JSSoft.Communication/IAdaptor.cs | 39 +- src/JSSoft.Communication/IAdaptorProvider.cs | 32 +- src/JSSoft.Communication/IInstanceContext.cs | 25 +- .../ILGeneratorExtensions.cs | 44 ++- src/JSSoft.Communication/IPeer.cs | 25 +- src/JSSoft.Communication/ISerializer.cs | 25 +- .../ISerializerProvider.cs | 29 +- src/JSSoft.Communication/IService.cs | 25 +- src/JSSoft.Communication/IServiceContext.cs | 45 ++- src/JSSoft.Communication/InstanceBase.cs | 167 +++----- .../InstanceCollection.cs | 27 +- src/JSSoft.Communication/InstanceContext.cs | 39 +- .../InstanceMethodAttribute.cs | 25 +- src/JSSoft.Communication/InvokeOptions.cs | 19 - src/JSSoft.Communication/InvokeResult.cs | 17 - src/JSSoft.Communication/JsonSerializer.cs | 35 +- .../JsonSerializerProvider.cs | 33 +- .../Logging/ConsoleLogger.cs | 29 +- .../Logging/EmptyLogger.cs | 29 +- src/JSSoft.Communication/Logging/ILogger.cs | 25 +- .../Logging/ILoggerExtensions.cs | 25 +- src/JSSoft.Communication/Logging/LogLevel.cs | 25 +- .../Logging/LogUtility.cs | 39 +- .../Logging/TraceLogger.cs | 31 +- src/JSSoft.Communication/MethodDescriptor.cs | 130 +++--- .../MethodDescriptorCollection.cs | 31 +- src/JSSoft.Communication/MethodUtility.cs | 52 +-- src/JSSoft.Communication/PeerDescriptor.cs | 35 +- src/JSSoft.Communication/ServerContext.cs | 25 +- src/JSSoft.Communication/ServerService.cs | 44 ++- src/JSSoft.Communication/ServiceAttribute.cs | 27 +- src/JSSoft.Communication/ServiceBase.cs | 35 +- src/JSSoft.Communication/ServiceCollection.cs | 41 +- .../ServiceContextAttribute.cs | 27 +- .../ServiceContextBase.cs | 188 ++++----- .../ServiceContractAttribute.cs | 25 +- .../ServiceInstanceBuilder.cs | 144 ++++--- src/JSSoft.Communication/ServiceState.cs | 25 +- src/JSSoft.Communication/ServiceToken.cs | 32 +- src/JSSoft.Communication/ServiceUtility.cs | 68 ++-- src/JSSoft.Communication/TaskUtility.cs | 30 +- .../Threading/Dispatcher.cs | 112 ++++-- .../Threading/DispatcherScheduler.cs | 73 ++-- .../DispatcherSynchronizationContext.cs | 30 -- .../UnreachableException.cs | 25 +- .../Application.cs | 274 ++++++------- .../ApplicationOptions.cs | 29 +- .../CommandContext.cs | 37 +- .../Commands/CloseCommand.cs | 39 +- .../Commands/DataCommand.cs | 42 +- .../Commands/ExitCommand.cs | 38 +- .../Commands/HelpCommand.cs | 7 +- .../Commands/LoginCommand.cs | 50 ++- .../Commands/LogoutCommand.cs | 37 +- .../Commands/OpenCommand.cs | 35 +- .../Commands/UserCommand.cs | 105 ++--- .../Commands/VersionCommand.cs | 7 +- .../IApplication.cs | 31 +- .../SystemTerminal.cs | 39 +- .../JSSoft.Communication.Services.projitems | 10 +- .../Services/Authority.cs | 38 +- .../Services/IDataService.cs | 25 +- .../Services/INotifyUserService.cs | 27 +- .../Services/IUserCallback.cs | 37 +- .../Services/IUserService.cs | 41 +- .../Services/UserAuthorityEventArgs.cs | 29 +- .../Services/UserAuthorityOptions.cs | 17 - .../Services/UserCreateOptions.cs | 19 - .../Services/UserDeleteOptions.cs | 15 - .../Services/UserEventArgs.cs | 31 +- .../Services/UserInfo.cs | 15 - .../Services/UserLoginOptions.cs | 13 - .../Services/UserMessageEventArgs.cs | 25 +- .../Services/UserNameEventArgs.cs | 29 +- .../Services/UserRenameOptions.cs | 15 - .../Services/UserSendMessageOptions.cs | 17 - stylecop.json | 18 - .../CallbackTest.cs | 107 ++--- .../ClientContextTest.cs | 73 ++-- .../ClientTestBase.cs | 44 ++- .../DispatcherTest.cs | 46 ++- .../EventTestUtility.cs | 36 +- .../Exceptions/ArgumentExceptionTest.cs | 25 +- .../Exceptions/ArgumentNullExceptionTest.cs | 25 +- .../ArgumentOutOfRangeExceptionTest.cs | 25 +- .../Exceptions/ExceptionTestBase.cs | 57 +-- .../IndexOutOfRangeExceptionTest.cs | 25 +- .../InvalidOperationExceptionTest.cs | 25 +- .../Exceptions/NotSupportedExceptionTest.cs | 25 +- .../Exceptions/NullReferenceExceptionTest.cs | 25 +- .../Exceptions/ObjectDisposedExceptionTest.cs | 25 +- .../Extensions/ServiceContextExtensions.cs | 29 +- .../GlobalUsings.cs | 27 +- test/JSSoft.Communication.Tests/InvokeTest.cs | 80 ++-- .../RandomEndPoint.cs | 33 +- .../ServerContextTest.cs | 66 +++- test/JSSoft.Communication.Tests/TestLogger.cs | 25 +- test/JSSoft.Communication.Tests/UnitTest1.cs | 81 ++-- 125 files changed, 3421 insertions(+), 2501 deletions(-) delete mode 100644 Menees.Analyzers.Settings.xml delete mode 100644 src/JSSoft.Communication.Server/Services/User.cs create mode 100644 src/JSSoft.Communication.Server/Services/UserInfo.cs delete mode 100644 src/JSSoft.Communication/Grpc/CallbackData.cs delete mode 100644 src/JSSoft.Communication/InvokeOptions.cs delete mode 100644 src/JSSoft.Communication/InvokeResult.cs delete mode 100644 src/JSSoft.Communication/Threading/DispatcherSynchronizationContext.cs delete mode 100644 src/Sharing/JSSoft.Communication.Services/Services/UserAuthorityOptions.cs delete mode 100644 src/Sharing/JSSoft.Communication.Services/Services/UserCreateOptions.cs delete mode 100644 src/Sharing/JSSoft.Communication.Services/Services/UserDeleteOptions.cs delete mode 100644 src/Sharing/JSSoft.Communication.Services/Services/UserInfo.cs delete mode 100644 src/Sharing/JSSoft.Communication.Services/Services/UserLoginOptions.cs delete mode 100644 src/Sharing/JSSoft.Communication.Services/Services/UserRenameOptions.cs delete mode 100644 src/Sharing/JSSoft.Communication.Services/Services/UserSendMessageOptions.cs delete mode 100644 stylecop.json diff --git a/.editorconfig b/.editorconfig index 5b17aa8..879a997 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,89 +1,28 @@ -# https://editorconfig.org/ -root = true +[*.{cs,vb}] -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -tab_width = 8 -trim_trailing_whitespace = true -max_line_length = 80 -indent_style = space -continuation_indent_size = 4 -indent_size = 4 +# IDE0060: 사용하지 않는 매개 변수를 제거하세요. +dotnet_code_quality_unused_parameters = non_public:none -[*.{json,ps1,sh,yaml,yml}] -indent_size = 2 -continuation_indent_size = 2 +# IDE0003: 한정자 제거 +dotnet_style_qualification_for_event = false:none -[*.{csproj,xml}] -indent_size = 2 -quote_type = double +# IDE0003: 한정자 제거 +dotnet_style_qualification_for_field = false:none -[*.cs] -max_line_length = 100 -curly_bracket_next_line = true -spaces_around_operators = true -indent_brace_style = Allman -dotnet_naming_rule.public_members_must_be_capitalized.symbols = public_symbols -dotnet_naming_symbols.public_symbols.applicable_kinds = property,method,field,event,delegate -dotnet_naming_symbols.public_symbols.applicable_accessibilities = public -dotnet_naming_rule.public_members_must_be_capitalized.style = first_word_upper_case_style -dotnet_naming_style.first_word_upper_case_style.capitalization = first_word_upper -dotnet_naming_rule.public_members_must_be_capitalized.severity = warning +# IDE0003: 한정자 제거 +dotnet_style_qualification_for_method = false:none -# SA0001: XML comment analysis is disabled due to project configuration -dotnet_diagnostic.SA0001.severity = none +# IDE0003: 한정자 제거 +dotnet_style_qualification_for_property = false:none -# SA1101: Prefix local calls with this -dotnet_diagnostic.SA1101.severity = none +# IDE0059: 불필요한 값 할당 +csharp_style_unused_value_assignment_preference = unused_local_variable:suggestion -# SA1309: Field names should not begin with underscore -dotnet_diagnostic.SA1309.severity = none +# IDE0056: 인덱스 연산자 사용 +csharp_style_prefer_index_operator = true:silent -# SA1600: Elements should be documented -dotnet_diagnostic.SA1600.severity = none +# IDE0032: auto 속성 사용 +dotnet_style_prefer_auto_properties = true:suggestion -# S1133: Deprecated code should be removed -dotnet_diagnostic.S1133.severity = none - -# S1125: Boolean literals should not be redundant -dotnet_diagnostic.S1125.severity = none - -# S2372: Exceptions should not be thrown from property getters -dotnet_diagnostic.S2372.severity = none - -# S3881: "IDisposable" should be implemented correctly -dotnet_diagnostic.S3881.severity = none - -# S3971: "GC.SuppressFinalize" should not be called -dotnet_diagnostic.S3971.severity = none - -# S4143: Collection elements should not be replaced unconditionally -dotnet_diagnostic.S4143.severity = none - -# S6605: Collection-specific "Exists" method should be used instead of the "Any" extension -dotnet_diagnostic.S6605.severity = none - -# S6608: Prefer indexing instead of "Enumerable" methods on types implementing "IList" -dotnet_diagnostic.S6608.severity = none - -# MEN007: Use a single return -dotnet_diagnostic.MEN007.severity = none - -# MEN016: Avoid top-level statements -dotnet_diagnostic.MEN016.severity = none - -[*.csproj] -quote_type = double - -[*.sln] -indent_style = tab -indent_size = 2 - -[hooks/*] -indent_size = 2 -continuation_indent_size = 2 - -[stylecop.json] -max_line_length = +# IDE0057: 범위 연산자 사용 +csharp_style_prefer_range_operator = false diff --git a/.vscode/settings.json b/.vscode/settings.json index cad5ced..b460f96 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,4 @@ { - "[csharp]": { - "editor.rulers": [ - 100 - ] - }, "files.exclude": { ".vs": true, "**/bin": true, diff --git a/Directory.Build.props b/Directory.Build.props index b9dfd85..cf60c78 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -21,31 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - - - - - all - - runtime; build; native; contentfiles; analyzers - - - - all - runtime; build; native; contentfiles; analyzers - - - all - runtime; build; native; contentfiles; analyzers - - - - - Menees.Analyzers.Settings.xml - - - net6.0;net7.0;net8.0;netstandard2.1 net8.0 @@ -75,10 +51,8 @@ SOFTWARE. --> communication $(MSBuildThisFileDirectory).build/public.snk - - \ No newline at end of file diff --git a/Menees.Analyzers.Settings.xml b/Menees.Analyzers.Settings.xml deleted file mode 100644 index fdd5b33..0000000 --- a/Menees.Analyzers.Settings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - 100 - 200 - diff --git a/src/JSSoft.Communication.Client/ClientContext.cs b/src/JSSoft.Communication.Client/ClientContext.cs index c4e85ba..4029d11 100644 --- a/src/JSSoft.Communication.Client/ClientContext.cs +++ b/src/JSSoft.Communication.Client/ClientContext.cs @@ -1,15 +1,32 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System.ComponentModel.Composition; -namespace JSSoft.Communication.Client; +namespace JSSoft.Communication.ConsoleApp; [Export(typeof(IServiceContext))] [method: ImportingConstructor] -internal sealed class ClientContext([ImportMany] IService[] services) +sealed class ClientContext([ImportMany] IService[] services) : Communication.ClientContext(services) { } diff --git a/src/JSSoft.Communication.Client/Program.cs b/src/JSSoft.Communication.Client/Program.cs index 2405340..6100944 100644 --- a/src/JSSoft.Communication.Client/Program.cs +++ b/src/JSSoft.Communication.Client/Program.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using JSSoft.Communication.ConsoleApp; diff --git a/src/JSSoft.Communication.Client/Services/DataService.cs b/src/JSSoft.Communication.Client/Services/DataService.cs index 2342c4b..6376cbc 100644 --- a/src/JSSoft.Communication.Client/Services/DataService.cs +++ b/src/JSSoft.Communication.Client/Services/DataService.cs @@ -1,22 +1,37 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; -using JSSoft.Communication.Services; -namespace JSSoft.Communication.Client.Services; +namespace JSSoft.Communication.Services; [Export(typeof(IService))] [Export(typeof(IDataService))] -internal sealed class DataService : ClientService, IDataService +sealed class DataService : ClientService, IDataService { - public Task CreateDataBaseAsync( - string dataBaseName, CancellationToken cancellationToken) + public Task CreateDataBaseAsync(string dataBaseName, CancellationToken cancellationToken) { return Server.CreateDataBaseAsync(dataBaseName, cancellationToken); } diff --git a/src/JSSoft.Communication.Client/Services/UserService.cs b/src/JSSoft.Communication.Client/Services/UserService.cs index 4409480..a86dba8 100644 --- a/src/JSSoft.Communication.Client/Services/UserService.cs +++ b/src/JSSoft.Communication.Client/Services/UserService.cs @@ -1,86 +1,103 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; -using JSSoft.Communication.Services; -namespace JSSoft.Communication.Client.Services; +namespace JSSoft.Communication.Services; [Export(typeof(IService))] [Export(typeof(IUserService))] [Export(typeof(INotifyUserService))] -internal sealed class UserService - : ClientService, IUserService, IUserCallback, INotifyUserService +sealed class UserService : ClientService, IUserService, IUserCallback, INotifyUserService { - public event EventHandler? LoggedIn; + public Task CreateAsync(Guid token, string userID, string password, Authority authority, CancellationToken cancellationToken) + => Server.CreateAsync(token, userID, password, authority, cancellationToken); - public event EventHandler? LoggedOut; + public Task DeleteAsync(Guid token, string userID, CancellationToken cancellationToken) + => Server.DeleteAsync(token, userID, cancellationToken); - public event EventHandler? Created; + public Task RenameAsync(Guid token, string userName, CancellationToken cancellationToken) + => Server.RenameAsync(token, userName, cancellationToken); - public event EventHandler? Deleted; + public Task SetAuthorityAsync(Guid token, string userID, Authority authority, CancellationToken cancellationToken) + => Server.SetAuthorityAsync(token, userID, authority, cancellationToken); - public event EventHandler? MessageReceived; + public Task LoginAsync(string userID, string password, CancellationToken cancellationToken) + => Server.LoginAsync(userID, password, cancellationToken); - public event EventHandler? Renamed; + public Task LogoutAsync(Guid token, CancellationToken cancellationToken) + => Server.LogoutAsync(token, cancellationToken); - public event EventHandler? AuthorityChanged; + public Task<(string userName, Authority authority)> GetInfoAsync(Guid token, string userID, CancellationToken cancellationToken) + => Server.GetInfoAsync(token, userID, cancellationToken); + + public Task GetUsersAsync(Guid token, CancellationToken cancellationToken) + => Server.GetUsersAsync(token, cancellationToken); - public Task CreateAsync(UserCreateOptions options, CancellationToken cancellationToken) - => Server.CreateAsync(options, cancellationToken); + public Task IsOnlineAsync(Guid token, string userID, CancellationToken cancellationToken) + => Server.IsOnlineAsync(token, userID, cancellationToken); - public Task DeleteAsync(UserDeleteOptions options, CancellationToken cancellationToken) - => Server.DeleteAsync(options, cancellationToken); + public Task SendMessageAsync(Guid token, string userID, string message, CancellationToken cancellationToken) + => Server.SendMessageAsync(token, userID, message, cancellationToken); - public Task RenameAsync(UserRenameOptions options, CancellationToken cancellationToken) - => Server.RenameAsync(options, cancellationToken); + public event EventHandler? LoggedIn; - public Task SetAuthorityAsync(UserAuthorityOptions options, CancellationToken cancellationToken) - => Server.SetAuthorityAsync(options, cancellationToken); + public event EventHandler? LoggedOut; - public Task LoginAsync(UserLoginOptions options, CancellationToken cancellationToken) - => Server.LoginAsync(options, cancellationToken); + public event EventHandler? Created; - public Task LogoutAsync(Guid token, CancellationToken cancellationToken) - => Server.LogoutAsync(token, cancellationToken); + public event EventHandler? Deleted; - public Task GetInfoAsync( - Guid token, string userId, CancellationToken cancellationToken) - => Server.GetInfoAsync(token, userId, cancellationToken); + public event EventHandler? MessageReceived; - public Task GetUsersAsync(Guid token, CancellationToken cancellationToken) - => Server.GetUsersAsync(token, cancellationToken); + public event EventHandler? Renamed; - public Task IsOnlineAsync(Guid token, string userId, CancellationToken cancellationToken) - => Server.IsOnlineAsync(token, userId, cancellationToken); + public event EventHandler? AuthorityChanged; - public Task SendMessageAsync( - UserSendMessageOptions options, CancellationToken cancellationToken) - => Server.SendMessageAsync(options, cancellationToken); + #region IUserCallback - void IUserCallback.OnCreated(string userId) - => Created?.Invoke(this, new UserEventArgs(userId)); + void IUserCallback.OnCreated(string userID) + => Created?.Invoke(this, new UserEventArgs(userID)); - void IUserCallback.OnDeleted(string userId) - => Deleted?.Invoke(this, new UserEventArgs(userId)); + void IUserCallback.OnDeleted(string userID) + => Deleted?.Invoke(this, new UserEventArgs(userID)); - void IUserCallback.OnLoggedIn(string userId) - => LoggedIn?.Invoke(this, new UserEventArgs(userId)); + void IUserCallback.OnLoggedIn(string userID) + => LoggedIn?.Invoke(this, new UserEventArgs(userID)); - void IUserCallback.OnLoggedOut(string userId) - => LoggedOut?.Invoke(this, new UserEventArgs(userId)); + void IUserCallback.OnLoggedOut(string userID) + => LoggedOut?.Invoke(this, new UserEventArgs(userID)); void IUserCallback.OnMessageReceived(string sender, string receiver, string message) => MessageReceived?.Invoke(this, new UserMessageEventArgs(sender, receiver, message)); - void IUserCallback.OnRenamed(string userId, string userName) - => Renamed?.Invoke(this, new UserNameEventArgs(userId, userName)); + void IUserCallback.OnRenamed(string userID, string userName) + => Renamed?.Invoke(this, new UserNameEventArgs(userID, userName)); + + void IUserCallback.OnAuthorityChanged(string userID, Authority authority) + => AuthorityChanged?.Invoke(this, new UserAuthorityEventArgs(userID, authority)); - void IUserCallback.OnAuthorityChanged(string userId, Authority authority) - => AuthorityChanged?.Invoke(this, new UserAuthorityEventArgs(userId, authority)); + #endregion } diff --git a/src/JSSoft.Communication.Server/Program.cs b/src/JSSoft.Communication.Server/Program.cs index 2405340..6100944 100644 --- a/src/JSSoft.Communication.Server/Program.cs +++ b/src/JSSoft.Communication.Server/Program.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using JSSoft.Communication.ConsoleApp; diff --git a/src/JSSoft.Communication.Server/ServerContext.cs b/src/JSSoft.Communication.Server/ServerContext.cs index 514dcfa..efe490f 100644 --- a/src/JSSoft.Communication.Server/ServerContext.cs +++ b/src/JSSoft.Communication.Server/ServerContext.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System.ComponentModel.Composition; @@ -9,7 +26,7 @@ namespace JSSoft.Communication.ConsoleApp; [Export(typeof(IServiceContext))] [method: ImportingConstructor] -internal sealed class ServerContext([ImportMany] IService[] services) +sealed class ServerContext([ImportMany] IService[] services) : Communication.ServerContext(services) { } diff --git a/src/JSSoft.Communication.Server/Services/DataService.cs b/src/JSSoft.Communication.Server/Services/DataService.cs index 37f6465..ee68929 100644 --- a/src/JSSoft.Communication.Server/Services/DataService.cs +++ b/src/JSSoft.Communication.Server/Services/DataService.cs @@ -1,21 +1,37 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; -using JSSoft.Communication.Services; using JSSoft.Communication.Threading; -namespace JSSoft.Communication.Server.Services; +namespace JSSoft.Communication.Services; [Export(typeof(IService))] [Export(typeof(IDataService))] -internal sealed class DataService : ServerService, IDataService, IDisposable +sealed class DataService : ServerService, IDataService, IDisposable { private readonly HashSet _dataBases = []; private Dispatcher? _dispatcher; @@ -25,31 +41,26 @@ public DataService() _dispatcher = new Dispatcher(this); } - public Dispatcher Dispatcher => _dispatcher ?? throw new ObjectDisposedException($"{this}"); - - public Task CreateDataBaseAsync( - string dataBaseName, CancellationToken cancellationToken) + public Task CreateDataBaseAsync(string dataBaseName, CancellationToken cancellationToken) { - return Dispatcher.InvokeAsync(() => CreateDataBase(dataBaseName)); - - DateTime CreateDataBase(string dataBaseName) + return Dispatcher.InvokeAsync(() => { if (_dataBases.Contains(dataBaseName) == true) - { - throw new ArgumentException("The database already exists.", nameof(dataBaseName)); - } - + throw new ArgumentNullException(nameof(dataBaseName)); _dataBases.Add(dataBaseName); return DateTime.UtcNow; - } + }); } public void Dispose() { - ObjectDisposedException.ThrowIf(_dispatcher == null, this); + if (_dispatcher == null) + throw new ObjectDisposedException($"{this}"); _dispatcher.Dispose(); _dispatcher = null; GC.SuppressFinalize(this); } -} + + public Dispatcher Dispatcher => _dispatcher ?? throw new ObjectDisposedException($"{this}"); +} \ No newline at end of file diff --git a/src/JSSoft.Communication.Server/Services/User.cs b/src/JSSoft.Communication.Server/Services/User.cs deleted file mode 100644 index 7fcbb62..0000000 --- a/src/JSSoft.Communication.Server/Services/User.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; -using JSSoft.Communication.Services; - -namespace JSSoft.Communication.Server.Services; - -internal sealed class User -{ - public string UserId { get; set; } = string.Empty; - - public string UserName { get; set; } = string.Empty; - - public string Password { get; set; } = string.Empty; - - public Authority Authority { get; set; } - - public Guid Token { get; set; } -} diff --git a/src/JSSoft.Communication.Server/Services/UserInfo.cs b/src/JSSoft.Communication.Server/Services/UserInfo.cs new file mode 100644 index 0000000..f71c857 --- /dev/null +++ b/src/JSSoft.Communication.Server/Services/UserInfo.cs @@ -0,0 +1,38 @@ +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace JSSoft.Communication.Services; + +class UserInfo +{ + public string UserID { get; set; } = string.Empty; + + public string UserName { get; set; } = string.Empty; + + public string Password { get; set; } = string.Empty; + + public Authority Authority { get; set; } + + public Guid Token { get; set; } +} \ No newline at end of file diff --git a/src/JSSoft.Communication.Server/Services/UserService.cs b/src/JSSoft.Communication.Server/Services/UserService.cs index bb5bf6f..af765b4 100644 --- a/src/JSSoft.Communication.Server/Services/UserService.cs +++ b/src/JSSoft.Communication.Server/Services/UserService.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Generic; @@ -9,27 +26,25 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using JSSoft.Communication.Services; using JSSoft.Communication.Threading; -namespace JSSoft.Communication.Server.Services; +namespace JSSoft.Communication.Services; [Export(typeof(IService))] [Export(typeof(IUserService))] [Export(typeof(INotifyUserService))] -internal sealed class UserService - : ServerService, IUserService, INotifyUserService, IDisposable +sealed class UserService : ServerService, IUserService, INotifyUserService, IDisposable { - private readonly Dictionary _userById = []; - private readonly Dictionary _userByToken = []; + private readonly Dictionary _userByID = []; + private readonly Dictionary _userByToken = []; private Dispatcher? _dispatcher; public UserService() { - _userById.Add("admin", new User() + _userByID.Add("admin", new UserInfo() { - UserId = "admin", + UserID = "admin", Password = "admin", UserName = "Administrator", Authority = Authority.Admin, @@ -37,82 +52,59 @@ public UserService() for (var i = 0; i < 10; i++) { - var user = new User() + var user = new UserInfo() { - UserId = $"user{i}", + UserID = $"user{i}", Password = "1234", UserName = $"사용자{i}", Authority = Authority.Member, }; - _userById.Add(user.UserId, user); + _userByID.Add(user.UserID, user); } - _dispatcher = new Dispatcher(this); } - public event EventHandler? Created; - - public event EventHandler? Deleted; - - public event EventHandler? LoggedIn; - - public event EventHandler? LoggedOut; - - public event EventHandler? MessageReceived; - - public event EventHandler? Renamed; - - public event EventHandler? AuthorityChanged; - - public Dispatcher Dispatcher => _dispatcher - ?? throw new InvalidOperationException($"'{this}' has not been initialized."); - - public Task CreateAsync(UserCreateOptions options, CancellationToken cancellationToken) + public Task CreateAsync(Guid token, string userID, string password, Authority authority, CancellationToken cancellationToken) { return Dispatcher.InvokeAsync(() => { - ValidateCreate(options); + ValidateCreate(token, userID, password); - var user = new User() + var user = _userByToken[token]; + var userInfo = new UserInfo() { - UserId = options.UserId, - Password = options.Password, + UserID = userID, + Password = password, UserName = string.Empty, - Authority = options.Authority, + Authority = authority }; - _userById.Add(options.UserId, user); - Client.OnCreated(options.UserId); - Created?.Invoke(this, new UserEventArgs(options.UserId)); + _userByID.Add(userID, userInfo); + Client.OnCreated(userID); + Created?.Invoke(this, new UserEventArgs(userID)); }); } - public Task DeleteAsync(UserDeleteOptions options, CancellationToken cancellationToken) + public Task DeleteAsync(Guid token, string userID, CancellationToken cancellationToken) { return Dispatcher.InvokeAsync(() => { - ValidateDelete(options); + ValidateDelete(token, userID); - var userId = options.UserId; - _userById.Remove(userId); - Client.OnDeleted(userId); - Deleted?.Invoke(this, new UserEventArgs(userId)); + _userByID.Remove(userID); + Client.OnDeleted(userID); + Deleted?.Invoke(this, new UserEventArgs(userID)); }); } - public Task GetInfoAsync( - Guid token, string userId, CancellationToken cancellationToken) + public Task<(string, Authority)> GetInfoAsync(Guid token, string userID, CancellationToken cancellationToken) { return Dispatcher.InvokeAsync(() => { ValidateUser(token); - ValidateUser(userId); + ValidateUser(userID); - var user = _userById[userId]; - return new UserInfo - { - UserName = user.UserName, - Authority = user.Authority, - }; + var user = _userByID[userID]; + return (user.UserName, user.Authority); }); } @@ -122,36 +114,34 @@ public Task GetUsersAsync(Guid token, CancellationToken cancellationTo { ValidateUser(token); - return _userById.Keys.ToArray(); + return _userByID.Keys.ToArray(); }); } - public Task IsOnlineAsync(Guid token, string userId, CancellationToken cancellationToken) + public Task IsOnlineAsync(Guid token, string userID, CancellationToken cancellationToken) { return Dispatcher.InvokeAsync(() => { ValidateUser(token); - ValidateUser(userId); + ValidateUser(userID); - var user = _userById[userId]; + var user = _userByID[userID]; return user.Token != Guid.Empty; }); } - public Task LoginAsync(UserLoginOptions options, CancellationToken cancellationToken) + public Task LoginAsync(string userID, string password, CancellationToken cancellationToken) { return Dispatcher.InvokeAsync(() => { - var userId = options.UserId; - var password = options.Password; - ValidatePassword(userId, password); + ValidatePassword(userID, password); var token = Guid.NewGuid(); - var user = _userById[userId]; + var user = _userByID[userID]; user.Token = token; _userByToken.Add(token, user); - Client.OnLoggedIn(userId); - LoggedIn?.Invoke(this, new UserEventArgs(userId)); + Client.OnLoggedIn(userID); + LoggedIn?.Invoke(this, new UserEventArgs(userID)); return token; }); } @@ -165,174 +155,158 @@ public Task LogoutAsync(Guid token, CancellationToken cancellationToken) var user = _userByToken[token]; user.Token = Guid.Empty; _userByToken.Remove(token); - Client.OnLoggedOut(user.UserId); - LoggedOut?.Invoke(this, new UserEventArgs(user.UserId)); + Client.OnLoggedOut(user.UserID); + LoggedOut?.Invoke(this, new UserEventArgs(user.UserID)); }); } - public Task SendMessageAsync( - UserSendMessageOptions options, CancellationToken cancellationToken) + public Task SendMessageAsync(Guid token, string userID, string message, CancellationToken cancellationToken) { return Dispatcher.InvokeAsync(() => { - var token = options.Token; - var userId = options.UserId; - var message = options.Message; ValidateUser(token); - ValidateOnline(userId); + ValidateOnline(userID); ValidateMessage(message); var user = _userByToken[token]; - Client.OnMessageReceived(user.UserId, userId, message); - MessageReceived?.Invoke(this, new UserMessageEventArgs(user.UserId, userId, message)); + Client.OnMessageReceived(user.UserID, userID, message); + MessageReceived?.Invoke(this, new UserMessageEventArgs(user.UserID, userID, message)); }); } - public Task RenameAsync(UserRenameOptions options, CancellationToken cancellationToken) + public Task RenameAsync(Guid token, string userName, CancellationToken cancellationToken) { return Dispatcher.InvokeAsync(() => { - var token = options.Token; - var userName = options.UserName; ValidateRename(token, userName); var user = _userByToken[token]; user.UserName = userName; - Client.OnRenamed(user.UserId, userName); - Renamed?.Invoke(this, new UserNameEventArgs(user.UserId, userName)); + Client.OnRenamed(user.UserID, userName); + Renamed?.Invoke(this, new UserNameEventArgs(user.UserID, userName)); }); } - public Task SetAuthorityAsync(UserAuthorityOptions options, CancellationToken cancellationToken) + public Task SetAuthorityAsync(Guid token, string userID, Authority authority, CancellationToken cancellationToken) { return Dispatcher.InvokeAsync(() => { - var token = options.Token; - var userId = options.UserId; - var authority = options.Authority; - ValidateSetAuthority(token, userId); + ValidateSetAuthority(token, userID); - var user = _userById[userId]; + var user = _userByID[userID]; user.Authority = authority; - Client.OnAuthorityChanged(userId, authority); - AuthorityChanged?.Invoke(this, new UserAuthorityEventArgs(userId, authority)); + Client.OnAuthorityChanged(userID, authority); + AuthorityChanged?.Invoke(this, new UserAuthorityEventArgs(userID, authority)); }); } public void Dispose() { - ObjectDisposedException.ThrowIf(_dispatcher == null, $"{this}"); + if (_dispatcher == null) + throw new ObjectDisposedException($"{this}"); _dispatcher.Dispose(); _dispatcher = null; GC.SuppressFinalize(this); } - private void ValidateUser(string userId) + public Dispatcher Dispatcher { - Dispatcher.VerifyAccess(); - if (userId == null) + get { - throw new ArgumentNullException(nameof(userId)); - } - - if (_userById.ContainsKey(userId) != true) - { - throw new ArgumentException("Invalid userID", nameof(userId)); + if (_dispatcher == null) + throw new InvalidOperationException($"'{this}' has not been initialized."); + return _dispatcher; } } - private void ValidateUser(Guid token) + public event EventHandler? Created; + + public event EventHandler? Deleted; + + public event EventHandler? LoggedIn; + + public event EventHandler? LoggedOut; + + public event EventHandler? MessageReceived; + + public event EventHandler? Renamed; + + public event EventHandler? AuthorityChanged; + + private void ValidateUser(string userID) { Dispatcher.VerifyAccess(); - if (_userByToken.ContainsKey(token) != true) - { - throw new ArgumentException("Invalid token.", nameof(token)); - } + if (userID == null) + throw new ArgumentNullException(nameof(userID)); + if (_userByID.ContainsKey(userID) != true) + throw new ArgumentException("Invalid userID", nameof(userID)); } - private void ValidateNotUser(string userId) + private void ValidateNotUser(string userID) { Dispatcher.VerifyAccess(); - if (userId == null) - { - throw new ArgumentNullException(nameof(userId)); - } + if (userID == null) + throw new ArgumentNullException(nameof(userID)); + if (_userByID.ContainsKey(userID) == true) + throw new ArgumentException("user is already exists.", nameof(userID)); + } - if (_userById.ContainsKey(userId) == true) - { - throw new ArgumentException("user is already exists.", nameof(userId)); - } + private void ValidateUser(Guid token) + { + Dispatcher.VerifyAccess(); + if (_userByToken.ContainsKey(token) != true) + throw new ArgumentException("Invalid token.", nameof(token)); } private void ValidatePassword(string password) { Dispatcher.VerifyAccess(); if (password == null) - { throw new ArgumentNullException(nameof(password)); - } - if (password == string.Empty) - { throw new ArgumentException("Invalid password.", nameof(password)); - } - if (password.Length < 4) - { - throw new ArgumentException( - "length of password must be greater or equal than 4.", nameof(password)); - } + throw new ArgumentException("length of password must be greater or equal than 4.", nameof(password)); } - private void ValidatePassword(string userId, string password) + private void ValidatePassword(string userID, string password) { Dispatcher.VerifyAccess(); - ValidateUser(userId); + ValidateUser(userID); if (password == null) - { throw new ArgumentNullException(nameof(password)); - } - - var user = _userById[userId]; + var user = _userByID[userID]; if (user.Password != password) - { throw new InvalidOperationException("wrong userID or password."); - } } - private void ValidateCreate(UserCreateOptions options) + private void ValidateCreate(Guid token, string userID, string password) { Dispatcher.VerifyAccess(); - ValidateUser(options.Token); - ValidateNotUser(options.UserId); - ValidatePassword(options.Password); - var user = _userByToken[options.Token]; + ValidateUser(token); + ValidateNotUser(userID); + ValidatePassword(password); + var user = _userByToken[token]; if (user.Authority != Authority.Admin) - { throw new InvalidOperationException("permission denied."); - } } private void ValidateMessage(string message) { Dispatcher.VerifyAccess(); if (message == null) - { throw new ArgumentNullException(nameof(message)); - } } - private void ValidateOnline(string userId) + private void ValidateOnline(string userID) { Dispatcher.VerifyAccess(); - ValidateUser(userId); - var user = _userById[userId]; + ValidateUser(userID); + var user = _userByID[userID]; if (user.Token == Guid.Empty) - { throw new InvalidOperationException($"user is offline."); - } } private void ValidateRename(Guid token, string userName) @@ -340,75 +314,45 @@ private void ValidateRename(Guid token, string userName) Dispatcher.VerifyAccess(); ValidateUser(token); if (userName == null) - { throw new ArgumentNullException(nameof(userName)); - } - if (userName == string.Empty) - { throw new ArgumentException("Invalid name.", nameof(userName)); - } - var user = _userByToken[token]; if (user.UserName == userName) - { throw new ArgumentException("same name can not set.", nameof(userName)); - } - - if (user.UserId == "admin") - { + if (user.UserID == "admin") throw new InvalidOperationException("permission denied."); - } } - private void ValidateSetAuthority(Guid token, string userId) + private void ValidateSetAuthority(Guid token, string userID) { Dispatcher.VerifyAccess(); ValidateUser(token); - ValidateUser(userId); + ValidateUser(userID); var user1 = _userByToken[token]; - if (user1.UserId == userId) - { + if (user1.UserID == userID) throw new InvalidOperationException("can not set authority."); - } - if (user1.Authority != Authority.Admin) - { throw new InvalidOperationException("permission denied."); - } - - var user2 = _userById[userId]; + var user2 = _userByID[userID]; if (user2.Token != Guid.Empty) - { throw new InvalidOperationException("can not set authority of online user."); - } - - if (userId == "admin") - { + if (userID == "admin") throw new InvalidOperationException("permission denied."); - } } - private void ValidateDelete(UserDeleteOptions options) + private void ValidateDelete(Guid token, string userID) { Dispatcher.VerifyAccess(); - ValidateUser(options.Token); - ValidateNotUser(options.UserId); - var user1 = _userByToken[options.Token]; + ValidateUser(token); + ValidateNotUser(userID); + var user1 = _userByToken[token]; if (user1.Authority != Authority.Admin) - { throw new InvalidOperationException("permission denied."); - } - - var user2 = _userById[options.UserId]; + var user2 = _userByID[userID]; if (user2.Token != Guid.Empty) - { throw new InvalidOperationException("can not delete online user."); - } - - if (options.UserId == "admin") - { + if (userID == "admin") throw new InvalidOperationException("permission denied."); - } } } diff --git a/src/JSSoft.Communication/AdaptorProvider.cs b/src/JSSoft.Communication/AdaptorProvider.cs index 799dc1b..937c377 100644 --- a/src/JSSoft.Communication/AdaptorProvider.cs +++ b/src/JSSoft.Communication/AdaptorProvider.cs @@ -1,31 +1,48 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; namespace JSSoft.Communication; -internal sealed class AdaptorProvider : IAdaptorProvider +sealed class AdaptorProvider : IAdaptorProvider { public const string DefaultName = "grpc"; - public static readonly AdaptorProvider Default = new(); - - public string Name => DefaultName; - public IAdaptor Create( - IServiceContext serviceContext, IInstanceContext instanceContext, ServiceToken serviceToken) + public IAdaptor Create(IServiceContext serviceContext, IInstanceContext instanceContext, ServiceToken token) { + // Environment.SetEnvironmentVariable("GRPC_VERBOSITY", "DEBUG"); + // Environment.SetEnvironmentVariable("GRPC_TRACE", "all"); + // global::Grpc.Core.GrpcEnvironment.SetLogger(new global::Grpc.Core.Logging.ConsoleLogger()); + if (serviceContext is ServerContext serverContext) - { return new Grpc.AdaptorServer(serverContext, instanceContext); - } else if (serviceContext is ClientContext clientContext) - { return new Grpc.AdaptorClient(clientContext, instanceContext); - } throw new NotSupportedException($"'{serviceContext.GetType()}' is not supported."); } + + public string Name => DefaultName; + + public static readonly AdaptorProvider Default = new(); } diff --git a/src/JSSoft.Communication/ClientContext.cs b/src/JSSoft.Communication/ClientContext.cs index 9e2b9f8..1bd9cda 100644 --- a/src/JSSoft.Communication/ClientContext.cs +++ b/src/JSSoft.Communication/ClientContext.cs @@ -1,7 +1,26 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; namespace JSSoft.Communication; diff --git a/src/JSSoft.Communication/ClientService.cs b/src/JSSoft.Communication/ClientService.cs index 4368318..afa0f52 100644 --- a/src/JSSoft.Communication/ClientService.cs +++ b/src/JSSoft.Communication/ClientService.cs @@ -1,12 +1,27 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -// File may only contain a single type -#pragma warning disable SA1402 +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; +using System.Reflection; namespace JSSoft.Communication; @@ -26,20 +41,18 @@ public ClientService(TClient client) public ClientService() : base(typeof(TServer), typeof(TClient)) { - var obj = this; - if (obj is TClient client) + if (this is TClient client) { _client = client; + } else { - throw new InvalidOperationException( - $"'{GetType()}' must be implemented by '{typeof(TClient)}'."); + throw new InvalidOperationException($"'{GetType()}' must be implemented by '{typeof(TClient)}'."); } } - public TServer Server - => _server ?? throw new InvalidOperationException("Server is not created."); + public TServer Server => _server ?? throw new InvalidOperationException("Server is not created."); protected virtual TClient CreateClient(IPeer peer) => _client; @@ -72,8 +85,7 @@ public ClientService() { } - public TServer Server - => _server ?? throw new InvalidOperationException("Server is not created."); + public TServer Server => _server ?? throw new InvalidOperationException("Server is not created."); protected virtual void OnServiceCreated(IPeer peer, TServer server) { diff --git a/src/JSSoft.Communication/EndPointUtility.cs b/src/JSSoft.Communication/EndPointUtility.cs index 6d5afe7..3870098 100644 --- a/src/JSSoft.Communication/EndPointUtility.cs +++ b/src/JSSoft.Communication/EndPointUtility.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Diagnostics.CodeAnalysis; @@ -13,7 +30,7 @@ namespace JSSoft.Communication; public static class EndPointUtility { - public static (string Host, int Port) GetElements(EndPoint endPoint) + public static (string host, int port) GetElements(EndPoint endPoint) { if (endPoint is DnsEndPoint dnsEndPoint) { @@ -23,7 +40,6 @@ public static (string Host, int Port) GetElements(EndPoint endPoint) { return ($"{iPEndPoint.Address}", iPEndPoint.Port); } - throw new NotSupportedException($"'{endPoint}' is not supported."); } @@ -37,7 +53,6 @@ public static string ToString(EndPoint endPoint) { return $"{iPEndPoint.Address}:{iPEndPoint.Port}"; } - throw new NotSupportedException($"'{endPoint}' is not supported."); } @@ -71,8 +86,7 @@ public static bool TryParse(string text, [MaybeNullWhen(false)] out EndPoint end } #if NETSTANDARD - internal static global::Grpc.Core.ServerPort GetServerPort( - EndPoint endPoint, global::Grpc.Core.ServerCredentials credentials) + internal static global::Grpc.Core.ServerPort GetServerPort(EndPoint endPoint, global::Grpc.Core.ServerCredentials credentials) { if (endPoint is DnsEndPoint dnsEndPoint) { diff --git a/src/JSSoft.Communication/Extensions/ISerializerExtensions.cs b/src/JSSoft.Communication/Extensions/ISerializerExtensions.cs index fe9fb91..dba7f59 100644 --- a/src/JSSoft.Communication/Extensions/ISerializerExtensions.cs +++ b/src/JSSoft.Communication/Extensions/ISerializerExtensions.cs @@ -1,14 +1,31 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Threading; namespace JSSoft.Communication.Extensions; -internal static class ISerializerExtensions +static class ISerializerExtensions { public static string[] SerializeMany(this ISerializer @this, Type[] types, object?[] args) { @@ -19,7 +36,6 @@ public static string[] SerializeMany(this ISerializer @this, Type[] types, objec var value = args[i]; items[i] = @this.Serialize(type, value); } - return items; } @@ -32,12 +48,10 @@ public static string[] SerializeMany(this ISerializer @this, Type[] types, objec var value = datas[i]; items[i] = @this.Deserialize(type, value); } - return items; } - public static object?[] DeserializeMany( - this ISerializer @this, Type[] types, string[] datas, CancellationToken? cancellationToken) + public static object?[] DeserializeMany(this ISerializer @this, Type[] types, string[] datas, CancellationToken? cancellationToken) { var length = cancellationToken != null ? datas.Length + 1 : datas.Length; var items = new object?[length]; @@ -47,12 +61,10 @@ public static string[] SerializeMany(this ISerializer @this, Type[] types, objec var value = datas[i]; items[i] = @this.Deserialize(type, value); } - if (cancellationToken != null) { items[datas.Length] = cancellationToken; } - return items; } } diff --git a/src/JSSoft.Communication/Extensions/IServiceContextExtensions.cs b/src/JSSoft.Communication/Extensions/IServiceContextExtensions.cs index 0fe215d..5831031 100644 --- a/src/JSSoft.Communication/Extensions/IServiceContextExtensions.cs +++ b/src/JSSoft.Communication/Extensions/IServiceContextExtensions.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Threading.Tasks; @@ -21,11 +38,11 @@ public static void Error(this IServiceContext @this, string message) /// Close the service context without exception throwing. /// /// Indicates a service context instance to close. - /// Indicates a token to close. you can get the token when + /// Indicates a token to close. you can get the token when /// the service context is opened. /// /// - /// If the service context is closed successfully, it returns 0. + /// If the service context is closed successfully, it returns 0. /// If the service context is faulted, it returns 1. /// public static async Task ReleaseAsync(this IServiceContext @this, Guid token) @@ -38,7 +55,6 @@ public static async Task ReleaseAsync(this IServiceContext @this, Guid toke } catch { - // do nothing } } diff --git a/src/JSSoft.Communication/Grpc/AdaptorClient.cs b/src/JSSoft.Communication/Grpc/AdaptorClient.cs index 6cf7899..9b4e0ac 100644 --- a/src/JSSoft.Communication/Grpc/AdaptorClient.cs +++ b/src/JSSoft.Communication/Grpc/AdaptorClient.cs @@ -1,19 +1,34 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using Grpc.Core; +using JSSoft.Communication.Extensions; +using JSSoft.Communication.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using Grpc.Core; -using JSSoft.Communication.Extensions; -using JSSoft.Communication.Logging; -using Newtonsoft.Json; - #if NETSTANDARD using GrpcChannel = Grpc.Core.Channel; #elif NET @@ -23,7 +38,7 @@ namespace JSSoft.Communication.Grpc; -internal sealed class AdaptorClient : IAdaptor +sealed class AdaptorClient : IAdaptor { private static readonly TimeSpan Timeout = new(0, 0, 15); @@ -44,32 +59,23 @@ public AdaptorClient(IServiceContext serviceContext, IInstanceContext instanceCo _serviceContext = serviceContext; _instanceContext = instanceContext; _serviceByName = serviceContext.Services; - _methodsByService = _serviceByName.ToDictionary( - keySelector: item => item.Value, - elementSelector: item => new MethodDescriptorCollection(item.Value.ClientType)); + _methodsByService = _serviceByName.ToDictionary(item => item.Value, item => new MethodDescriptorCollection(item.Value.ClientType)); } - public event EventHandler? Disconnected; - public async Task OpenAsync(EndPoint endPoint, CancellationToken cancellationToken) { if (_adaptorImpl != null) - { throw new InvalidOperationException("Already opened."); - } - try { #if NETSTANDARD _channel = new Channel(EndPointUtility.ToString(endPoint), ChannelCredentials.Insecure); await _channel.ConnectAsync(deadline: DateTime.UtcNow.AddSeconds(15)); #elif NET - _channel = GrpcChannel.ForAddress( - $"http://{EndPointUtility.ConvertToIPEndPoint(endPoint)}"); + _channel = GrpcChannel.ForAddress($"http://{EndPointUtility.ConvertToIPEndPoint(endPoint)}"); await _channel.ConnectAsync(cancellationToken); #endif - _adaptorImpl = new AdaptorClientImpl( - _channel, _serviceContext.Id, _serviceByName.Values.ToArray()); + _adaptorImpl = new AdaptorClientImpl(_channel, _serviceContext.Id, _serviceByName.Values.ToArray()); await _adaptorImpl.OpenAsync(cancellationToken); _descriptor = _instanceContext.CreateInstance(_adaptorImpl); _cancellationTokenSource = new CancellationTokenSource(); @@ -84,43 +90,35 @@ public async Task OpenAsync(EndPoint endPoint, CancellationToken cancellationTok await _channel.ShutdownAsync(); _channel = null; } - throw; } } public async Task CloseAsync(CancellationToken cancellationToken) { - if (_cancellationTokenSource is not null) - { - await _cancellationTokenSource.CancelAsync(); - _cancellationTokenSource.Dispose(); - _cancellationTokenSource = null; - } - + _cancellationTokenSource?.Cancel(); if (_timer != null) { await _timer.DisposeAsync(); _timer = null; } - if (_adaptorImpl != null) { _instanceContext.DestroyInstance(_adaptorImpl); await _adaptorImpl.CloseAsync(cancellationToken); _adaptorImpl = null; } - if (_task != null) { await _task; } - if (_channel != null) { await _channel.ShutdownAsync(); _channel = null; } + _cancellationTokenSource?.Dispose(); + _cancellationTokenSource = null; } public async ValueTask DisposeAsync() @@ -131,34 +129,133 @@ public async ValueTask DisposeAsync() await _timer.DisposeAsync(); _timer = null; } - if (_adaptorImpl != null) { _instanceContext.DestroyInstance(_adaptorImpl); await _adaptorImpl.TryCloseAsync(cancellationToken: default); _adaptorImpl = null; } - if (_channel != null) { await _channel.ShutdownAsync(); _channel = null; } - GC.SuppressFinalize(this); } - void IAdaptor.Invoke(InvokeOptions options) + public event EventHandler? Disconnected; + + private async void Timer_TimerCallback(object? state) + { + try + { + if (_adaptorImpl != null) + { + var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; + var request = new PingRequest(); + await _adaptorImpl.PingAsync(request, metaData); + } + } + catch + { + } + } + + private async Task PollAsync(CancellationToken cancellationToken) { if (_adaptorImpl == null) + throw new InvalidOperationException("adaptor is not set."); + + var closeCode = int.MinValue; + try + { + var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; + using var call = _adaptorImpl.Poll(metaData); + var request = new PollRequest(); + await call.RequestStream.WriteAsync(request); + while (await MoveAsync(call, cancellationToken) == true) + { + var reply = call.ResponseStream.Current; + if (reply.Code != int.MinValue) + { + closeCode = reply.Code; + break; + } + InvokeCallback(reply); + await call.RequestStream.WriteAsync(request); + } + await call.RequestStream.CompleteAsync(); + } + catch (Exception e) + { + closeCode = -2; + LogUtility.Error(e); + } + if (closeCode != int.MinValue) + { + Disconnected?.Invoke(this, EventArgs.Empty); + } + _serviceContext.Debug("Poll finished."); + + static async Task MoveAsync(AsyncDuplexStreamingCall call, CancellationToken cancellationToken) { + if (cancellationToken.IsCancellationRequested == true) + return false; + using var cancellationTokenSource = new CancellationTokenSource(Timeout); + return await call.ResponseStream.MoveNext(cancellationTokenSource.Token); + } + } + + private void InvokeCallback(IService service, string name, string[] data) + { + if (_adaptorImpl == null) throw new InvalidOperationException("adaptor is not set."); + var methodDescriptors = _methodsByService[service]; + if (methodDescriptors.Contains(name) != true) + throw new InvalidOperationException("Invalid method name."); + + var methodDescriptor = methodDescriptors[name]; + var args = _serializer!.DeserializeMany(methodDescriptor.ParameterTypes, data); + var instance = _descriptor!.ClientInstances[service]; + Task.Run(() => methodDescriptor.InvokeAsync(_serviceContext, instance, args)); + } + + private void InvokeCallback(PollReply reply) + { + foreach (var item in reply.Items) + { + var service = _serviceByName[item.ServiceName]; + InvokeCallback(service, item.Name, [.. item.Data]); } + reply.Items.Clear(); + } + + private void HandleReply(InvokeReply reply) + { + if (reply.ID != string.Empty && Type.GetType(reply.ID) is { } exceptionType) + { + ThrowException(exceptionType, reply.Data); + } + } + + private void ThrowException(Type exceptionType, string data) + { + if (_serializer == null) + throw new InvalidOperationException("serializer is not set."); + + if (Newtonsoft.Json.JsonConvert.DeserializeObject(data, exceptionType) is Exception exception) + throw exception; + + throw new UnreachableException($"This code should not be reached in {nameof(ThrowException)}."); + } + + #region IAdaptor + + void IAdaptor.Invoke(InstanceBase instance, string name, Type[] types, object?[] args) + { + if (_adaptorImpl == null) + throw new InvalidOperationException("adaptor is not set."); - var instance = options.Instance; - var name = options.Name; - var types = options.Types; - var args = options.Args; var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; var data = _serializer!.SerializeMany(types, args); var request = new InvokeRequest @@ -171,17 +268,11 @@ void IAdaptor.Invoke(InvokeOptions options) HandleReply(reply); } - async void IAdaptor.InvokeOneWay(InvokeOptions options) + async void IAdaptor.InvokeOneWay(InstanceBase instance, string name, Type[] types, object?[] args) { if (_adaptorImpl == null) - { throw new InvalidOperationException("adaptor is not set."); - } - var instance = options.Instance; - var name = options.Name; - var types = options.Types; - var args = options.Args; var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; var data = _serializer!.SerializeMany(types, args); var request = new InvokeRequest @@ -196,26 +287,16 @@ async void IAdaptor.InvokeOneWay(InvokeOptions options) } catch { - // do nothing } } - T IAdaptor.Invoke(InvokeOptions options) + T IAdaptor.Invoke(InstanceBase instance, string name, Type[] types, object?[] args) { if (_adaptorImpl == null) - { throw new InvalidOperationException("adaptor is not set."); - } - if (_serializer == null) - { throw new InvalidOperationException("serializer is not set."); - } - var instance = options.Instance; - var name = options.Name; - var types = options.Types; - var args = options.Args; var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; var data = _serializer.SerializeMany(types, args); var request = new InvokeRequest @@ -227,30 +308,18 @@ T IAdaptor.Invoke(InvokeOptions options) var reply = _adaptorImpl.Invoke(request, metaData); HandleReply(reply); if (_serializer.Deserialize(typeof(T), reply.Data) is T value) - { return value; - } - throw new UnreachableException( - $"This code should not be reached in {nameof(IAdaptor.Invoke)}."); + throw new UnreachableException($"This code should not be reached in {nameof(IAdaptor.Invoke)}."); } - async Task IAdaptor.InvokeAsync(InvokeOptions options, CancellationToken cancellationToken) + async Task IAdaptor.InvokeAsync(InstanceBase instance, string name, Type[] types, object?[] args, CancellationToken cancellationToken) { if (_adaptorImpl == null) - { throw new InvalidOperationException("adaptor is not set."); - } - if (_serializer == null) - { throw new InvalidOperationException("serializer is not set."); - } - var instance = options.Instance; - var name = options.Name; - var types = options.Types; - var args = options.Args; var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; var data = _serializer.SerializeMany(types, args); var request = new InvokeRequest @@ -261,40 +330,24 @@ async Task IAdaptor.InvokeAsync(InvokeOptions options, CancellationToken cancell }; try { - var reply = await _adaptorImpl.InvokeAsync( - request: request, - headers: metaData, - cancellationToken: cancellationToken); + var reply = await _adaptorImpl.InvokeAsync(request, metaData, cancellationToken: cancellationToken); HandleReply(reply); } catch (RpcException e) { if (e.StatusCode == StatusCode.Cancelled) - { cancellationToken.ThrowIfCancellationRequested(); - } - throw; } } - async Task IAdaptor.InvokeAsync( - InvokeOptions options, CancellationToken cancellationToken) + async Task IAdaptor.InvokeAsync(InstanceBase instance, string name, Type[] types, object?[] args, CancellationToken cancellationToken) { if (_adaptorImpl == null) - { throw new InvalidOperationException("adaptor is not set."); - } - if (_serializer == null) - { throw new InvalidOperationException("serializer is not set."); - } - var instance = options.Instance; - var name = options.Name; - var types = options.Types; - var args = options.Args; var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; var data = _serializer.SerializeMany(types, args); var request = new InvokeRequest @@ -305,147 +358,17 @@ async Task IAdaptor.InvokeAsync( request.Data.AddRange(data); try { - var reply = await _adaptorImpl.InvokeAsync( - request: request, - headers: metaData, - cancellationToken: cancellationToken); + var reply = await _adaptorImpl.InvokeAsync(request, metaData, cancellationToken: cancellationToken); HandleReply(reply); return _serializer.Deserialize(typeof(T), reply.Data) is T value ? value : default!; } catch (RpcException e) { if (e.StatusCode == StatusCode.Cancelled) - { cancellationToken.ThrowIfCancellationRequested(); - } - throw; } } - private static async Task MoveAsync( - AsyncDuplexStreamingCall call, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested == true) - { - return false; - } - - using var cancellationTokenSource = new CancellationTokenSource(Timeout); - return await call.ResponseStream.MoveNext(cancellationTokenSource.Token); - } - - private async void Timer_TimerCallback(object? state) - { - try - { - if (_adaptorImpl != null) - { - var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; - var request = new PingRequest(); - await _adaptorImpl.PingAsync(request, metaData); - } - } - catch - { - // do nothing - } - } - - private async Task PollAsync(CancellationToken cancellationToken) - { - if (_adaptorImpl == null) - { - throw new InvalidOperationException("adaptor is not set."); - } - - var closeCode = int.MinValue; - try - { - var metaData = new Metadata { { "id", $"{_serviceContext.Id}" } }; - using var call = _adaptorImpl.Poll(metaData); - var request = new PollRequest(); - await call.RequestStream.WriteAsync(request); - while (await MoveAsync(call, cancellationToken) == true) - { - var reply = call.ResponseStream.Current; - if (reply.Code != int.MinValue) - { - closeCode = reply.Code; - break; - } - - InvokeCallback(reply); - await call.RequestStream.WriteAsync(request); - } - - await call.RequestStream.CompleteAsync(); - } - catch (Exception e) - { - closeCode = -2; - LogUtility.Error(e); - } - - if (closeCode != int.MinValue) - { - Disconnected?.Invoke(this, EventArgs.Empty); - } - - _serviceContext.Debug("Poll finished."); - } - - private void InvokeCallback(IService service, string name, string[] data) - { - if (_adaptorImpl == null) - { - throw new InvalidOperationException("adaptor is not set."); - } - - var methodDescriptors = _methodsByService[service]; - if (methodDescriptors.Contains(name) != true) - { - throw new InvalidOperationException("Invalid method name."); - } - - var methodDescriptor = methodDescriptors[name]; - var args = _serializer!.DeserializeMany(methodDescriptor.ParameterTypes, data); - var instance = _descriptor!.ClientInstances[service]; - Task.Run(() => methodDescriptor.InvokeAsync(_serviceContext, instance, args)); - } - - private void InvokeCallback(PollReply reply) - { - foreach (var item in reply.Items) - { - var service = _serviceByName[item.ServiceName]; - InvokeCallback(service, item.Name, [.. item.Data]); - } - - reply.Items.Clear(); - } - - private void HandleReply(InvokeReply reply) - { - if (reply.ID != string.Empty && Type.GetType(reply.ID) is { } exceptionType) - { - ThrowException(exceptionType, reply.Data); - } - } - - private void ThrowException(Type exceptionType, string data) - { - if (_serializer == null) - { - throw new InvalidOperationException("serializer is not set."); - } - - if (JsonConvert.DeserializeObject(data, exceptionType) is Exception exception) - { - throw exception; - } - - throw new UnreachableException( - $"This code should not be reached in {nameof(ThrowException)}."); - } + #endregion } diff --git a/src/JSSoft.Communication/Grpc/AdaptorClientImpl.cs b/src/JSSoft.Communication/Grpc/AdaptorClientImpl.cs index 2181f7e..ebd0026 100644 --- a/src/JSSoft.Communication/Grpc/AdaptorClientImpl.cs +++ b/src/JSSoft.Communication/Grpc/AdaptorClientImpl.cs @@ -1,12 +1,30 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using Grpc.Core; using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using Grpc.Core; #if NETSTANDARD using GrpcChannel = Grpc.Core.Channel; #elif NET @@ -15,7 +33,7 @@ namespace JSSoft.Communication.Grpc; -internal sealed class AdaptorClientImpl(GrpcChannel channel, Guid id, IService[] services) +sealed class AdaptorClientImpl(GrpcChannel channel, Guid id, IService[] services) : Adaptor.AdaptorClient(channel), IPeer { public string Id { get; } = $"{id}"; @@ -24,6 +42,7 @@ internal sealed class AdaptorClientImpl(GrpcChannel channel, Guid id, IService[] public async Task OpenAsync(CancellationToken cancellationToken) { + var serviceNames = Services.Select(item => item.Name).ToArray(); var request = new OpenRequest(); var metaData = new Metadata() { { "id", $"{Id}" } }; try @@ -33,10 +52,7 @@ public async Task OpenAsync(CancellationToken cancellationToken) catch (RpcException e) { if (e.StatusCode == StatusCode.Cancelled) - { cancellationToken.ThrowIfCancellationRequested(); - } - throw; } } @@ -52,10 +68,7 @@ public async Task CloseAsync(CancellationToken cancellationToken) catch (RpcException e) { if (e.StatusCode == StatusCode.Cancelled) - { cancellationToken.ThrowIfCancellationRequested(); - } - throw; } } @@ -72,7 +85,6 @@ public async Task TryCloseAsync(CancellationToken cancellationToken) { return true; } - return false; } } diff --git a/src/JSSoft.Communication/Grpc/AdaptorServer.cs b/src/JSSoft.Communication/Grpc/AdaptorServer.cs index ca0c0ed..5406758 100644 --- a/src/JSSoft.Communication/Grpc/AdaptorServer.cs +++ b/src/JSSoft.Communication/Grpc/AdaptorServer.cs @@ -1,37 +1,55 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using Grpc.Core; +using JSSoft.Communication.Logging; +using JSSoft.Communication.Extensions; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using Grpc.Core; -using JSSoft.Communication.Extensions; -using JSSoft.Communication.Logging; #if NET using System.Diagnostics; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; #endif namespace JSSoft.Communication.Grpc; -internal sealed class AdaptorServer : IAdaptor +record struct CallbackData(IService Service, string Name, string[] Data); + +sealed class AdaptorServer : IAdaptor { private static readonly TimeSpan Timeout = new(0, 0, 30); private static readonly TimeSpan PollTimeout = new(0, 0, 10); private readonly IServiceContext _serviceContext; private readonly IReadOnlyDictionary _serviceByName; private readonly Dictionary _methodsByService; - private readonly Timer _timer; #if NETSTANDARD private Server? _server; private AdaptorServerImpl? _adaptor; @@ -39,54 +57,41 @@ internal sealed class AdaptorServer : IAdaptor private IHost? _host; #endif private ISerializer? _serializer; + private readonly Timer _timer; private EventHandler? _disconnectedEventHandler; public AdaptorServer(IServiceContext serviceContext, IInstanceContext instanceContext) { _serviceContext = serviceContext; _serviceByName = serviceContext.Services; - _methodsByService = _serviceByName.ToDictionary( - keySelector: item => item.Value, - elementSelector: item => new MethodDescriptorCollection(item.Value.ServerType)); + _methodsByService = _serviceByName.ToDictionary(item => item.Value, item => new MethodDescriptorCollection(item.Value.ServerType)); Peers = new PeerCollection(instanceContext); _timer = new Timer(Timer_TimerCallback, null, TimeSpan.Zero, Timeout); } - event EventHandler? IAdaptor.Disconnected - { - add => _disconnectedEventHandler += value; - remove => _disconnectedEventHandler -= value; - } - public PeerCollection Peers { get; } public Guid Id => _serviceContext.Id; - public async Task OpenAsync( - OpenRequest request, ServerCallContext context, CancellationToken cancellationToken) + public async Task OpenAsync(OpenRequest request, ServerCallContext context, CancellationToken cancellationToken) { var id = GetId(context); if (Peers.TryGetValue(id, out var peer) == true) - { throw new InvalidOperationException($"The peer '{id}' already exists."); - } Peers.Add(_serviceContext, id); - await Task.Delay(1, cancellationToken); + await Task.CompletedTask; return new OpenReply(); } - public async Task CloseAsync( - CloseRequest request, ServerCallContext context, CancellationToken cancellationToken) + public async Task CloseAsync(CloseRequest request, ServerCallContext context, CancellationToken cancellationToken) { var id = GetId(context); if (Peers.TryGetValue(id, out var peer) != true) - { throw new InvalidOperationException($"The peer '{id}' does not exists."); - } Peers.Remove(_serviceContext, id, closeCode: 0); - await Task.Delay(1, cancellationToken); + await Task.CompletedTask; return new CloseReply(); } @@ -95,9 +100,7 @@ public async Task PingAsync(PingRequest request, ServerCallContext co var id = GetId(context); var dateTime = DateTime.UtcNow; if (Peers.TryGetValue(id, out var peer) != true) - { throw new InvalidOperationException($"The peer '{id}' does not exists."); - } peer.PingTime = dateTime; _serviceContext.Debug($"{id} Ping({dateTime})"); @@ -108,74 +111,53 @@ public async Task PingAsync(PingRequest request, ServerCallContext co public async Task InvokeAsync(InvokeRequest request, ServerCallContext context) { if (_serializer == null) - { throw new InvalidOperationException("Serializer is not set."); - } - - var serviceName = request.ServiceName; - if (_serviceByName.TryGetValue(serviceName, out var service) != true) - { - throw new InvalidOperationException( - $"Service '{serviceName}' does not exists."); - } - + if (_serviceByName.ContainsKey(request.ServiceName) != true) + throw new InvalidOperationException($"Service '{request.ServiceName}' does not exists."); + var service = _serviceByName[request.ServiceName]; var methodDescriptors = _methodsByService[service]; if (methodDescriptors.Contains(request.Name) != true) - { throw new InvalidOperationException($"Method '{request.Name}' does not exists."); - } var id = GetId(context); if (Peers.TryGetValue(id, out var peer) != true) - { throw new InvalidOperationException($"The peer '{id}' does not exists."); - } var methodDescriptor = methodDescriptors[request.Name]; - var isCancelable = methodDescriptor.IsCancelable; - var cancellationToken = isCancelable ? (CancellationToken?)context.CancellationToken : null; + var cancellationToken = methodDescriptor.IsCancelable == true ? (CancellationToken?)context.CancellationToken : null; var instance = peer.Services[service]; - var args = _serializer.DeserializeMany( - types: methodDescriptor.ParameterTypes, - datas: [.. request.Data], - cancellationToken: cancellationToken); + var args = _serializer.DeserializeMany(methodDescriptor.ParameterTypes, [.. request.Data], cancellationToken); if (methodDescriptor.IsOneWay == true) { + methodDescriptor.InvokeOneWay(_serviceContext, instance, args); var reply = new InvokeReply() { ID = string.Empty, - Data = _serializer.Serialize(typeof(void), null), + Data = _serializer.Serialize(typeof(void), null) }; - var methodShortName = methodDescriptor.ShortName; - methodDescriptor.InvokeOneWay(_serviceContext, instance, args); - _serviceContext.Debug($"{id} Invoke(one way): {serviceName}.{methodShortName}"); + _serviceContext.Debug($"{id} Invoke(one way): {request.ServiceName}.{methodDescriptor.ShortName}"); return reply; } else { - var result = await methodDescriptor.InvokeAsync(_serviceContext, instance, args); + var (assemblyQualifiedName, valueType, value) = await methodDescriptor.InvokeAsync(_serviceContext, instance, args); var reply = new InvokeReply() { - ID = result.AssemblyQualifiedName, - Data = _serializer.Serialize(result.ValueType, result.Value), + ID = $"{assemblyQualifiedName}", + Data = _serializer.Serialize(valueType, value) }; - _serviceContext.Debug($"{id} Invoke: {methodDescriptor.Name}"); + _serviceContext.Debug($"{id} Invoke: {request.ServiceName}.{methodDescriptor.ShortName}"); return reply; } } - public async Task PollAsync( - IAsyncStreamReader requestStream, - IServerStreamWriter responseStream, - ServerCallContext context) + public async Task PollAsync(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) { var id = GetId(context); if (Peers.TryGetValue(id, out var peer) != true) - { throw new InvalidOperationException($"The peer '{id}' does not exists."); - } using var manualResetEvent = new ManualResetEvent(initialState: true); var cancellationToken = peer.BeginPolling(manualResetEvent); @@ -186,15 +168,9 @@ public async Task PollAsync( var reply = peer.Collect(); await responseStream.WriteAsync(reply); if (cancellationToken.IsCancellationRequested == true) - { break; - } - if (peer.CanCollect == true) - { continue; - } - manualResetEvent.Reset(); manualResetEvent.WaitOne(PollTimeout); } @@ -203,7 +179,6 @@ public async Task PollAsync( { _serviceContext.Error(e.Message); } - peer.EndPolling(); _serviceContext.Debug("Poll finished."); @@ -220,6 +195,44 @@ public async ValueTask DisposeAsync() GC.SuppressFinalize(this); } + private static string GetId(ServerCallContext context) + { + if (context.RequestHeaders.Get("id") is { } entry) + { + return entry.Value; + } + + throw new ArgumentException("The id is not found."); + } + + private void AddCallback(InstanceBase instance, string name, Type[] types, object?[] args) + { + if (_serializer == null) + throw new UnreachableException("Serializer is not set."); + + var data = _serializer.SerializeMany(types, args); + var peers = instance.Peer is not Peer peer ? Peers.ToArray().Select(item => item.Value) : new Peer[] { peer }; + var service = instance.Service; + var callbackData = new CallbackData(service, name, data); + Parallel.ForEach(peers, item => item.AddCallback(callbackData)); + } + + private void Timer_TimerCallback(object? state) + { + var dateTime = DateTime.UtcNow; + var peers = Peers.ToArray(); + var query = from item in peers + let peer = item.Value + where dateTime - peer.PingTime > Timeout + select peer; + foreach (var item in query) + { + Peers.Remove(_serviceContext, item.Id, closeCode: -1); + } + } + + #region IAdaptor + #if NETSTANDARD async Task IAdaptor.OpenAsync(EndPoint endPoint, CancellationToken cancellationToken) { @@ -299,73 +312,36 @@ async Task IAdaptor.CloseAsync(CancellationToken cancellationToken) } #endif - void IAdaptor.Invoke(InvokeOptions options) + void IAdaptor.Invoke(InstanceBase instance, string name, Type[] types, object?[] args) { - AddCallback(options); + AddCallback(instance, name, types, args); } - void IAdaptor.InvokeOneWay(InvokeOptions options) + void IAdaptor.InvokeOneWay(InstanceBase instance, string name, Type[] types, object?[] args) { - AddCallback(options); + AddCallback(instance, name, types, args); } - T IAdaptor.Invoke(InvokeOptions options) + T IAdaptor.Invoke(InstanceBase instance, string name, Type[] types, object?[] args) { - throw new NotSupportedException( - $"This method '{nameof(IAdaptor.Invoke)}' is not supported."); + throw new NotSupportedException($"This method '{nameof(IAdaptor.Invoke)}' is not supported."); } - Task IAdaptor.InvokeAsync(InvokeOptions options, CancellationToken cancellationToken) + Task IAdaptor.InvokeAsync(InstanceBase instance, string name, Type[] types, object?[] args, CancellationToken cancellationToken) { - throw new NotSupportedException( - $"This method '{nameof(IAdaptor.InvokeAsync)}' is not supported."); + throw new NotSupportedException($"This method '{nameof(IAdaptor.InvokeAsync)}' is not supported."); } - Task IAdaptor.InvokeAsync(InvokeOptions options, CancellationToken cancellationToken) + Task IAdaptor.InvokeAsync(InstanceBase instance, string name, Type[] types, object?[] args, CancellationToken cancellationToken) { - throw new NotSupportedException( - $"This method '{nameof(IAdaptor.InvokeAsync)}' is not supported."); - } - - private static string GetId(ServerCallContext context) - { - if (context.RequestHeaders.Get("id") is { } entry) - { - return entry.Value; - } - - throw new ArgumentException("The id is not found."); + throw new NotSupportedException($"This method '{nameof(IAdaptor.InvokeAsync)}' is not supported."); } - private void AddCallback(InvokeOptions options) + event EventHandler? IAdaptor.Disconnected { - if (_serializer == null) - { - throw new UnreachableException("Serializer is not set."); - } - - var instance = options.Instance; - var name = options.Name; - var types = options.Types; - var args = options.Args; - var data = _serializer.SerializeMany(types, args); - var peers = instance.Peer is not Peer peer ? Peers.Select(item => item.Value) : [peer]; - var service = instance.Service; - var callbackData = new CallbackData(service, name, data); - Parallel.ForEach(peers, item => item.AddCallback(callbackData)); + add => _disconnectedEventHandler += value; + remove => _disconnectedEventHandler -= value; } - private void Timer_TimerCallback(object? state) - { - var dateTime = DateTime.UtcNow; - var peers = Peers.ToArray(); - var query = from item in peers - let peer = item.Value - where dateTime - peer.PingTime > Timeout - select peer; - foreach (var item in query) - { - Peers.Remove(_serviceContext, item.Id, closeCode: -1); - } - } + #endregion } diff --git a/src/JSSoft.Communication/Grpc/AdaptorServerImpl.cs b/src/JSSoft.Communication/Grpc/AdaptorServerImpl.cs index 160e08e..03450f7 100644 --- a/src/JSSoft.Communication/Grpc/AdaptorServerImpl.cs +++ b/src/JSSoft.Communication/Grpc/AdaptorServerImpl.cs @@ -1,14 +1,31 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. -using System.Threading.Tasks; using Grpc.Core; +using System.Threading.Tasks; namespace JSSoft.Communication.Grpc; -internal sealed class AdaptorServerImpl(AdaptorServer adaptorServer) : Adaptor.AdaptorBase +sealed class AdaptorServerImpl(AdaptorServer adaptorServer) : Adaptor.AdaptorBase { private readonly AdaptorServer _adaptorServer = adaptorServer; @@ -24,9 +41,6 @@ public override Task Ping(PingRequest request, ServerCallContext cont public override Task Invoke(InvokeRequest request, ServerCallContext context) => _adaptorServer.InvokeAsync(request, context); - public override Task Poll( - IAsyncStreamReader requestStream, - IServerStreamWriter responseStream, - ServerCallContext context) + public override Task Poll(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) => _adaptorServer.PollAsync(requestStream, responseStream, context); } diff --git a/src/JSSoft.Communication/Grpc/CallbackData.cs b/src/JSSoft.Communication/Grpc/CallbackData.cs deleted file mode 100644 index a0aa55c..0000000 --- a/src/JSSoft.Communication/Grpc/CallbackData.cs +++ /dev/null @@ -1,8 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -namespace JSSoft.Communication.Grpc; - -internal record struct CallbackData(IService Service, string Name, string[] Data); diff --git a/src/JSSoft.Communication/Grpc/Peer.cs b/src/JSSoft.Communication/Grpc/Peer.cs index 320cb49..886f4dc 100644 --- a/src/JSSoft.Communication/Grpc/Peer.cs +++ b/src/JSSoft.Communication/Grpc/Peer.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Generic; @@ -9,7 +26,7 @@ namespace JSSoft.Communication.Grpc; -internal sealed class Peer(string id) : IPeer +sealed class Peer(string id) : IPeer { private readonly object _lockObject = new(); private readonly List _callbackDataList = []; @@ -26,8 +43,6 @@ internal sealed class Peer(string id) : IPeer public int CloseCode { get; private set; } = int.MinValue; - public bool CanCollect => _callbackDataList.Count > 0; - public CancellationToken BeginPolling(ManualResetEvent manualResetEvent) { lock (_lockObject) @@ -53,7 +68,6 @@ public void Disconect(int closeCode) { CloseCode = closeCode; _cancellationTokenSource?.Cancel(); - _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; _manualResetEvent?.Set(); } @@ -77,10 +91,11 @@ public PollReply Collect() Data = { item.Data }, }); } - return reply; } + public bool CanCollect => _callbackDataList.Count > 0; + public void AddCallback(CallbackData callbackData) { lock (_lockObject) @@ -100,7 +115,6 @@ private CallbackData[] Flush() _callbackDataList.Clear(); return items; } - return []; } } diff --git a/src/JSSoft.Communication/Grpc/PeerCollection.cs b/src/JSSoft.Communication/Grpc/PeerCollection.cs index 32a8be8..07c5eec 100644 --- a/src/JSSoft.Communication/Grpc/PeerCollection.cs +++ b/src/JSSoft.Communication/Grpc/PeerCollection.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Concurrent; @@ -13,7 +30,7 @@ namespace JSSoft.Communication.Grpc; -internal sealed class PeerCollection(IInstanceContext instanceContext) +sealed class PeerCollection(IInstanceContext instanceContext) : ConcurrentDictionary { private readonly IInstanceContext _instanceContext = instanceContext; @@ -42,12 +59,10 @@ public bool Remove(IServiceContext serviceContext, string id, int closeCode) serviceContext.Debug($"{id} Disconnected ({closeCode})"); return true; } - return false; } - public async Task DisconnectAsync( - IServiceContext serviceContext, CancellationToken cancellationToken) + public async Task DisconnectAsync(IServiceContext serviceContext, CancellationToken cancellationToken) { var items = Values.ToArray(); using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 3000); @@ -55,12 +70,10 @@ public async Task DisconnectAsync( { item.Disconect(closeCode: 0); } - while (Count > 0 && cancellationTokenSource.IsCancellationRequested != true) { await Task.Delay(1, cancellationToken); } - Clear(); } } diff --git a/src/JSSoft.Communication/IAdaptor.cs b/src/JSSoft.Communication/IAdaptor.cs index 0c37c31..92956b9 100644 --- a/src/JSSoft.Communication/IAdaptor.cs +++ b/src/JSSoft.Communication/IAdaptor.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Net; @@ -12,19 +29,19 @@ namespace JSSoft.Communication; public interface IAdaptor : IAsyncDisposable { - event EventHandler? Disconnected; - Task OpenAsync(EndPoint endPoint, CancellationToken cancellationToken); Task CloseAsync(CancellationToken cancellationToken); - void InvokeOneWay(InvokeOptions options); + void InvokeOneWay(InstanceBase instance, string name, Type[] types, object?[] args); - void Invoke(InvokeOptions options); + void Invoke(InstanceBase instance, string name, Type[] types, object?[] args); - T Invoke(InvokeOptions options); + T Invoke(InstanceBase instance, string name, Type[] types, object?[] args); - Task InvokeAsync(InvokeOptions options, CancellationToken cancellationToken); + Task InvokeAsync(InstanceBase instance, string name, Type[] types, object?[] args, CancellationToken cancellationToken); - Task InvokeAsync(InvokeOptions options, CancellationToken cancellationToken); + Task InvokeAsync(InstanceBase instance, string name, Type[] types, object?[] args, CancellationToken cancellationToken); + + event EventHandler? Disconnected; } diff --git a/src/JSSoft.Communication/IAdaptorProvider.cs b/src/JSSoft.Communication/IAdaptorProvider.cs index 8b55ae9..92c4eda 100644 --- a/src/JSSoft.Communication/IAdaptorProvider.cs +++ b/src/JSSoft.Communication/IAdaptorProvider.cs @@ -1,16 +1,30 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication; public interface IAdaptorProvider { - string Name { get; } + IAdaptor Create(IServiceContext serviceContext, IInstanceContext instanceContext, ServiceToken serviceToken); - IAdaptor Create( - IServiceContext serviceContext, - IInstanceContext instanceContext, - ServiceToken serviceToken); + string Name { get; } } diff --git a/src/JSSoft.Communication/IInstanceContext.cs b/src/JSSoft.Communication/IInstanceContext.cs index 6419a62..011375e 100644 --- a/src/JSSoft.Communication/IInstanceContext.cs +++ b/src/JSSoft.Communication/IInstanceContext.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication; diff --git a/src/JSSoft.Communication/ILGeneratorExtensions.cs b/src/JSSoft.Communication/ILGeneratorExtensions.cs index 47a2512..3d927e5 100644 --- a/src/JSSoft.Communication/ILGeneratorExtensions.cs +++ b/src/JSSoft.Communication/ILGeneratorExtensions.cs @@ -1,30 +1,40 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System.Reflection.Emit; namespace JSSoft.Communication; -internal static class ILGeneratorExtensions +static class ILGeneratorExtensions { - private static readonly OpCode[] LdcI4 = - [ - OpCodes.Ldc_I4_0, OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_3, - OpCodes.Ldc_I4_4, OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7, OpCodes.Ldc_I4_8, - ]; - - private static readonly OpCode[] Ldarg = - [ - OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2, OpCodes.Ldarg_3, - ]; + private static readonly OpCode[] Ldc_I4 = [ OpCodes.Ldc_I4_0, OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_3, + OpCodes.Ldc_I4_4, OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7, OpCodes.Ldc_I4_8 ]; + private static readonly OpCode[] Ldarg = [OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2, OpCodes.Ldarg_3]; public static void EmitLdc_I4(this ILGenerator il, int n) { - if (n < LdcI4.Length) + if (n < Ldc_I4.Length) { - il.Emit(LdcI4[n]); + il.Emit(Ldc_I4[n]); } else { diff --git a/src/JSSoft.Communication/IPeer.cs b/src/JSSoft.Communication/IPeer.cs index d8ec6c3..6b49611 100644 --- a/src/JSSoft.Communication/IPeer.cs +++ b/src/JSSoft.Communication/IPeer.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication; diff --git a/src/JSSoft.Communication/ISerializer.cs b/src/JSSoft.Communication/ISerializer.cs index f35bf2c..0ea92f7 100644 --- a/src/JSSoft.Communication/ISerializer.cs +++ b/src/JSSoft.Communication/ISerializer.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; diff --git a/src/JSSoft.Communication/ISerializerProvider.cs b/src/JSSoft.Communication/ISerializerProvider.cs index 058308f..e994335 100644 --- a/src/JSSoft.Communication/ISerializerProvider.cs +++ b/src/JSSoft.Communication/ISerializerProvider.cs @@ -1,13 +1,30 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication; public interface ISerializerProvider { - string Name { get; } - ISerializer Create(IServiceContext serviceContext); + + string Name { get; } } diff --git a/src/JSSoft.Communication/IService.cs b/src/JSSoft.Communication/IService.cs index b8f624e..bfb77df 100644 --- a/src/JSSoft.Communication/IService.cs +++ b/src/JSSoft.Communication/IService.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; diff --git a/src/JSSoft.Communication/IServiceContext.cs b/src/JSSoft.Communication/IServiceContext.cs index 39eb5ae..20c4658 100644 --- a/src/JSSoft.Communication/IServiceContext.cs +++ b/src/JSSoft.Communication/IServiceContext.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Generic; @@ -13,16 +30,6 @@ namespace JSSoft.Communication; public interface IServiceContext : IServiceProvider { - event EventHandler? Opened; - - event EventHandler? Closed; - - event EventHandler? Faulted; - - event EventHandler? Disconnected; - - event EventHandler? ServiceStateChanged; - IReadOnlyDictionary Services { get; } EndPoint EndPoint { get; set; } @@ -36,4 +43,14 @@ public interface IServiceContext : IServiceProvider Task CloseAsync(Guid token, CancellationToken cancellationToken); Task AbortAsync(); + + event EventHandler? Opened; + + event EventHandler? Closed; + + event EventHandler? Faulted; + + event EventHandler? Disconnected; + + event EventHandler? ServiceStateChanged; } diff --git a/src/JSSoft.Communication/InstanceBase.cs b/src/JSSoft.Communication/InstanceBase.cs index dbf3259..bcd4bdb 100644 --- a/src/JSSoft.Communication/InstanceBase.cs +++ b/src/JSSoft.Communication/InstanceBase.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Reflection; @@ -24,8 +41,6 @@ public abstract class InstanceBase private IService? _service; private IPeer? _peer; - internal static InstanceBase Empty { get; } = new Instance(); - internal IAdaptor Adaptor { get => _adaptor ?? throw new InvalidOperationException("adaptor is not set."); @@ -46,118 +61,60 @@ internal IPeer Peer set => _peer = value; } + internal static InstanceBase Empty { get; } = new Instance(); + [InstanceMethod(InvokeMethod)] protected void Invoke(string name, Type[] types, object?[] args) - => Adaptor.Invoke(new InvokeOptions - { - Instance = this, - Name = name, - Types = types, - Args = args, - }); - - protected void Invoke((string Name, Type[] Types, object?[] Args) info) - => Adaptor.Invoke(new InvokeOptions - { - Instance = this, - Name = info.Name, - Types = info.Types, - Args = info.Args, - }); + => Adaptor.Invoke(this, name, types, args); - [InstanceMethod(InvokeGenericMethod)] - protected T Invoke(string name, Type[] types, object?[] args) - => Adaptor.Invoke(new InvokeOptions - { - Instance = this, - Name = name, - Types = types, - Args = args, - }); - - protected T Invoke((string Name, Type[] Types, object?[] Args) info) - => Adaptor.Invoke(new InvokeOptions - { - Instance = this, - Name = info.Name, - Types = info.Types, - Args = info.Args, - }); + protected void Invoke((string name, Type[] types, object?[] args) info) + => Adaptor.Invoke(this, info.name, info.types, info.args); [InstanceMethod(InvokeOneWayMethod)] protected void InvokeOneWay(string name, Type[] types, object?[] args) - => Adaptor.InvokeOneWay(new InvokeOptions - { - Instance = this, - Name = name, - Types = types, - Args = args, - }); - - protected void InvokeOneWay((string Name, Type[] Types, object?[] Args) info) - => Adaptor.Invoke(new InvokeOptions - { - Instance = this, - Name = info.Name, - Types = info.Types, - Args = info.Args, - }); + => Adaptor.InvokeOneWay(this, name, types, args); + + protected void InvokeOneWay((string name, Type[] types, object?[] args) info) + => Adaptor.Invoke(this, info.name, info.types, info.args); + + [InstanceMethod(InvokeGenericMethod)] + protected T Invoke(string name, Type[] types, object?[] args) + => Adaptor.Invoke(this, name, types, args); + + protected T Invoke((string name, Type[] types, object?[] args) info) + => Adaptor.Invoke(this, info.name, info.types, info.args); [InstanceMethod(InvokeAsyncMethod)] - protected Task InvokeAsync( - string name, Type[] types, object?[] args, CancellationToken cancellationToken) - { - var options = new InvokeOptions - { - Instance = this, - Name = name, - Types = types, - Args = args, - }; - return Adaptor.InvokeAsync(options, cancellationToken); - } + protected Task InvokeAsync(string name, Type[] types, object?[] args, CancellationToken cancellationToken) + => Adaptor.InvokeAsync(this, name, types, args, cancellationToken); - protected Task InvokeAsync( - (string Name, Type[] Types, object?[] Args) info, CancellationToken cancellationToken) - { - var options = new InvokeOptions - { - Instance = this, - Name = info.Name, - Types = info.Types, - Args = info.Args, - }; - return Adaptor.InvokeAsync(options, cancellationToken); - } + protected Task InvokeAsync((string name, Type[] types, object?[] args) info, CancellationToken cancellationToken) + => Adaptor.InvokeAsync(this, info.name, info.types, info.args, cancellationToken); [InstanceMethod(InvokeGenericAsyncMethod)] - protected Task InvokeAsync( - string name, Type[] types, object?[] args, CancellationToken cancellationToken) - { - var options = new InvokeOptions - { - Instance = this, - Name = name, - Types = types, - Args = args, - }; - return Adaptor.InvokeAsync(options, cancellationToken); - } + protected Task InvokeAsync(string name, Type[] types, object?[] args, CancellationToken cancellationToken) + => Adaptor.InvokeAsync(this, name, types, args, cancellationToken); - protected Task InvokeAsync( - (string Name, Type[] Types, object?[] Args) info, CancellationToken cancellationToken) - { - var options = new InvokeOptions - { - Instance = this, - Name = info.Name, - Types = info.Types, - Args = info.Args, - }; - return Adaptor.InvokeAsync(options, cancellationToken); - } + protected Task InvokeAsync((string name, Type[] types, object?[] args) info, CancellationToken cancellationToken) + => Adaptor.InvokeAsync(this, info.name, info.types, info.args, cancellationToken); - private sealed class Instance : InstanceBase + protected static (string, Type[], object?[]) Info

(MethodInfo methodInfo, Type serviceType, P arg) + => (MethodUtility.GenerateName(methodInfo, serviceType), [typeof(P)], [arg]); + + protected static (string, Type[], object?[]) Info(MethodInfo methodInfo, Type serviceType, P1 arg1, P2 arg2) + => (MethodUtility.GenerateName(methodInfo, serviceType), [typeof(P1), typeof(P2)], [arg1, arg2]); + + protected static (string, Type[], object?[]) Info(MethodInfo methodInfo, Type serviceType, P1 arg1, P2 arg2, P3 arg3) + => (MethodUtility.GenerateName(methodInfo, serviceType), [typeof(P1), typeof(P2), typeof(P3)], [arg1, arg2, arg3]); + + protected static (string, Type[], object?[]) Info(MethodInfo methodInfo, Type serviceType, P1 arg1, P2 arg2, P3 arg3, P4 arg4) + => (MethodUtility.GenerateName(methodInfo, serviceType), [typeof(P1), typeof(P2), typeof(P3), typeof(P4)], [arg1, arg2, arg3, arg4]); + + #region Instance + + sealed class Instance : InstanceBase { } + + #endregion } diff --git a/src/JSSoft.Communication/InstanceCollection.cs b/src/JSSoft.Communication/InstanceCollection.cs index 5a52769..112465a 100644 --- a/src/JSSoft.Communication/InstanceCollection.cs +++ b/src/JSSoft.Communication/InstanceCollection.cs @@ -1,12 +1,29 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System.Collections.Generic; namespace JSSoft.Communication; -internal sealed class InstanceCollection : Dictionary +sealed class InstanceCollection : Dictionary { } diff --git a/src/JSSoft.Communication/InstanceContext.cs b/src/JSSoft.Communication/InstanceContext.cs index 279dc4d..a509d99 100644 --- a/src/JSSoft.Communication/InstanceContext.cs +++ b/src/JSSoft.Communication/InstanceContext.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Concurrent; @@ -9,7 +26,7 @@ namespace JSSoft.Communication; -internal sealed class InstanceContext(ServiceContextBase serviceContext) +sealed class InstanceContext(ServiceContextBase serviceContext) : IInstanceContext, IPeer { private readonly ConcurrentDictionary _descriptorByPeer = new(); @@ -32,6 +49,7 @@ where ServiceContextBase.IsPerPeer(_serviceContext, item) != true public void ReleaseInstance() { + var isServer = ServiceContextBase.IsServer(_serviceContext); var query = from item in _serviceContext.Services.Values.Reverse() where ServiceContextBase.IsPerPeer(_serviceContext, item) != true select item; @@ -55,12 +73,10 @@ public PeerDescriptor CreateInstance(IPeer peer) } else { - var service = _descriptor.ServerInstances[item]; - var callback = _descriptor.ClientInstances[item]; + var (service, callback) = (_descriptor.ServerInstances[item], _descriptor.ClientInstances[item]); peerDescriptor.AddInstance(item, service, callback); } } - _descriptorByPeer.TryAdd(peer, peerDescriptor); return peerDescriptor; } @@ -68,10 +84,7 @@ public PeerDescriptor CreateInstance(IPeer peer) public void DestroyInstance(IPeer peer) { if (_descriptorByPeer.TryRemove(peer, out var peerDescriptor) == false) - { return; - } - foreach (var item in _serviceContext.Services.Values.Reverse()) { var isPerPeer = ServiceContextBase.IsPerPeer(_serviceContext, item); @@ -85,15 +98,15 @@ public void DestroyInstance(IPeer peer) peerDescriptor.RemoveInstance(item); } } - peerDescriptor.Dispose(); + } public object? GetService(Type serviceType) { var query = from descriptor in _descriptorByPeer.Values from service in descriptor.ServerInstances.Values - where serviceType.IsInstanceOfType(service) == true + where serviceType.IsAssignableFrom(service.GetType()) == true select service; return query.SingleOrDefault(); } diff --git a/src/JSSoft.Communication/InstanceMethodAttribute.cs b/src/JSSoft.Communication/InstanceMethodAttribute.cs index e170909..d200b29 100644 --- a/src/JSSoft.Communication/InstanceMethodAttribute.cs +++ b/src/JSSoft.Communication/InstanceMethodAttribute.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; diff --git a/src/JSSoft.Communication/InvokeOptions.cs b/src/JSSoft.Communication/InvokeOptions.cs deleted file mode 100644 index 7763754..0000000 --- a/src/JSSoft.Communication/InvokeOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; - -namespace JSSoft.Communication; - -public sealed record class InvokeOptions -{ - public required InstanceBase Instance { get; init; } - - public string Name { get; init; } = string.Empty; - - public Type[] Types { get; init; } = []; - - public object?[] Args { get; init; } = []; -} diff --git a/src/JSSoft.Communication/InvokeResult.cs b/src/JSSoft.Communication/InvokeResult.cs deleted file mode 100644 index 1c1f275..0000000 --- a/src/JSSoft.Communication/InvokeResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; - -namespace JSSoft.Communication; - -public sealed record class InvokeResult -{ - public required string AssemblyQualifiedName { get; init; } - - public required Type ValueType { get; init; } - - public object? Value { get; init; } -} diff --git a/src/JSSoft.Communication/JsonSerializer.cs b/src/JSSoft.Communication/JsonSerializer.cs index 86b66b8..c787228 100644 --- a/src/JSSoft.Communication/JsonSerializer.cs +++ b/src/JSSoft.Communication/JsonSerializer.cs @@ -1,20 +1,37 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. -using System; using Newtonsoft.Json; +using System; namespace JSSoft.Communication; -internal sealed class JsonSerializer : ISerializer +sealed class JsonSerializer : ISerializer { - private static readonly JsonSerializerSettings Settings = new(); + private static readonly JsonSerializerSettings settings = new(); public string Serialize(Type type, object? data) - => JsonConvert.SerializeObject(data, type, Settings); + => JsonConvert.SerializeObject(data, type, settings); public object? Deserialize(Type type, string text) - => JsonConvert.DeserializeObject(text, type, Settings); + => JsonConvert.DeserializeObject(text, type, settings); } diff --git a/src/JSSoft.Communication/JsonSerializerProvider.cs b/src/JSSoft.Communication/JsonSerializerProvider.cs index fadb296..f795181 100644 --- a/src/JSSoft.Communication/JsonSerializerProvider.cs +++ b/src/JSSoft.Communication/JsonSerializerProvider.cs @@ -1,18 +1,35 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication; -internal sealed class JsonSerializerProvider : ISerializerProvider +sealed class JsonSerializerProvider : ISerializerProvider { public const string DefaultName = "json"; - public static readonly JsonSerializerProvider Default = new(); + public ISerializer Create(IServiceContext serviceContext) + => new JsonSerializer(); public string Name => DefaultName; - public ISerializer Create(IServiceContext serviceContext) - => new JsonSerializer(); + public static readonly JsonSerializerProvider Default = new(); } diff --git a/src/JSSoft.Communication/Logging/ConsoleLogger.cs b/src/JSSoft.Communication/Logging/ConsoleLogger.cs index a17b431..1f0fddb 100644 --- a/src/JSSoft.Communication/Logging/ConsoleLogger.cs +++ b/src/JSSoft.Communication/Logging/ConsoleLogger.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; @@ -9,8 +26,6 @@ namespace JSSoft.Communication.Logging; public sealed class ConsoleLogger : ILogger { - public static readonly ConsoleLogger Default = new(); - public void Debug(object message) => Console.WriteLine(message); public void Info(object message) => Console.WriteLine(message); @@ -20,4 +35,6 @@ public sealed class ConsoleLogger : ILogger public void Warn(object message) => Console.WriteLine(message); public void Fatal(object message) => Console.Error.WriteLine(message); + + public static readonly ConsoleLogger Default = new(); } diff --git a/src/JSSoft.Communication/Logging/EmptyLogger.cs b/src/JSSoft.Communication/Logging/EmptyLogger.cs index 3bb334e..922c411 100644 --- a/src/JSSoft.Communication/Logging/EmptyLogger.cs +++ b/src/JSSoft.Communication/Logging/EmptyLogger.cs @@ -1,14 +1,29 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Logging; public class EmptyLogger : ILogger { - public static readonly EmptyLogger Default = new(); - public void Debug(object message) { } @@ -28,4 +43,6 @@ public void Warn(object message) public void Fatal(object message) { } + + public static readonly EmptyLogger Default = new(); } diff --git a/src/JSSoft.Communication/Logging/ILogger.cs b/src/JSSoft.Communication/Logging/ILogger.cs index 3d8cb67..2081175 100644 --- a/src/JSSoft.Communication/Logging/ILogger.cs +++ b/src/JSSoft.Communication/Logging/ILogger.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Logging; diff --git a/src/JSSoft.Communication/Logging/ILoggerExtensions.cs b/src/JSSoft.Communication/Logging/ILoggerExtensions.cs index 6f8b66b..e24b65d 100644 --- a/src/JSSoft.Communication/Logging/ILoggerExtensions.cs +++ b/src/JSSoft.Communication/Logging/ILoggerExtensions.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; diff --git a/src/JSSoft.Communication/Logging/LogLevel.cs b/src/JSSoft.Communication/Logging/LogLevel.cs index 1981a1a..b1df4fc 100644 --- a/src/JSSoft.Communication/Logging/LogLevel.cs +++ b/src/JSSoft.Communication/Logging/LogLevel.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Logging; diff --git a/src/JSSoft.Communication/Logging/LogUtility.cs b/src/JSSoft.Communication/Logging/LogUtility.cs index f224fae..f8cccd4 100644 --- a/src/JSSoft.Communication/Logging/LogUtility.cs +++ b/src/JSSoft.Communication/Logging/LogUtility.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Logging; @@ -11,45 +28,35 @@ public static class LogUtility public static LogLevel LogLevel { get; set; } = LogLevel.Fatal; - private static ILogger LoggerInternal => Logger ?? EmptyLogger.Default; - public static void Debug(object message) { if (LogLevel >= LogLevel.Debug) - { LoggerInternal.Debug(message); - } } public static void Info(object message) { if (LogLevel >= LogLevel.Info) - { LoggerInternal.Info(message); - } } public static void Error(object message) { if (LogLevel >= LogLevel.Error) - { LoggerInternal.Error(message); - } } public static void Warn(object message) { if (LogLevel >= LogLevel.Warn) - { LoggerInternal.Warn(message); - } } public static void Fatal(object message) { if (LogLevel >= LogLevel.Fatal) - { LoggerInternal.Fatal(message); - } } + + private static ILogger LoggerInternal => Logger ?? EmptyLogger.Default; } diff --git a/src/JSSoft.Communication/Logging/TraceLogger.cs b/src/JSSoft.Communication/Logging/TraceLogger.cs index 5476dc2..e9ea38c 100644 --- a/src/JSSoft.Communication/Logging/TraceLogger.cs +++ b/src/JSSoft.Communication/Logging/TraceLogger.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System.Diagnostics; @@ -9,9 +26,7 @@ namespace JSSoft.Communication.Logging; public sealed class TraceLogger : ILogger { - public static readonly TraceLogger Default = new(); - - public void Debug(object message) => Trace.TraceInformation($"{message}"); + public void Debug(object message) => Trace.WriteLine(message); public void Info(object message) => Trace.TraceInformation($"{message}"); @@ -20,4 +35,6 @@ public sealed class TraceLogger : ILogger public void Warn(object message) => Trace.TraceWarning($"{message}"); public void Fatal(object message) => Trace.TraceError($"{message}"); + + public static readonly TraceLogger Default = new(); } diff --git a/src/JSSoft.Communication/MethodDescriptor.cs b/src/JSSoft.Communication/MethodDescriptor.cs index b80a9a1..585cbbd 100644 --- a/src/JSSoft.Communication/MethodDescriptor.cs +++ b/src/JSSoft.Communication/MethodDescriptor.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Linq; @@ -26,10 +43,9 @@ public MethodDescriptor(MethodInfo methodInfo) } else if (ReturnType.IsSubclassOf(typeof(Task)) == true) { - ReturnType = ReturnType.GetGenericArguments()[0]; + ReturnType = ReturnType.GetGenericArguments().First(); IsAsync = true; } - Name = GenerateName(methodInfo); ShortName = methodInfo.Name; IsOneWay = IsMethodOneWay(methodInfo); @@ -53,103 +69,59 @@ public MethodDescriptor(MethodInfo methodInfo) public MethodInfo MethodInfo { get; } - // "async" methods should not return "void" -#pragma warning disable S3168 - internal async void InvokeOneWay( - IServiceProvider serviceProvider, object instance, object?[] args) + private static void Verify(MethodInfo methodInfo) { - try + var parameterInfos = methodInfo.GetParameters(); + var isAsync = typeof(Task).IsAssignableFrom(methodInfo.ReturnType); + if (isAsync == true) { - await InvokeAsync(instance, args); + if (parameterInfos.Count(item => item.ParameterType == typeof(CancellationToken)) > 1) + throw new InvalidOperationException($"In method '{methodInfo}', only one parameter of type {nameof(CancellationToken)} should be defined."); + if (parameterInfos.Count(item => item.ParameterType == typeof(CancellationToken)) == 1 && + parameterInfos.Last().ParameterType != typeof(CancellationToken)) + throw new InvalidOperationException($"The last parameter of method '{methodInfo}' must be of type {nameof(CancellationToken)}."); } - catch + else if (methodInfo.ReturnType == typeof(void)) + { + if (parameterInfos.Any(item => item.ParameterType == typeof(CancellationToken)) == true) + throw new InvalidOperationException($"The {nameof(CancellationToken)} type cannot be used in method '{methodInfo}'."); + } + else { - // do nothing + throw new InvalidOperationException($"The return type of method '{methodInfo}' must be '{typeof(Task)}' or '{typeof(void)}'."); } } -#pragma warning restore S3168 - internal async Task InvokeAsync( - IServiceProvider serviceProvider, object instance, object?[] args) + internal async void InvokeOneWay(IServiceProvider serviceProvider, object instance, object?[] args) { try { - var (valueType, value) = await InvokeAsync(instance, args); - return new InvokeResult - { - AssemblyQualifiedName = string.Empty, - ValueType = valueType, - Value = value, - }; - } - catch (TargetInvocationException e) - { - var exception = e.InnerException ?? e; - return new InvokeResult - { - AssemblyQualifiedName = exception.GetType().AssemblyQualifiedName!, - ValueType = exception.GetType(), - Value = exception, - }; + await InvokeAsync(instance, args); } - catch (Exception e) + catch { - return new InvokeResult - { - AssemblyQualifiedName = e.GetType().AssemblyQualifiedName!, - ValueType = e.GetType(), - Value = e, - }; } } - private static void Verify(MethodInfo methodInfo) + internal async Task<(string, Type, object?)> InvokeAsync(IServiceProvider serviceProvider, object instance, object?[] args) { - var parameterInfos = methodInfo.GetParameters(); - var isAsync = typeof(Task).IsAssignableFrom(methodInfo.ReturnType); - if (isAsync == true) + try { - if (parameterInfos.Count(item => item.ParameterType == typeof(CancellationToken)) > 1) - { - var message = $""" - In method '{methodInfo}', only one parameter of type - {nameof(CancellationToken)} should be defined. - """; - throw new InvalidOperationException(message); - } - - if (parameterInfos.Count(item => item.ParameterType == typeof(CancellationToken)) == 1 - && parameterInfos.Last().ParameterType != typeof(CancellationToken)) - { - var message = $""" - The last parameter of method '{methodInfo}' must be of - type {nameof(CancellationToken)}. - """; - throw new InvalidOperationException(message); - } + var (type, value) = await InvokeAsync(instance, args); + return (string.Empty, type, value); } - else if (methodInfo.ReturnType == typeof(void)) + catch (TargetInvocationException e) { - if (parameterInfos.Any(item => item.ParameterType == typeof(CancellationToken)) == true) - { - var message = $""" - The {nameof(CancellationToken)} type cannot be used in method '{methodInfo}'. - """; - throw new InvalidOperationException(message); - } + var exception = e.InnerException ?? e; + return (exception.GetType().AssemblyQualifiedName!, exception.GetType(), exception); } - else + catch (Exception e) { - var message = $""" - The return type of method '{methodInfo}' must be - '{typeof(Task)}' or '{typeof(void)}'. - """; - throw new InvalidOperationException(message); + return (e.GetType().AssemblyQualifiedName!, e.GetType(), e); } } - private async Task<(Type ValueType, object? Value)> InvokeAsync( - object? instance, object?[] args) + private async Task<(Type, object?)> InvokeAsync(object? instance, object?[] args) { var value = await Task.Run(() => MethodInfo.Invoke(instance, args)); var valueType = MethodInfo.ReturnType; diff --git a/src/JSSoft.Communication/MethodDescriptorCollection.cs b/src/JSSoft.Communication/MethodDescriptorCollection.cs index bd98b86..3b4a78f 100644 --- a/src/JSSoft.Communication/MethodDescriptorCollection.cs +++ b/src/JSSoft.Communication/MethodDescriptorCollection.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections; @@ -35,13 +52,17 @@ internal MethodDescriptorCollection(Type type) public int Count => _discriptorByName.Count; + public bool Contains(string name) => _discriptorByName.ContainsKey(name); + public MethodDescriptor this[string name] => _discriptorByName[name]; - public bool Contains(string name) => _discriptorByName.ContainsKey(name); + #region IEnumerator public IEnumerator GetEnumerator() => _discriptorByName.Values.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _discriptorByName.Values.GetEnumerator(); + + #endregion } diff --git a/src/JSSoft.Communication/MethodUtility.cs b/src/JSSoft.Communication/MethodUtility.cs index 071fd82..646f519 100644 --- a/src/JSSoft.Communication/MethodUtility.cs +++ b/src/JSSoft.Communication/MethodUtility.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Linq; @@ -14,28 +31,17 @@ public static class MethodUtility { public static string GenerateName(MethodInfo methodInfo) { - var returnType = methodInfo.ReturnType; - var reflectedType = methodInfo.ReflectedType - ?? throw new ArgumentException("reflected type is null", nameof(methodInfo)); - var name = methodInfo.Name; - var parameterTypes = methodInfo.GetParameters() - .Select(item => item.ParameterType) - .ToArray(); - return GenerateName(returnType, reflectedType, name, parameterTypes); + var parameterTypes = methodInfo.GetParameters().Select(item => item.ParameterType).ToArray(); + return GenerateName(methodInfo.ReturnType, methodInfo.ReflectedType!, methodInfo.Name, parameterTypes); } public static string GenerateName(MethodInfo methodInfo, Type serviceType) { - var returnType = methodInfo.ReturnType; - var name = methodInfo.Name; - var parameterTypes = methodInfo.GetParameters() - .Select(item => item.ParameterType) - .ToArray(); - return GenerateName(returnType, serviceType, name, parameterTypes); + var parameterTypes = methodInfo.GetParameters().Select(item => item.ParameterType).ToArray(); + return GenerateName(methodInfo.ReturnType, serviceType, methodInfo.Name, parameterTypes); } - public static string GenerateName( - Type returnType, Type reflectedType, string methodName, params Type[] parameterTypes) + public static string GenerateName(Type returnType, Type reflectedType, string methodName, params Type[] parameterTypes) { var parameterTypeNames = string.Join(", ", parameterTypes); return $"{returnType} {reflectedType}.{methodName}({parameterTypeNames})"; @@ -49,12 +55,8 @@ public static bool IsMethodOneWay(MethodInfo methodInfo) public static bool IsMethodCancelable(MethodInfo methodInfo) { var parameterInfos = methodInfo.GetParameters(); - if (parameterInfos.Length > 0 - && parameterInfos[^1].ParameterType == typeof(CancellationToken)) - { + if (parameterInfos.Length > 0 && parameterInfos[parameterInfos.Length - 1].ParameterType == typeof(CancellationToken)) return true; - } - return false; } diff --git a/src/JSSoft.Communication/PeerDescriptor.cs b/src/JSSoft.Communication/PeerDescriptor.cs index f4a058f..1d04ef8 100644 --- a/src/JSSoft.Communication/PeerDescriptor.cs +++ b/src/JSSoft.Communication/PeerDescriptor.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Generic; @@ -19,34 +36,30 @@ public sealed class PeerDescriptor : IDisposable public void Dispose() { - ObjectDisposedException.ThrowIf(_isDisposed, this); + if (_isDisposed == true) + throw new ObjectDisposedException($"{this}"); var items = ClientInstances.Values.OfType().ToArray(); foreach (var item in items) { item.Dispose(); } - _isDisposed = true; } public void AddInstance(IService service, object serverInstance, object clientInstance) { if (_isDisposed == true) - { throw new ObjectDisposedException($"{this}"); - } ServerInstances.Add(service, serverInstance); ClientInstances.Add(service, clientInstance); } - public (object ServerInstance, object ClientInstance) RemoveInstance(IService service) + public (object serverInstance, object clientInstance) RemoveInstance(IService service) { if (_isDisposed == true) - { throw new ObjectDisposedException($"{this}"); - } var value = (ServerInstances[service], ClientInstances[service]); ServerInstances.Remove(service); diff --git a/src/JSSoft.Communication/ServerContext.cs b/src/JSSoft.Communication/ServerContext.cs index c0de9c5..6777716 100644 --- a/src/JSSoft.Communication/ServerContext.cs +++ b/src/JSSoft.Communication/ServerContext.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication; diff --git a/src/JSSoft.Communication/ServerService.cs b/src/JSSoft.Communication/ServerService.cs index a9a23e0..e7d9e0a 100644 --- a/src/JSSoft.Communication/ServerService.cs +++ b/src/JSSoft.Communication/ServerService.cs @@ -1,10 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -// File may only contain a single type -#pragma warning disable SA1402 +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; @@ -26,20 +40,18 @@ public ServerService(TServer server) public ServerService() : base(typeof(TServer), typeof(TClient)) { - var obj = this; - if (obj is TServer server) + if (this is TServer server) { _server = server; + } else { - throw new InvalidOperationException( - $"'{GetType()}' must be implemented by '{typeof(TServer)}'."); + throw new InvalidOperationException($"'{GetType()}' must be implemented by '{typeof(TServer)}'."); } } - public TClient Client - => _client ?? throw new InvalidOperationException("Client is not created."); + public TClient Client => _client ?? throw new InvalidOperationException("Client is not created."); protected virtual TServer CreateServer(IPeer peer) => _server; @@ -73,15 +85,13 @@ public ServerService(TServer server) public ServerService() : base(serverType: typeof(TServer), clientType: typeof(void)) { - var obj = this; - if (obj is TServer server) + if (this is TServer server) { _server = server; } else { - throw new InvalidOperationException( - $"'{GetType()}' must be implemented by '{typeof(TServer)}'."); + throw new InvalidOperationException($"'{GetType()}' must be implemented by '{typeof(TServer)}'."); } } diff --git a/src/JSSoft.Communication/ServiceAttribute.cs b/src/JSSoft.Communication/ServiceAttribute.cs index 70b04eb..619fc43 100644 --- a/src/JSSoft.Communication/ServiceAttribute.cs +++ b/src/JSSoft.Communication/ServiceAttribute.cs @@ -1,14 +1,31 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; namespace JSSoft.Communication; [AttributeUsage(AttributeTargets.Class)] -internal sealed class ServiceAttribute : Attribute +sealed class ServiceAttribute : Attribute { public bool IsServer { get; set; } } diff --git a/src/JSSoft.Communication/ServiceBase.cs b/src/JSSoft.Communication/ServiceBase.cs index 78deca2..e4aaa81 100644 --- a/src/JSSoft.Communication/ServiceBase.cs +++ b/src/JSSoft.Communication/ServiceBase.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; @@ -15,11 +32,15 @@ public abstract class ServiceBase(Type serverType, Type clientType) : IService public string Name { get; } = serverType.Name; + protected abstract object CreateInstance(IPeer peer, object obj); + + protected abstract void DestroyInstance(IPeer peer, object obj); + + #region IService + object IService.CreateInstance(IPeer peer, object obj) => CreateInstance(peer, obj); void IService.DestroyInstance(IPeer peer, object obj) => DestroyInstance(peer, obj); - protected abstract object CreateInstance(IPeer peer, object obj); - - protected abstract void DestroyInstance(IPeer peer, object obj); + #endregion } diff --git a/src/JSSoft.Communication/ServiceCollection.cs b/src/JSSoft.Communication/ServiceCollection.cs index 416e064..3ce341e 100644 --- a/src/JSSoft.Communication/ServiceCollection.cs +++ b/src/JSSoft.Communication/ServiceCollection.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System.Collections; using System.Collections.Generic; @@ -10,11 +27,11 @@ namespace JSSoft.Communication; -public class ServiceCollection(IEnumerable services) - : IReadOnlyDictionary +public class ServiceCollection(IEnumerable services) : IReadOnlyDictionary { - private readonly Dictionary _serviceByName - = services.ToDictionary(item => item.Name); + private readonly Dictionary _serviceByName = services.ToDictionary(item => item.Name); + + public IService this[string key] => _serviceByName[key]; public IEnumerable Keys => _serviceByName.Keys; @@ -22,8 +39,6 @@ private readonly Dictionary _serviceByName public int Count => _serviceByName.Count; - public IService this[string key] => _serviceByName[key]; - public bool ContainsKey(string key) => _serviceByName.ContainsKey(key); #if NETSTANDARD @@ -34,9 +49,13 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out IService value) => _serviceByName.TryGetValue(key, out value); #endif - public IEnumerator> GetEnumerator() + #region IEnumerable + + IEnumerator> IEnumerable>.GetEnumerator() => _serviceByName.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _serviceByName.GetEnumerator(); + + #endregion } diff --git a/src/JSSoft.Communication/ServiceContextAttribute.cs b/src/JSSoft.Communication/ServiceContextAttribute.cs index 3a81e83..07e88f9 100644 --- a/src/JSSoft.Communication/ServiceContextAttribute.cs +++ b/src/JSSoft.Communication/ServiceContextAttribute.cs @@ -1,14 +1,31 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; namespace JSSoft.Communication; [AttributeUsage(AttributeTargets.Class)] -internal sealed class ServiceContextAttribute : Attribute +sealed class ServiceContextAttribute : Attribute { public bool IsServer { get; set; } } diff --git a/src/JSSoft.Communication/ServiceContextBase.cs b/src/JSSoft.Communication/ServiceContextBase.cs index 90f36f4..373eb51 100644 --- a/src/JSSoft.Communication/ServiceContextBase.cs +++ b/src/JSSoft.Communication/ServiceContextBase.cs @@ -1,15 +1,32 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using JSSoft.Communication.Logging; using System; using System.Collections.Generic; using System.Net; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using JSSoft.Communication.Logging; namespace JSSoft.Communication; @@ -36,19 +53,6 @@ protected ServiceContextBase(IService[] services) _instanceContext = new InstanceContext(this); } - public event EventHandler? Opened; - - public event EventHandler? Closed; - - public event EventHandler? Faulted; - - ///

- /// Disconnected event is not used on the server side. - /// - public event EventHandler? Disconnected; - - public event EventHandler? ServiceStateChanged; - public virtual IAdaptorProvider AdaptorProvider => Communication.AdaptorProvider.Default; public virtual ISerializerProvider SerializerProvider => JsonSerializerProvider.Default; @@ -62,9 +66,9 @@ private set { if (_serviceState != value) { - var serviceState = _serviceState; + var _ = _serviceState; _serviceState = value; - Debug($"{nameof(ServiceState)}.{serviceState} => {nameof(ServiceState)}.{value}"); + Debug($"{nameof(ServiceState)}.{_} => {nameof(ServiceState)}.{value}"); OnServiceStateChanged(EventArgs.Empty); } } @@ -76,30 +80,19 @@ public EndPoint EndPoint set { if (ServiceState != ServiceState.None) - { - var message = $""" - Cannot set '{nameof(EndPoint)}'. service state is '{ServiceState.None}'. - """; - throw new InvalidOperationException(message); - } - + throw new InvalidOperationException($"Cannot set '{nameof(EndPoint)}'. service state is '{ServiceState.None}'."); _endPoint = value; } } public Guid Id { get; } = Guid.NewGuid(); - IReadOnlyDictionary IServiceContext.Services => Services; - public override string ToString() => $"{_t}[{Id}]"; public async Task OpenAsync(CancellationToken cancellationToken) { if (ServiceState != ServiceState.None) - { - var message = $"Service can only be open if the state is '{ServiceState.None}'."; - throw new InvalidOperationException(message); - } + throw new InvalidOperationException($"Service can only be open if the state is '{ServiceState.None}'."); try { @@ -131,15 +124,9 @@ public async Task OpenAsync(CancellationToken cancellationToken) public async Task CloseAsync(Guid token, CancellationToken cancellationToken) { if (ServiceState != ServiceState.Open) - { - throw new InvalidOperationException( - $"Service can only be closed if the state is '{ServiceState.Open}'."); - } - + throw new InvalidOperationException($"Service can only be closed if the state is '{ServiceState.Open}'."); if (token == Guid.Empty || _token!.Guid != token) - { throw new ArgumentException($"'{token}' is an invalid token.", nameof(token)); - } try { @@ -171,10 +158,7 @@ public async Task CloseAsync(Guid token, CancellationToken cancellationToken) public async Task AbortAsync() { if (ServiceState != ServiceState.Faulted) - { - throw new InvalidOperationException( - $"Service can only be aborted if the state is '{ServiceState.Faulted}'."); - } + throw new InvalidOperationException($"Service can only be aborted if the state is '{ServiceState.Faulted}'."); _token = null; _serializer = null; @@ -186,7 +170,6 @@ public async Task AbortAsync() Debug($"{nameof(IAdaptor)} ({AdaptorProvider.Name}) disposed."); _adaptor = null; } - ServiceState = ServiceState.None; OnClosed(EventArgs.Empty); } @@ -194,27 +177,57 @@ public async Task AbortAsync() public virtual object? GetService(Type serviceType) { if (serviceType == typeof(ISerializer)) - { return _serializer; - } - if (_instanceContext.GetService(serviceType) is { } instanceService) - { return instanceService; - } - return null; } + public event EventHandler? Opened; + + public event EventHandler? Closed; + + public event EventHandler? Faulted; + + /// + /// Disconnected event is not used on the server side. + /// + public event EventHandler? Disconnected; + + public event EventHandler? ServiceStateChanged; + + protected virtual InstanceBase CreateInstance(Type type) + { + if (_instanceBuilder == null) + throw new InvalidOperationException($"Cannot create instance of {type}"); + if (type == typeof(void)) + return InstanceBase.Empty; + var typeName = $"{type.Name}Impl"; + var instanceType = _instanceBuilder.CreateType(typeName, typeof(InstanceBase), type); + return (InstanceBase)Activator.CreateInstance(instanceType)!; + } + + protected virtual void OnOpened(EventArgs e) + => Opened?.Invoke(this, e); + + protected virtual void OnClosed(EventArgs e) + => Closed?.Invoke(this, e); + + protected virtual void OnFaulted(EventArgs e) + => Faulted?.Invoke(this, e); + + protected virtual void OnDisconnected(EventArgs e) + => Disconnected?.Invoke(this, e); + + protected virtual void OnServiceStateChanged(EventArgs e) + => ServiceStateChanged?.Invoke(this, e); + internal static bool IsServer(ServiceContextBase serviceContext) { - var serviceContextType = serviceContext.GetType(); - var attribute = serviceContextType.GetCustomAttribute(typeof(ServiceContextAttribute)); - if (attribute is ServiceContextAttribute serviceContextAttribute) + if (serviceContext.GetType().GetCustomAttribute(typeof(ServiceContextAttribute)) is ServiceContextAttribute attribute) { - return serviceContextAttribute.IsServer; + return attribute.IsServer; } - return false; } @@ -225,37 +238,31 @@ internal static Type GetInstanceType(ServiceContextBase serviceContext, IService { return service.ClientType; } - return service.ServerType; } internal static bool IsPerPeer(ServiceContextBase serviceContext, IService service) { if (IsServer(serviceContext) != true) - { return false; - } - var serviceType = service.ServerType; - var attribute = serviceType.GetCustomAttribute(typeof(ServiceContractAttribute)); - if (attribute is ServiceContractAttribute serviceContractAttribute) + if (serviceType.GetCustomAttribute(typeof(ServiceContractAttribute)) is ServiceContractAttribute attribute) { - return serviceContractAttribute.PerPeer; + return attribute.PerPeer; } - return false; } - internal (object ServiceInstance, object ClientInstance) CreateInstance( - IService service, IPeer peer) + internal (object, object) CreateInstance(IService service, IPeer peer) { var adaptor = _adaptor!; var baseType = GetInstanceType(this, service); var instance = CreateInstance(baseType); - - instance.Service = service; - instance.Adaptor = adaptor; - instance.Peer = peer; + { + instance.Service = service; + instance.Adaptor = adaptor; + instance.Peer = peer; + } var impl = service.CreateInstance(peer, instance); var serverInstance = _isServer ? impl : instance; @@ -263,8 +270,7 @@ internal static bool IsPerPeer(ServiceContextBase serviceContext, IService servi return (serverInstance, clientInstance); } - internal void DestroyInstance( - IService service, IPeer peer, object serverInstance, object clientInstance) + internal void DestroyInstance(IService service, IPeer peer, object serverInstance, object clientInstance) { if (_isServer == true) { @@ -276,38 +282,6 @@ internal void DestroyInstance( } } - protected virtual InstanceBase CreateInstance(Type type) - { - if (_instanceBuilder == null) - { - throw new InvalidOperationException($"Cannot create instance of {type}"); - } - - if (type == typeof(void)) - { - return InstanceBase.Empty; - } - - var typeName = $"{type.Name}Impl"; - var instanceType = _instanceBuilder.CreateType(typeName, typeof(InstanceBase), type); - return (InstanceBase)Activator.CreateInstance(instanceType)!; - } - - protected virtual void OnOpened(EventArgs e) - => Opened?.Invoke(this, e); - - protected virtual void OnClosed(EventArgs e) - => Closed?.Invoke(this, e); - - protected virtual void OnFaulted(EventArgs e) - => Faulted?.Invoke(this, e); - - protected virtual void OnDisconnected(EventArgs e) - => Disconnected?.Invoke(this, e); - - protected virtual void OnServiceStateChanged(EventArgs e) - => ServiceStateChanged?.Invoke(this, e); - private void Debug(string message) => LogUtility.Debug($"{this} {message}"); @@ -327,4 +301,10 @@ private void Adaptor_Disconnected(object? sender, EventArgs e) Debug($"{nameof(IServiceContext)} ({this.GetType()}) closed."); OnClosed(EventArgs.Empty); } + + #region IService + + IReadOnlyDictionary IServiceContext.Services => Services; + + #endregion } diff --git a/src/JSSoft.Communication/ServiceContractAttribute.cs b/src/JSSoft.Communication/ServiceContractAttribute.cs index 6a2c9be..e7df73b 100644 --- a/src/JSSoft.Communication/ServiceContractAttribute.cs +++ b/src/JSSoft.Communication/ServiceContractAttribute.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; diff --git a/src/JSSoft.Communication/ServiceInstanceBuilder.cs b/src/JSSoft.Communication/ServiceInstanceBuilder.cs index 571330e..f124f8b 100644 --- a/src/JSSoft.Communication/ServiceInstanceBuilder.cs +++ b/src/JSSoft.Communication/ServiceInstanceBuilder.cs @@ -1,10 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -// Reflection should not be used to increase accessibility of classes, methods, or fields -#pragma warning disable S3011 +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Generic; @@ -16,34 +30,18 @@ namespace JSSoft.Communication; -internal sealed class ServiceInstanceBuilder +sealed class ServiceInstanceBuilder { - private const string Namespace = "JSSoft.Communication.Runtime"; + private const string ns = "JSSoft.Communication.Runtime"; private readonly Dictionary _typeByName = []; + private readonly AssemblyBuilder _assemblyBuilder; private readonly ModuleBuilder _moduleBuilder; internal ServiceInstanceBuilder() { - var assemblyName = new AssemblyName(Namespace); - var access = AssemblyBuilderAccess.RunAndCollect; - var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, access); - _moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name!); - AssemblyName = assemblyName; - } - - public AssemblyName AssemblyName { get; } - - public Type CreateType(string name, Type baseType, Type interfaceType) - { - var fullName = $"{AssemblyName}.{name}"; - if (_typeByName.TryGetValue(fullName, out var value) != true) - { - var type = CreateType(_moduleBuilder, name, baseType, interfaceType); - _typeByName.Add(fullName, type); - value = type; - } - - return value; + AssemblyName = new AssemblyName(ns); + _assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(AssemblyName, AssemblyBuilderAccess.RunAndCollect); + _moduleBuilder = _assemblyBuilder.DefineDynamicModule(AssemblyName.Name!); } internal static ServiceInstanceBuilder? Create() @@ -58,11 +56,22 @@ public Type CreateType(string name, Type baseType, Type interfaceType) } } - private static Type CreateType( - ModuleBuilder moduleBuilder, string typeName, Type baseType, Type interfaceType) + public AssemblyName AssemblyName { get; } + + public Type CreateType(string name, Type baseType, Type interfaceType) + { + var fullName = $"{AssemblyName}.{name}"; + if (_typeByName.ContainsKey(fullName) != true) + { + var type = CreateType(_moduleBuilder, name, baseType, interfaceType); + _typeByName.Add(fullName, type); + } + return _typeByName[fullName]; + } + + private static Type CreateType(ModuleBuilder moduleBuilder, string typeName, Type baseType, Type interfaceType) { - var typeAttrs = TypeAttributes.Class | TypeAttributes.Public; - var typeBuilder = moduleBuilder.DefineType(typeName, typeAttrs, baseType, [interfaceType]); + var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public, baseType, [interfaceType]); var methodInfos = interfaceType.GetMethods(); foreach (var methodInfo in methodInfos) { @@ -79,13 +88,9 @@ private static Type CreateType( else if (returnType == typeof(void)) { if (isOneWay == true) - { CreateInvoke(typeBuilder, methodInfo, InstanceBase.InvokeOneWayMethod); - } else - { CreateInvoke(typeBuilder, methodInfo, InstanceBase.InvokeMethod); - } } else { @@ -96,26 +101,19 @@ private static Type CreateType( return typeBuilder.CreateType(); } - private static void CreateInvoke( - TypeBuilder typeBuilder, MethodInfo methodInfo, string methodName) + private static void CreateInvoke(TypeBuilder typeBuilder, MethodInfo methodInfo, string methodName) { var parameterInfos = methodInfo.GetParameters(); var parameterTypes = parameterInfos.Select(i => i.ParameterType).ToArray(); var returnType = methodInfo.ReturnType; - var methodAttributes = MethodAttributes.Public | MethodAttributes.Virtual - | MethodAttributes.Final | MethodAttributes.HideBySig; - var methodBuilder = typeBuilder.DefineMethod( - name: methodInfo.Name, - attributes: methodAttributes, - callingConvention: CallingConventions.Standard, - returnType: returnType, - parameterTypes: parameterTypes); + var methodAttributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig; + var methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, methodAttributes, CallingConventions.Standard, returnType, parameterTypes); var invokeMethod = FindInvokeMethod(typeBuilder.BaseType!, methodName, returnType); var typeofMethod = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle))!; for (var i = 0; i < parameterInfos.Length; i++) { - methodBuilder.DefineParameter(i, ParameterAttributes.Lcid, parameterInfos[i].Name); + var pb = methodBuilder.DefineParameter(i, ParameterAttributes.Lcid, parameterInfos[i].Name); } var il = methodBuilder.GetILGenerator(); @@ -125,19 +123,18 @@ private static void CreateInvoke( { il.DeclareLocal(returnType); } - il.Emit(OpCodes.Nop); il.EmitLdc_I4(parameterInfos.Length); il.Emit(OpCodes.Newarr, typeof(Type)); for (var i = 0; i < parameterInfos.Length; i++) { + var item = parameterInfos[i]; il.Emit(OpCodes.Dup); il.EmitLdc_I4(i); il.Emit(OpCodes.Ldtoken, parameterTypes[i]); il.Emit(OpCodes.Call, typeofMethod); il.Emit(OpCodes.Stelem_Ref); } - il.Emit(OpCodes.Stloc_0); il.EmitLdc_I4(parameterInfos.Length); il.Emit(OpCodes.Newarr, typeof(object)); @@ -151,10 +148,8 @@ private static void CreateInvoke( { il.Emit(OpCodes.Box, parameterTypes[i]); } - il.Emit(OpCodes.Stelem_Ref); } - il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, MethodUtility.GenerateName(methodInfo)); @@ -165,20 +160,14 @@ private static void CreateInvoke( il.Emit(OpCodes.Ret); } - private static void CreateInvokeAsync( - TypeBuilder typeBuilder, MethodInfo methodInfo, string methodName) + private static void CreateInvokeAsync(TypeBuilder typeBuilder, MethodInfo methodInfo, string methodName) { var isCancelable = MethodUtility.IsMethodCancelable(methodInfo); var parameterInfos = methodInfo.GetParameters(); var parameterTypes = parameterInfos.Select(i => i.ParameterType).ToArray(); var returnType = methodInfo.ReturnType; - var methodAttributes = MethodAttributes.Public | MethodAttributes.Virtual - | MethodAttributes.Final | MethodAttributes.HideBySig; - var methodBuilder = typeBuilder.DefineMethod( - name: methodInfo.Name, - attributes: methodAttributes, - returnType: returnType, - parameterTypes: parameterTypes); + var methodAttributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig; + var methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, methodAttributes, returnType, parameterTypes); var invokeMethod = FindInvokeMethod(typeBuilder.BaseType!, methodName, returnType); var typeofMethod = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle))!; @@ -187,9 +176,7 @@ private static void CreateInvokeAsync( methodBuilder.DefineParameter(i, ParameterAttributes.None, parameterInfos[i].Name); } - var parameterLength = isCancelable == true - ? parameterInfos.Length - 1 - : parameterInfos.Length; + var parameterLength = isCancelable == true ? parameterInfos.Length - 1 : parameterInfos.Length; var il = methodBuilder.GetILGenerator(); il.DeclareLocal(typeof(Type[])); il.DeclareLocal(typeof(object[])); @@ -199,13 +186,13 @@ private static void CreateInvokeAsync( il.Emit(OpCodes.Newarr, typeof(Type)); for (var i = 0; i < parameterLength; i++) { + var item = parameterInfos[i]; il.Emit(OpCodes.Dup); il.EmitLdc_I4(i); il.Emit(OpCodes.Ldtoken, parameterTypes[i]); il.Emit(OpCodes.Call, typeofMethod); il.Emit(OpCodes.Stelem_Ref); } - il.Emit(OpCodes.Stloc_0); il.EmitLdc_I4(parameterLength); il.Emit(OpCodes.Newarr, typeof(object)); @@ -219,10 +206,8 @@ private static void CreateInvokeAsync( { il.Emit(OpCodes.Box, parameterTypes[i]); } - il.Emit(OpCodes.Stelem_Ref); } - il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, MethodUtility.GenerateName(methodInfo)); @@ -234,11 +219,8 @@ private static void CreateInvokeAsync( } else { - var bindingFlags = BindingFlags.Public | BindingFlags.Static; - var meth = typeof(CancellationToken).GetMethod("get_None", bindingFlags)!; - il.Emit(OpCodes.Call, meth); + il.Emit(OpCodes.Call, typeof(CancellationToken).GetMethod("get_None", BindingFlags.Public | BindingFlags.Static)!); } - il.Emit(OpCodes.Call, invokeMethod); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ret); @@ -249,22 +231,28 @@ private static MethodInfo FindInvokeMethod(Type baseType, string methodName, Typ var methodInfos = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance); foreach (var item in methodInfos) { - var attribute = item.GetCustomAttribute(); - if (attribute is InstanceMethodAttribute instanceMethodAttribute - && instanceMethodAttribute.MethodName == methodName) + if (item.GetCustomAttribute() is InstanceMethodAttribute attr && attr.MethodName == methodName) { if (item.IsGenericMethod == true) { if (returnType.IsGenericType == true) - { return item.MakeGenericMethod(returnType.GetGenericArguments()); - } else - { return item.MakeGenericMethod(returnType); - } } + return item; + } + } + + throw new NotSupportedException($"'{methodName}' method is not found."); + } + private static MethodInfo FindInvokeMethod(MethodInfo[] methodInfos, string methodName) + { + foreach (var item in methodInfos) + { + if (item.GetCustomAttribute() is InstanceMethodAttribute attr && attr.MethodName == methodName) + { return item; } } diff --git a/src/JSSoft.Communication/ServiceState.cs b/src/JSSoft.Communication/ServiceState.cs index 37ae8ab..b5bd1bd 100644 --- a/src/JSSoft.Communication/ServiceState.cs +++ b/src/JSSoft.Communication/ServiceState.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication; diff --git a/src/JSSoft.Communication/ServiceToken.cs b/src/JSSoft.Communication/ServiceToken.cs index 48927d9..ca1b8ce 100644 --- a/src/JSSoft.Communication/ServiceToken.cs +++ b/src/JSSoft.Communication/ServiceToken.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; @@ -9,11 +26,12 @@ namespace JSSoft.Communication; public sealed class ServiceToken { - internal static readonly ServiceToken Empty = new(Guid.Empty); - internal ServiceToken(Guid guid) => Guid = guid; internal Guid Guid { get; } - internal static ServiceToken NewToken() => new(Guid.NewGuid()); + internal static ServiceToken NewToken() + => new(Guid.NewGuid()); + + internal static readonly ServiceToken Empty = new(Guid.Empty); } diff --git a/src/JSSoft.Communication/ServiceUtility.cs b/src/JSSoft.Communication/ServiceUtility.cs index cf4c6ec..da02394 100644 --- a/src/JSSoft.Communication/ServiceUtility.cs +++ b/src/JSSoft.Communication/ServiceUtility.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Reflection; @@ -10,43 +27,26 @@ namespace JSSoft.Communication; public static class ServiceUtility { - public static Type ValidateServerType(Type serviceType) + public static Type ValidateServerType(Type ServiceType) { - if (serviceType.IsInterface != true) - { + if (ServiceType.IsInterface != true) throw new InvalidOperationException("Server type must be interface."); - } - - if (IsNestedPublicType(serviceType) != true - && IsPublicType(serviceType) != true - && IsInternalType(serviceType) != true) - { - throw new InvalidOperationException( - $"'{serviceType.Name}' must be public or internal."); - } - return serviceType; + if (IsNestedPublicType(ServiceType) != true && IsPublicType(ServiceType) != true && IsInternalType(ServiceType) != true) + throw new InvalidOperationException($"'{ServiceType.Name}' must be public or internal."); + return ServiceType; } - public static Type ValidateClientType(Type callbackType) + public static Type ValidateClientType(Type CallbackType) { - if (callbackType != typeof(void)) + if (CallbackType != typeof(void)) { - if (callbackType.IsInterface != true) - { + if (CallbackType.IsInterface != true) throw new InvalidOperationException("Client type must be interface."); - } - - if (IsNestedPublicType(callbackType) != true - && IsPublicType(callbackType) != true - && IsInternalType(callbackType) != true) - { - throw new InvalidOperationException( - $"'{callbackType.Name}' must be public or internal."); - } + if (IsNestedPublicType(CallbackType) != true && IsPublicType(CallbackType) != true && IsInternalType(CallbackType) != true) + throw new InvalidOperationException($"'{CallbackType.Name}' must be public or internal."); } - - return callbackType; + return CallbackType; } public static bool IsNestedPublicType(Type type) @@ -60,12 +60,10 @@ public static bool IsInternalType(Type type) public static bool IsServer(IService service) { - var attribute = service.GetType().GetCustomAttribute(typeof(ServiceAttribute)); - if (attribute is ServiceAttribute serviceAttribute) + if (service.GetType().GetCustomAttribute(typeof(ServiceAttribute)) is ServiceAttribute serviceAttribute) { return serviceAttribute.IsServer; } - return false; } } diff --git a/src/JSSoft.Communication/TaskUtility.cs b/src/JSSoft.Communication/TaskUtility.cs index c024ec1..50f7561 100644 --- a/src/JSSoft.Communication/TaskUtility.cs +++ b/src/JSSoft.Communication/TaskUtility.cs @@ -1,17 +1,33 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System.Threading; using System.Threading.Tasks; namespace JSSoft.Communication; -internal static class TaskUtility +static class TaskUtility { - public static async Task TryDelay( - int millisecondsDelay, CancellationToken cancellationToken) + public static async Task TryDelay(int millisecondsDelay, CancellationToken cancellationToken) { try { diff --git a/src/JSSoft.Communication/Threading/Dispatcher.cs b/src/JSSoft.Communication/Threading/Dispatcher.cs index ec1cb0b..379d4cd 100644 --- a/src/JSSoft.Communication/Threading/Dispatcher.cs +++ b/src/JSSoft.Communication/Threading/Dispatcher.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Threading; @@ -16,22 +33,22 @@ public class Dispatcher : IDisposable private readonly CancellationToken _cancellationToken; private readonly DispatcherSynchronizationContext _context; private readonly DispatcherScheduler _scheduler; + private bool _isDisposed; + #if DEBUG - private readonly System.Diagnostics.StackTrace _stackTrace; + private readonly System.Diagnostics.StackTrace? _stackTrace; #endif - public Dispatcher(object owner) { _cancellationTokenSource = new CancellationTokenSource(); _cancellationToken = _cancellationTokenSource.Token; - _scheduler = new DispatcherScheduler(_cancellationToken); - _factory = new TaskFactory( - _cancellationToken, TaskCreationOptions.None, TaskContinuationOptions.None, _scheduler); + _scheduler = new DispatcherScheduler(this, _cancellationToken); + _factory = new TaskFactory(_cancellationToken, TaskCreationOptions.None, TaskContinuationOptions.None, _scheduler); _context = new DispatcherSynchronizationContext(_factory); Owner = owner; #if DEBUG _stackTrace = new System.Diagnostics.StackTrace(true); -#endif +#endif Thread = new Thread(_scheduler.Run) { Name = $"{owner}: {owner.GetHashCode()}", @@ -48,10 +65,6 @@ public Dispatcher(object owner) public SynchronizationContext SynchronizationContext => _context; -#if DEBUG - internal string StackTrace => $"{_stackTrace}"; -#endif - public override string ToString() => $"{Owner}"; public void VerifyAccess() @@ -66,35 +79,22 @@ public void VerifyAccess() public void Invoke(Action action) { - ObjectDisposedException.ThrowIf(_cancellationToken.IsCancellationRequested, this); + if (_cancellationToken.IsCancellationRequested == true) + throw new ObjectDisposedException($"{this}"); if (CheckAccess() == true) { action(); return; } - var task = _factory.StartNew(action, _cancellationToken); task.Wait(_cancellationToken); } - public TResult Invoke(Func func) - { - ObjectDisposedException.ThrowIf(_cancellationToken.IsCancellationRequested, this); - - if (CheckAccess() == true) - { - return func(); - } - - var task = _factory.StartNew(func, _cancellationToken); - task.Wait(_cancellationToken); - return task.Result; - } - public void Post(Action action) { - ObjectDisposedException.ThrowIf(_cancellationToken.IsCancellationRequested, this); + if (_cancellationToken.IsCancellationRequested == true) + throw new ObjectDisposedException($"{this}"); _factory.StartNew(action, _cancellationToken); } @@ -107,25 +107,69 @@ public async Task InvokeAsync(Task task) public Task InvokeAsync(Action action) { - ObjectDisposedException.ThrowIf(_cancellationToken.IsCancellationRequested, this); + if (_cancellationToken.IsCancellationRequested == true) + throw new ObjectDisposedException($"{this}"); return _factory.StartNew(action, _cancellationToken); } + public TResult Invoke(Func func) + { + if (_cancellationTokenSource.IsCancellationRequested == true) + throw new ObjectDisposedException($"{this}"); + + if (CheckAccess() == true) + { + return func(); + } + var task = _factory.StartNew(func, _cancellationToken); + task.Wait(_cancellationToken); + return task.Result; + } + public async Task InvokeAsync(Func callback) { - ObjectDisposedException.ThrowIf(_cancellationToken.IsCancellationRequested, this); + if (_cancellationTokenSource.IsCancellationRequested == true) + throw new ObjectDisposedException($"{this}"); return await _factory.StartNew(callback, _cancellationToken); } public void Dispose() { - ObjectDisposedException.ThrowIf(_cancellationTokenSource.IsCancellationRequested, this); + if (_isDisposed == true) + throw new ObjectDisposedException($"{this}"); + if (_cancellationTokenSource.IsCancellationRequested == true) + throw new ObjectDisposedException($"{this}"); _cancellationTokenSource.Cancel(); _scheduler.WaitClose(); _cancellationTokenSource.Dispose(); + _isDisposed = true; GC.SuppressFinalize(this); } + +#if DEBUG + internal string StackTrace => $"{_stackTrace}"; +#endif +} + +public sealed class DispatcherSynchronizationContext : SynchronizationContext +{ + private readonly TaskFactory _factory; + + internal DispatcherSynchronizationContext(TaskFactory factory) + { + _factory = factory; + } + + public override void Send(SendOrPostCallback d, object? state) + { + _factory.StartNew(() => d(state)).Wait(); + } + + public override void Post(SendOrPostCallback d, object? state) + { + _factory.StartNew(() => d(state)); + } } diff --git a/src/JSSoft.Communication/Threading/DispatcherScheduler.cs b/src/JSSoft.Communication/Threading/DispatcherScheduler.cs index b183224..d74c7d2 100644 --- a/src/JSSoft.Communication/Threading/DispatcherScheduler.cs +++ b/src/JSSoft.Communication/Threading/DispatcherScheduler.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Collections.Concurrent; @@ -13,23 +30,39 @@ namespace JSSoft.Communication.Threading; public sealed class DispatcherScheduler : TaskScheduler { + private readonly Dispatcher _dispatcher; private readonly CancellationToken _cancellationToken; private readonly ConcurrentQueue _taskQueue = []; private readonly ManualResetEvent _executionEventSet = new(false); private bool _isRunning = true; private bool _isClosed; - internal DispatcherScheduler(CancellationToken cancellationToken) + internal DispatcherScheduler(Dispatcher dispatcher, CancellationToken cancellationToken) { + _dispatcher = dispatcher; _cancellationToken = cancellationToken; } + protected override IEnumerable GetScheduledTasks() => _taskQueue; + + protected override void QueueTask(Task task) + { + if (_cancellationToken.IsCancellationRequested != true) + { + _taskQueue.Enqueue(task); + _executionEventSet.Set(); + } + } + + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return false; + } + internal void WaitClose() { if (_isClosed == true) - { throw new InvalidOperationException("Dispatcher is already closed."); - } while (_isRunning == true) { @@ -41,10 +74,8 @@ internal void WaitClose() { _executionEventSet.Close(); } - Thread.Sleep(1); } - _executionEventSet.Dispose(); _isClosed = true; } @@ -53,6 +84,11 @@ internal void Run() { try { +#if DEBUG + var owner = _dispatcher.Owner; + var stackTrace = _dispatcher.StackTrace; +#endif + while (_cancellationToken.IsCancellationRequested != true) { if (_taskQueue.TryDequeue(out var task) == true) @@ -72,31 +108,12 @@ internal void Run() } } } - if (_isClosed == true) - { throw new InvalidOperationException("Dispatcher is already closed."); - } } finally { _isRunning = false; } } - - protected override IEnumerable GetScheduledTasks() => _taskQueue; - - protected override void QueueTask(Task task) - { - if (_cancellationToken.IsCancellationRequested != true) - { - _taskQueue.Enqueue(task); - _executionEventSet.Set(); - } - } - - protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) - { - return false; - } } diff --git a/src/JSSoft.Communication/Threading/DispatcherSynchronizationContext.cs b/src/JSSoft.Communication/Threading/DispatcherSynchronizationContext.cs deleted file mode 100644 index 92493c2..0000000 --- a/src/JSSoft.Communication/Threading/DispatcherSynchronizationContext.cs +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace JSSoft.Communication.Threading; - -public sealed class DispatcherSynchronizationContext : SynchronizationContext -{ - private readonly TaskFactory _factory; - - internal DispatcherSynchronizationContext(TaskFactory factory) - { - _factory = factory; - } - - public override void Send(SendOrPostCallback d, object? state) - { - _factory.StartNew(() => d(state)).Wait(); - } - - public override void Post(SendOrPostCallback d, object? state) - { - _factory.StartNew(() => d(state)); - } -} diff --git a/src/JSSoft.Communication/UnreachableException.cs b/src/JSSoft.Communication/UnreachableException.cs index 5cb2e30..0218444 100644 --- a/src/JSSoft.Communication/UnreachableException.cs +++ b/src/JSSoft.Communication/UnreachableException.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. #if !NET7_0_OR_GREATER using System; diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Application.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Application.cs index 287764c..3e8f967 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Application.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Application.cs @@ -1,35 +1,47 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using JSSoft.Communication.Services; using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.ComponentModel.Composition.Hosting; using System.IO; -using System.Linq; -using System.Net; using System.Threading; using System.Threading.Tasks; -using JSSoft.Communication.Services; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Hosting; +using System.Collections; +using System.Linq; +using System.Collections.Generic; using JSSoft.Terminals; +using System.Net; namespace JSSoft.Communication.ConsoleApp; -internal sealed class Application : IApplication, IServiceProvider +sealed class Application : IApplication, IServiceProvider { - private static readonly string Postfix = TerminalEnvironment.IsWindows() == true ? ">" : "$"; + private static readonly string postfix = TerminalEnvironment.IsWindows() == true ? ">" : "$"; private readonly ApplicationOptions _option; private readonly IServiceContext _serviceContext; + private readonly INotifyUserService _userServiceNotification; private readonly CompositionContainer _container; -#if SERVER - private readonly bool _isServer = true; -#else - private readonly bool _isServer = false; -#endif - private bool _isDisposed; private CancellationTokenSource? _cancellationTokenSource; private string title = string.Empty; @@ -39,6 +51,12 @@ static Application() Logging.LogUtility.Logger = Logging.TraceLogger.Default; } +#if SERVER + private readonly bool _isServer = true; +#else + private readonly bool _isServer = false; +#endif + public Application(ApplicationOptions option) { _container = new CompositionContainer(new AssemblyCatalog(typeof(Application).Assembly)); @@ -50,14 +68,11 @@ public Application(ApplicationOptions option) _serviceContext = _container.GetExportedValue(); _serviceContext.Opened += ServiceContext_Opened; _serviceContext.Closed += ServiceContext_Closed; + _userServiceNotification = _container.GetExportedValue(); + _userServiceNotification.LoggedIn += UserServiceNotification_LoggedIn; + _userServiceNotification.LoggedOut += UserServiceNotification_LoggedOut; + _userServiceNotification.MessageReceived += UserServiceNotification_MessageReceived; Title = "Server"; - if (_container.GetExportedValue() is { } userServiceNotification) - { - userServiceNotification = _container.GetExportedValue(); - userServiceNotification.LoggedIn += UserServiceNotification_LoggedIn; - userServiceNotification.LoggedOut += UserServiceNotification_LoggedOut; - userServiceNotification.MessageReceived += UserServiceNotification_MessageReceived; - } } public bool IsOpened { get; private set; } @@ -72,16 +87,6 @@ public string Title } } - internal Guid Token { get; set; } - - internal Guid UserToken { get; private set; } - - internal string UserId { get; private set; } = string.Empty; - - private TextWriter Out => Console.Out; - - private SystemTerminal Terminal => _container.GetExportedValue(); - public void Dispose() { if (_isDisposed != true) @@ -91,87 +96,9 @@ public void Dispose() } } - public async Task StartAsync() + internal void Login(string userID, Guid token) { - if (_cancellationTokenSource != null) - { - throw new InvalidOperationException("Application is already started."); - } - - _cancellationTokenSource = new CancellationTokenSource(); - _serviceContext.EndPoint = new DnsEndPoint(_option.Host, _option.Port); - try - { - Token = await _serviceContext.OpenAsync(_cancellationTokenSource.Token); - } - catch - { - await _serviceContext.AbortAsync(); - } - - UpdatePrompt(); - await Terminal.StartAsync(_cancellationTokenSource.Token); - } - - public async Task StopAsync(int exitCode) - { - if (_cancellationTokenSource == null) - { - throw new InvalidOperationException("Application is not started."); - } - - await _cancellationTokenSource.CancelAsync(); - if (_serviceContext.ServiceState == ServiceState.Open) - { - _serviceContext.Closed -= ServiceContext_Closed; - try - { - await _serviceContext.CloseAsync(Token, cancellationToken: default); - } - catch - { - await _serviceContext.AbortAsync(); - } - } - - _cancellationTokenSource.Dispose(); - _cancellationTokenSource = null; - } - - object? IServiceProvider.GetService(Type serviceType) - { - if (serviceType == typeof(IServiceProvider)) - { - return this; - } - - if (typeof(IEnumerable).IsAssignableFrom(serviceType) - && serviceType.GenericTypeArguments.Length == 1) - { - var itemType = serviceType.GenericTypeArguments.First(); - var contractName = AttributedModelServices.GetContractName(itemType); - var items = _container.GetExportedValues(contractName); - var listGenericType = typeof(List<>); - var list = listGenericType.MakeGenericType(itemType); - var ci = list.GetConstructor([typeof(int)])!; - var instance = (IList)ci.Invoke([items.Count(),])!; - foreach (var item in items) - { - instance.Add(item); - } - - return instance; - } - else - { - var contractName = AttributedModelServices.GetContractName(serviceType); - return _container.GetExportedValue(contractName); - } - } - - internal void Login(string userId, Guid token) - { - UserId = userId; + UserID = userID; UserToken = token; UpdatePrompt(); Out.WriteLine("사용자 관련 명령을 수행하려면 'help user' 을(를) 입력하세요."); @@ -179,27 +106,34 @@ internal void Login(string userId, Guid token) internal void Logout() { - UserId = string.Empty; + UserID = string.Empty; UserToken = Guid.Empty; UpdatePrompt(); } + internal Guid Token { get; set; } + + internal Guid UserToken { get; private set; } + + internal string UserID { get; private set; } = string.Empty; + + private TextWriter Out => Console.Out; + + private SystemTerminal Terminal => _container.GetExportedValue(); + private void UpdatePrompt() { var prompt = string.Empty; var isOpened = IsOpened; - var userId = UserId; + var userID = UserID; if (isOpened == true) { prompt = EndPointUtility.ToString(_serviceContext.EndPoint); - if (userId != string.Empty) - { - prompt += $"@{userId}"; - } + if (userID != string.Empty) + prompt += $"@{userID}"; } - - Terminal.Prompt = $"{prompt} {Postfix} "; + Terminal.Prompt = $"{prompt} {postfix} "; } private void ServiceContext_Opened(object? sender, EventArgs e) @@ -217,7 +151,6 @@ private void ServiceContext_Opened(object? sender, EventArgs e) Title = $"Client {EndPointUtility.ToString(_serviceContext.EndPoint)}"; Out.WriteLine("서버에 연결되었습니다."); } - Out.WriteLine("사용 가능한 명령을 확인려면 '--help' 을(를) 입력하세요."); Out.WriteLine("로그인을 하려면 'login admin admin' 을(를) 입력하세요."); } @@ -242,27 +175,100 @@ private void ServiceContext_Closed(object? sender, EventArgs e) private void UserServiceNotification_LoggedIn(object? sender, UserEventArgs e) { - Out.WriteLine($"User logged in: {e.UserId}"); + Out.WriteLine($"User logged in: {e.UserID}"); } private void UserServiceNotification_LoggedOut(object? sender, UserEventArgs e) { - Out.WriteLine($"User logged out: {e.UserId}"); + Out.WriteLine($"User logged out: {e.UserID}"); } private void UserServiceNotification_MessageReceived(object? sender, UserMessageEventArgs e) { - if (e.Sender == UserId) + if (e.Sender == UserID) { - var message = $"to '{e.Receiver}'에게 귓속말: {e.Message}"; - var text = TerminalStringBuilder.GetString(message, TerminalColorType.BrightMagenta); + var text = TerminalStringBuilder.GetString($"'{e.Receiver}'에게 귓속말: {e.Message}", TerminalColorType.BrightMagenta); Out.WriteLine(text); } - else if (e.Receiver == UserId) + else if (e.Receiver == UserID) { - var message = $"from '{e.Receiver}': {e.Message}"; - var text = TerminalStringBuilder.GetString(message, TerminalColorType.BrightMagenta); + var text = TerminalStringBuilder.GetString($"'{e.Receiver}'의 귓속말: {e.Message}", TerminalColorType.BrightMagenta); Out.WriteLine(text); } } -} + + #region IServiceProvider + + object? IServiceProvider.GetService(Type serviceType) + { + if (serviceType == typeof(IServiceProvider)) + return this; + + if (typeof(IEnumerable).IsAssignableFrom(serviceType) && serviceType.GenericTypeArguments.Length == 1) + { + var itemType = serviceType.GenericTypeArguments.First(); + var contractName = AttributedModelServices.GetContractName(itemType); + var items = _container.GetExportedValues(contractName); + var listGenericType = typeof(List<>); + var list = listGenericType.MakeGenericType(itemType); + var ci = list.GetConstructor(new Type[] { typeof(int) })!; + var instance = (IList)ci.Invoke(new object[] { items.Count(), })!; + foreach (var item in items) + { + instance.Add(item); + } + return instance; + } + else + { + var contractName = AttributedModelServices.GetContractName(serviceType); + return _container.GetExportedValue(contractName); + } + } + + #endregion + + #region IApplication + + public async Task StartAsync() + { + if (_cancellationTokenSource != null) + throw new InvalidOperationException("Application is already started."); + + _cancellationTokenSource = new CancellationTokenSource(); + _serviceContext.EndPoint = new DnsEndPoint(_option.Host, _option.Port); + try + { + Token = await _serviceContext.OpenAsync(_cancellationTokenSource.Token); + } + catch (Exception e) + { + var text = TerminalStringBuilder.GetString(e.Message, TerminalColorType.BrightRed); + await _serviceContext.AbortAsync(); + } + UpdatePrompt(); + await Terminal.StartAsync(_cancellationTokenSource.Token); + } + + public async Task StopAsync(int exitCode) + { + if (_cancellationTokenSource == null) + throw new InvalidOperationException("Application is not started."); + + _cancellationTokenSource.Cancel(); + if (_serviceContext.ServiceState == ServiceState.Open) + { + _serviceContext.Closed -= ServiceContext_Closed; + try + { + await _serviceContext.CloseAsync(Token, cancellationToken: default); + } + catch + { + await _serviceContext.AbortAsync(); + } + } + } + + #endregion +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/ApplicationOptions.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/ApplicationOptions.cs index c768575..6fee939 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/ApplicationOptions.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/ApplicationOptions.cs @@ -1,10 +1,27 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. -using System.ComponentModel.Composition; using JSSoft.Commands; +using System.ComponentModel.Composition; namespace JSSoft.Communication.ConsoleApp; @@ -31,4 +48,4 @@ public static ApplicationOptions Parse(string[] args) parser.Parse(args); return options; } -} +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/CommandContext.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/CommandContext.cs index 777793e..7c42765 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/CommandContext.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/CommandContext.cs @@ -1,23 +1,36 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. -using System.Collections.Generic; -using System.ComponentModel.Composition; using JSSoft.Commands; using JSSoft.Communication.Commands; using JSSoft.Terminals; +using System.Collections.Generic; +using System.ComponentModel.Composition; namespace JSSoft.Communication.ConsoleApp; [Export(typeof(CommandContext))] [method: ImportingConstructor] -internal sealed class CommandContext( - [ImportMany] IEnumerable commands, - HelpCommand helpCommand, - VersionCommand versionCommand) - : CommandContextBase(commands) +sealed class CommandContext([ImportMany] IEnumerable commands, HelpCommand helpCommand, VersionCommand versionCommand) : CommandContextBase(commands) { protected override ICommand HelpCommand { get; } = helpCommand; @@ -35,4 +48,4 @@ protected override void OnEmptyExecute() tsb.AppendEnd(); Out.Write(tsb.ToString()); } -} +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/CloseCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/CloseCommand.cs index 8a54fa9..da830f8 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/CloseCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/CloseCommand.cs @@ -1,29 +1,44 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using JSSoft.Communication.ConsoleApp; +using JSSoft.Commands; using System; -using System.ComponentModel.Composition; -using System.Threading; using System.Threading.Tasks; -using JSSoft.Commands; -using JSSoft.Communication.ConsoleApp; +using System.Threading; +using System.ComponentModel.Composition; namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [method: ImportingConstructor] -internal sealed class CloseCommand(IServiceContext serviceContext, Application application) - : CommandAsyncBase +sealed class CloseCommand(IServiceContext serviceContext, Application application) : CommandAsyncBase { private readonly IServiceContext _serviceContext = serviceContext; private readonly Application _application = application; public override bool IsEnabled => _serviceContext.ServiceState == ServiceState.Open; - protected override async Task OnExecuteAsync( - CancellationToken cancellationToken, IProgress progress) + protected override async Task OnExecuteAsync(CancellationToken cancellationToken, IProgress progress) { try { diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/DataCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/DataCommand.cs index ffc8f02..33e4062 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/DataCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/DataCommand.cs @@ -1,35 +1,53 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using JSSoft.Communication.ConsoleApp; +using JSSoft.Communication.Services; +using JSSoft.Commands; using System; +using System.Threading.Tasks; using System.ComponentModel.Composition; using System.Threading; -using System.Threading.Tasks; -using JSSoft.Commands; -using JSSoft.Communication.ConsoleApp; -using JSSoft.Communication.Services; namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [method: ImportingConstructor] -internal class DataCommand(Application application, IDataService dataService) : CommandMethodBase +class DataCommand(Application application, IDataService dataService) : CommandMethodBase { private readonly Application _application = application; private readonly IDataService _dataService = dataService; - public override bool IsEnabled => _application.UserToken != Guid.Empty; - [CommandMethod] public Task CreateAsync(string dataBaseName, CancellationToken cancellationToken) { return _dataService.CreateDataBaseAsync(dataBaseName, cancellationToken); } - protected override bool IsMethodEnabled(CommandMethodDescriptor memberDescriptor) + public override bool IsEnabled => _application.UserToken != Guid.Empty; + + protected override bool IsMethodEnabled(CommandMethodDescriptor descriptor) { return _application.UserToken != Guid.Empty; } + } diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/ExitCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/ExitCommand.cs index 06d14ad..d785006 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/ExitCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/ExitCommand.cs @@ -1,28 +1,44 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using JSSoft.Communication.ConsoleApp; +using JSSoft.Commands; using System; -using System.ComponentModel.Composition; -using System.Threading; using System.Threading.Tasks; -using JSSoft.Commands; -using JSSoft.Communication.ConsoleApp; +using System.Threading; +using System.ComponentModel.Composition; namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [method: ImportingConstructor] -internal sealed class ExitCommand(IApplication application) : CommandAsyncBase +sealed class ExitCommand(IApplication application) : CommandAsyncBase { private readonly IApplication _application = application; [CommandPropertyRequired(DefaultValue = 0)] public int ExitCode { get; set; } - protected override Task OnExecuteAsync( - CancellationToken cancellationToken, IProgress progress) + protected override Task OnExecuteAsync(CancellationToken cancellationToken, IProgress progress) { return _application.StopAsync(ExitCode); } diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/HelpCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/HelpCommand.cs index 62559b9..7831972 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/HelpCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/HelpCommand.cs @@ -1,8 +1,3 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - using System.ComponentModel.Composition; using JSSoft.Commands; @@ -10,6 +5,6 @@ namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [Export(typeof(HelpCommand))] -internal sealed class HelpCommand : HelpCommandBase +sealed class HelpCommand : HelpCommandBase { } diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/LoginCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/LoginCommand.cs index 174eef3..753979d 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/LoginCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/LoginCommand.cs @@ -1,43 +1,53 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. -using System; -using System.ComponentModel.Composition; -using System.Threading; +using JSSoft.Communication.ConsoleApp; using System.Threading.Tasks; +using System; using JSSoft.Commands; -using JSSoft.Communication.ConsoleApp; using JSSoft.Communication.Services; +using System.Threading; +using System.ComponentModel.Composition; namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [method: ImportingConstructor] -internal sealed class LoginCommand(Application application, IUserService userService) - : CommandAsyncBase +sealed class LoginCommand(Application application, IUserService userService) : CommandAsyncBase { private readonly Application _application = application; private readonly IUserService _userService = userService; [CommandPropertyRequired] - public string UserId { get; set; } = string.Empty; + public string UserID { get; set; } = string.Empty; [CommandPropertyRequired] public string Password { get; set; } = string.Empty; public override bool IsEnabled => _application.UserToken == Guid.Empty; - protected override async Task OnExecuteAsync( - CancellationToken cancellationToken, IProgress progress) + protected override async Task OnExecuteAsync(CancellationToken cancellationToken, IProgress progress) { - var options = new UserLoginOptions() - { - UserId = UserId, - Password = Password, - }; - var token = await _userService.LoginAsync(options, cancellationToken); - _application.Login(UserId, token); + var token = await _userService.LoginAsync(UserID, Password, cancellationToken); + _application.Login(UserID, token); } } diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/LogoutCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/LogoutCommand.cs index 0c633c8..010253c 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/LogoutCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/LogoutCommand.cs @@ -1,30 +1,45 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using JSSoft.Communication.ConsoleApp; +using JSSoft.Communication.Services; +using JSSoft.Commands; using System; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; -using JSSoft.Commands; -using JSSoft.Communication.ConsoleApp; -using JSSoft.Communication.Services; namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [method: ImportingConstructor] -internal sealed class LogoutCommand(Application application, IUserService userService) - : CommandAsyncBase +sealed class LogoutCommand(Application application, IUserService userService) : CommandAsyncBase { private readonly Application _application = application; private readonly IUserService _userService = userService; public override bool IsEnabled => _application.UserToken != Guid.Empty; - protected override async Task OnExecuteAsync( - CancellationToken cancellationToken, IProgress progress) + protected override async Task OnExecuteAsync(CancellationToken cancellationToken, IProgress progress) { await _userService.LogoutAsync(_application.UserToken, cancellationToken); _application.Logout(); diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/OpenCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/OpenCommand.cs index 4d7602b..20dc931 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/OpenCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/OpenCommand.cs @@ -1,29 +1,44 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using JSSoft.Communication.ConsoleApp; +using JSSoft.Commands; using System; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; -using JSSoft.Commands; -using JSSoft.Communication.ConsoleApp; namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [method: ImportingConstructor] -internal sealed class OpenCommand(IServiceContext serviceContext, Application application) - : CommandAsyncBase +sealed class OpenCommand(IServiceContext serviceContext, Application application) : CommandAsyncBase { private readonly IServiceContext _serviceContext = serviceContext; private readonly Application _application = application; public override bool IsEnabled => _serviceContext.ServiceState == ServiceState.None; - protected override async Task OnExecuteAsync( - CancellationToken cancellationToken, IProgress progress) + protected override async Task OnExecuteAsync(CancellationToken cancellationToken, IProgress progress) { try { diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/UserCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/UserCommand.cs index 7f156a8..f3723e1 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/UserCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/UserCommand.cs @@ -1,112 +1,93 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +using JSSoft.Communication.ConsoleApp; +using JSSoft.Communication.Services; +using JSSoft.Commands; using System; +using System.Threading.Tasks; using System.ComponentModel.Composition; -using System.Text; using System.Threading; -using System.Threading.Tasks; -using JSSoft.Commands; -using JSSoft.Communication.ConsoleApp; -using JSSoft.Communication.Services; namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [method: ImportingConstructor] -internal class UserCommand(Application application, IUserService userService) : CommandMethodBase +class UserCommand(Application application, IUserService userService) : CommandMethodBase { private readonly Application _application = application; private readonly IUserService _userService = userService; - public override bool IsEnabled => _application.UserToken != Guid.Empty; - [CommandMethod] - public Task CreateAsync( - string userId, string password, Authority authority, CancellationToken cancellationToken) + public Task CreateAsync(string userID, string password, Authority authority, CancellationToken cancellationToken) { - var options = new UserCreateOptions - { - Token = _application.UserToken, - UserId = userId, - Password = password, - Authority = authority, - }; - return _userService.CreateAsync(options, cancellationToken); + return _userService.CreateAsync(_application.UserToken, userID, password, authority, cancellationToken); } [CommandMethod] - public Task DeleteAsync(string userId, CancellationToken cancellationToken) + public Task DeleteAsync(string userID, CancellationToken cancellationToken) { - var options = new UserDeleteOptions - { - Token = _application.UserToken, - UserId = userId, - }; - return _userService.DeleteAsync(options, cancellationToken); + return _userService.DeleteAsync(_application.UserToken, userID, cancellationToken); } [CommandMethod] public Task RenameAsync(string userName, CancellationToken cancellationToken) { - var options = new UserRenameOptions - { - Token = _application.UserToken, - UserName = userName, - }; - return _userService.RenameAsync(options, cancellationToken); + return _userService.RenameAsync(_application.UserToken, userName, cancellationToken); } [CommandMethod] - public Task AuthorityAsync( - string userId, Authority authority, CancellationToken cancellationToken) + public Task AuthorityAsync(string userID, Authority authority, CancellationToken cancellationToken) { - var options = new UserAuthorityOptions - { - Token = _application.UserToken, - UserId = userId, - Authority = authority, - }; - return _userService.SetAuthorityAsync(options, cancellationToken); + return _userService.SetAuthorityAsync(_application.UserToken, userID, authority, cancellationToken); } [CommandMethod] - public async Task InfoAsync(string userId, CancellationToken cancellationToken) + public async Task InfoAsync(string userID, CancellationToken cancellationToken) { - var token = _application.UserToken; - var userInfo = await _userService.GetInfoAsync(token, userId, cancellationToken); - await Out.WriteLineAsync($"UseName: {userInfo.UserName}"); - await Out.WriteLineAsync($"Authority: {userInfo.Authority}"); + var (userName, authority) = await _userService.GetInfoAsync(_application.UserToken, userID, cancellationToken); + Out.WriteLine($"UseName: {userName}"); + Out.WriteLine($"Authority: {authority}"); } [CommandMethod] public async Task ListAsync(CancellationToken cancellationToken) { var items = await _userService.GetUsersAsync(_application.UserToken, cancellationToken); - var sb = new StringBuilder(); foreach (var item in items) { - sb.AppendLine(item); + Out.WriteLine(item); } - - await Out.WriteLineAsync(sb.ToString()); } [CommandMethod] - public Task SendMessageAsync(string userId, string message, CancellationToken cancellationToken) + public Task SendMessageAsync(string userID, string message, CancellationToken cancellationToken) { - var options = new UserSendMessageOptions - { - Token = _application.UserToken, - UserId = userId, - Message = message, - }; - return _userService.SendMessageAsync(options, cancellationToken); + return _userService.SendMessageAsync(_application.UserToken, userID, message, cancellationToken); } - protected override bool IsMethodEnabled(CommandMethodDescriptor memberDescriptor) + public override bool IsEnabled => _application.UserToken != Guid.Empty; + + protected override bool IsMethodEnabled(CommandMethodDescriptor descriptor) { return _application.UserToken != Guid.Empty; } diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/VersionCommand.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/VersionCommand.cs index 8a0f474..adb5e4e 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/VersionCommand.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/Commands/VersionCommand.cs @@ -1,8 +1,3 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - using System.ComponentModel.Composition; using JSSoft.Commands; @@ -10,6 +5,6 @@ namespace JSSoft.Communication.Commands; [Export(typeof(ICommand))] [Export(typeof(VersionCommand))] -internal sealed class VersionCommand : VersionCommandBase +sealed class VersionCommand : VersionCommandBase { } diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/IApplication.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/IApplication.cs index 337a1a9..f806d3c 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/IApplication.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/IApplication.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Threading.Tasks; @@ -10,9 +27,9 @@ namespace JSSoft.Communication.ConsoleApp; public interface IApplication : IDisposable { - string Title { get; set; } - Task StartAsync(); Task StopAsync(int exitCode); -} + + string Title { get; set; } +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.ConsoleApp/SystemTerminal.cs b/src/Sharing/JSSoft.Communication.ConsoleApp/SystemTerminal.cs index 21baf9c..d7d26e3 100644 --- a/src/Sharing/JSSoft.Communication.ConsoleApp/SystemTerminal.cs +++ b/src/Sharing/JSSoft.Communication.ConsoleApp/SystemTerminal.cs @@ -1,24 +1,41 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. -using System.ComponentModel.Composition; +using JSSoft.Commands.Extensions; using System.IO; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using JSSoft.Commands.Extensions; +using System.ComponentModel.Composition; using JSSoft.Terminals; namespace JSSoft.Communication.ConsoleApp; [Export] [method: ImportingConstructor] -internal sealed class SystemTerminal(Application application, CommandContext commandContext) +sealed class SystemTerminal(Application application, CommandContext commandContext) : SystemTerminalBase { - private static readonly string Postfix = TerminalEnvironment.IsWindows() == true ? "> " : "$ "; + private static readonly string postfix = TerminalEnvironment.IsWindows() == true ? "> " : "$ "; private readonly Application _application = application; private readonly CommandContext _commandContext = commandContext; @@ -35,20 +52,20 @@ protected override void OnInitialize(TextWriter @out, TextWriter error) protected override string FormatPrompt(string prompt) { - if (_application.UserId == string.Empty) + if (_application.UserID == string.Empty) { return prompt; } else { var tb = new TerminalStringBuilder(); - var pattern = $"(.+@)(.+).{{{Postfix.Length}}}"; + var pattern = $"(.+@)(.+).{{{postfix.Length}}}"; var match = Regex.Match(prompt, pattern); tb.Append(match.Groups[1].Value); tb.Foreground = TerminalColorType.BrightGreen; tb.Append(match.Groups[2].Value); tb.Foreground = null; - tb.Append(Postfix); + tb.Append(postfix); return tb.ToString(); } } diff --git a/src/Sharing/JSSoft.Communication.Services/JSSoft.Communication.Services.projitems b/src/Sharing/JSSoft.Communication.Services/JSSoft.Communication.Services.projitems index 1ddfb5c..26b5d18 100644 --- a/src/Sharing/JSSoft.Communication.Services/JSSoft.Communication.Services.projitems +++ b/src/Sharing/JSSoft.Communication.Services/JSSoft.Communication.Services.projitems @@ -4,6 +4,14 @@ JSSoft.Communication.Services - + + + + + + + + + \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.Services/Services/Authority.cs b/src/Sharing/JSSoft.Communication.Services/Services/Authority.cs index bac7469..f1e60c8 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/Authority.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/Authority.cs @@ -1,24 +1,32 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Services; public enum Authority { - /// - /// Administrator - /// Admin, - /// - /// Member - /// Member, - /// - /// Guest - /// - Guest, -} + Guest +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.Services/Services/IDataService.cs b/src/Sharing/JSSoft.Communication.Services/Services/IDataService.cs index 7c2bb38..1d80849 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/IDataService.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/IDataService.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Threading; diff --git a/src/Sharing/JSSoft.Communication.Services/Services/INotifyUserService.cs b/src/Sharing/JSSoft.Communication.Services/Services/INotifyUserService.cs index 31d6849..f3ff7cf 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/INotifyUserService.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/INotifyUserService.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; @@ -22,4 +39,4 @@ public interface INotifyUserService event EventHandler? Renamed; event EventHandler? AuthorityChanged; -} +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.Services/Services/IUserCallback.cs b/src/Sharing/JSSoft.Communication.Services/Services/IUserCallback.cs index 5460aee..8993950 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/IUserCallback.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/IUserCallback.cs @@ -1,23 +1,40 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Services; public interface IUserCallback { - void OnCreated(string userId); + void OnCreated(string userID); - void OnDeleted(string userId); + void OnDeleted(string userID); - void OnLoggedIn(string userId); + void OnLoggedIn(string userID); - void OnLoggedOut(string userId); + void OnLoggedOut(string userID); void OnMessageReceived(string sender, string receiver, string message); - void OnRenamed(string userId, string userName); + void OnRenamed(string userID, string userName); - void OnAuthorityChanged(string userId, Authority authority); + void OnAuthorityChanged(string userID, Authority authority); } diff --git a/src/Sharing/JSSoft.Communication.Services/Services/IUserService.cs b/src/Sharing/JSSoft.Communication.Services/Services/IUserService.cs index 7efdf3f..9bb596f 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/IUserService.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/IUserService.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; using System.Threading; @@ -12,23 +29,23 @@ namespace JSSoft.Communication.Services; [ServiceContract] public interface IUserService { - Task CreateAsync(UserCreateOptions options, CancellationToken cancellationToken); + Task CreateAsync(Guid token, string userID, string password, Authority authority, CancellationToken cancellationToken); - Task DeleteAsync(UserDeleteOptions options, CancellationToken cancellationToken); + Task DeleteAsync(Guid token, string userID, CancellationToken cancellationToken); - Task RenameAsync(UserRenameOptions options, CancellationToken cancellationToken); + Task RenameAsync(Guid token, string userName, CancellationToken cancellationToken); - Task SetAuthorityAsync(UserAuthorityOptions options, CancellationToken cancellationToken); + Task SetAuthorityAsync(Guid token, string userID, Authority authority, CancellationToken cancellationToken); - Task LoginAsync(UserLoginOptions options, CancellationToken cancellationToken); + Task LoginAsync(string userID, string password, CancellationToken cancellationToken); Task LogoutAsync(Guid token, CancellationToken cancellationToken); - Task GetInfoAsync(Guid token, string userId, CancellationToken cancellationToken); + Task<(string userName, Authority authority)> GetInfoAsync(Guid token, string userID, CancellationToken cancellationToken); Task GetUsersAsync(Guid token, CancellationToken cancellationToken); - Task IsOnlineAsync(Guid token, string userId, CancellationToken cancellationToken); + Task IsOnlineAsync(Guid token, string userID, CancellationToken cancellationToken); - Task SendMessageAsync(UserSendMessageOptions options, CancellationToken cancellationToken); + Task SendMessageAsync(Guid token, string userID, string message, CancellationToken cancellationToken); } diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserAuthorityEventArgs.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserAuthorityEventArgs.cs index f30e4dd..52c6359 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserAuthorityEventArgs.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/UserAuthorityEventArgs.cs @@ -1,11 +1,28 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Services; -public class UserAuthorityEventArgs(string userId, Authority authority) : UserEventArgs(userId) +public class UserAuthorityEventArgs(string userID, Authority authority) : UserEventArgs(userID) { public Authority Authority { get; } = authority; -} +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserAuthorityOptions.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserAuthorityOptions.cs deleted file mode 100644 index c30c853..0000000 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserAuthorityOptions.cs +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; - -namespace JSSoft.Communication.Services; - -public sealed record class UserAuthorityOptions -{ - public required Guid Token { get; init; } - - public required string UserId { get; init; } - - public Authority Authority { get; init; } -} diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserCreateOptions.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserCreateOptions.cs deleted file mode 100644 index 58d4bb4..0000000 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserCreateOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; - -namespace JSSoft.Communication.Services; - -public sealed record class UserCreateOptions -{ - public required Guid Token { get; init; } - - public required string UserId { get; init; } - - public required string Password { get; init; } - - public Authority Authority { get; init; } -} diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserDeleteOptions.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserDeleteOptions.cs deleted file mode 100644 index 070a7df..0000000 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserDeleteOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; - -namespace JSSoft.Communication.Services; - -public sealed record class UserDeleteOptions -{ - public required Guid Token { get; init; } - - public required string UserId { get; init; } -} diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserEventArgs.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserEventArgs.cs index 346ca7c..c5df313 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserEventArgs.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/UserEventArgs.cs @@ -1,13 +1,30 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; namespace JSSoft.Communication.Services; -public class UserEventArgs(string userId) : EventArgs +public class UserEventArgs(string userID) : EventArgs { - public string UserId { get; } = userId; -} + public string UserID { get; } = userID; +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserInfo.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserInfo.cs deleted file mode 100644 index 060afe3..0000000 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; - -namespace JSSoft.Communication.Services; - -public readonly record struct UserInfo -{ - public string UserName { get; init; } - - public Authority Authority { get; init; } -} diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserLoginOptions.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserLoginOptions.cs deleted file mode 100644 index 07fb7c7..0000000 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserLoginOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -namespace JSSoft.Communication.Services; - -public sealed record class UserLoginOptions -{ - public required string UserId { get; init; } - - public required string Password { get; init; } -} diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserMessageEventArgs.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserMessageEventArgs.cs index 71bb551..e82823d 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserMessageEventArgs.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/UserMessageEventArgs.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System; diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserNameEventArgs.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserNameEventArgs.cs index 0ea6088..3ddc7b0 100644 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserNameEventArgs.cs +++ b/src/Sharing/JSSoft.Communication.Services/Services/UserNameEventArgs.cs @@ -1,11 +1,28 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Services; -public class UserNameEventArgs(string userId, string userName) : UserEventArgs(userId) +public class UserNameEventArgs(string userID, string userName) : UserEventArgs(userID) { public string UserName { get; } = userName; -} +} \ No newline at end of file diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserRenameOptions.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserRenameOptions.cs deleted file mode 100644 index 8c207ff..0000000 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserRenameOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; - -namespace JSSoft.Communication.Services; - -public sealed record class UserRenameOptions -{ - public required Guid Token { get; init; } - - public required string UserName { get; init; } -} diff --git a/src/Sharing/JSSoft.Communication.Services/Services/UserSendMessageOptions.cs b/src/Sharing/JSSoft.Communication.Services/Services/UserSendMessageOptions.cs deleted file mode 100644 index bec74c0..0000000 --- a/src/Sharing/JSSoft.Communication.Services/Services/UserSendMessageOptions.cs +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -using System; - -namespace JSSoft.Communication.Services; - -public sealed record class UserSendMessageOptions -{ - public required Guid Token { get; init; } - - public required string UserId { get; init; } - - public string Message { get; init; } = string.Empty; -} diff --git a/stylecop.json b/stylecop.json deleted file mode 100644 index e0bd48f..0000000 --- a/stylecop.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "orderingRules": { - "usingDirectivesPlacement": "outsideNamespace" - }, - "documentationRules": { - "companyName": "JSSoft", - "copyrightText": " Copyright (c) {year} {author}. All Rights Reserved.\n Licensed under the {licenseName} License. See {licenseFile} in the project root for license information.", - "variables": { - "licenseName": "MIT", - "licenseFile": "LICENSE.md", - "year": "2024", - "author": "Jeesu Choi" - } - } - } -} diff --git a/test/JSSoft.Communication.Tests/CallbackTest.cs b/test/JSSoft.Communication.Tests/CallbackTest.cs index eeb9e66..555ee38 100644 --- a/test/JSSoft.Communication.Tests/CallbackTest.cs +++ b/test/JSSoft.Communication.Tests/CallbackTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using JSSoft.Communication.Tests.Extensions; using Xunit.Abstractions; @@ -30,13 +47,18 @@ public CallbackTest(ITestOutputHelper logger) logger.WriteLine($"{_endPoint}"); } + public class ValueEventArgs(object? value) : EventArgs + { + public object? Value { get; } = value; + } + public interface ITestService { void Invoke(); void Invoke(int value); - void Invoke((int Value1, string Value2) value); + void Invoke((int value1, string value2) value); } public interface ITestCallback @@ -45,7 +67,41 @@ public interface ITestCallback void OnInvoked(int value); - void OnInvoked((int Value1, string Value2) value); + void OnInvoked((int value1, string value2) value); + } + + sealed class TestServer : ServerService, ITestService + { + public void Invoke() => Client.OnInvoked(); + + public void Invoke(int value) => Client.OnInvoked(value); + + public void Invoke((int value1, string value2) value) => Client.OnInvoked(value); + } + + sealed class TestClient : ClientService, ITestCallback + { + public AutoResetEvent AutoResetEvent { get; } = new(initialState: false); + + public event EventHandler? Invoked; + + void ITestCallback.OnInvoked() + { + Invoked?.Invoke(this, new(null)); + AutoResetEvent.Set(); + } + + void ITestCallback.OnInvoked(int value) + { + Invoked?.Invoke(this, new(value)); + AutoResetEvent.Set(); + } + + void ITestCallback.OnInvoked((int value1, string value2) value) + { + Invoked?.Invoke(this, new(value)); + AutoResetEvent.Set(); + } } [Fact] @@ -109,43 +165,4 @@ public async Task DisposeAsync() _endPoint.Dispose(); _logger.WriteLine($"DisposeAsync 2"); } - - public class ValueEventArgs(object? value) : EventArgs - { - public object? Value { get; } = value; - } - - private sealed class TestServer : ServerService, ITestService - { - public void Invoke() => Client.OnInvoked(); - - public void Invoke(int value) => Client.OnInvoked(value); - - public void Invoke((int Value1, string Value2) value) => Client.OnInvoked(value); - } - - private sealed class TestClient : ClientService, ITestCallback - { - public event EventHandler? Invoked; - - public AutoResetEvent AutoResetEvent { get; } = new(initialState: false); - - void ITestCallback.OnInvoked() - { - Invoked?.Invoke(this, new(null)); - AutoResetEvent.Set(); - } - - void ITestCallback.OnInvoked(int value) - { - Invoked?.Invoke(this, new(value)); - AutoResetEvent.Set(); - } - - void ITestCallback.OnInvoked((int Value1, string Value2) value) - { - Invoked?.Invoke(this, new(value)); - AutoResetEvent.Set(); - } - } } diff --git a/test/JSSoft.Communication.Tests/ClientContextTest.cs b/test/JSSoft.Communication.Tests/ClientContextTest.cs index 158752d..348e5c1 100644 --- a/test/JSSoft.Communication.Tests/ClientContextTest.cs +++ b/test/JSSoft.Communication.Tests/ClientContextTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using JSSoft.Communication.Extensions; @@ -13,27 +30,42 @@ public sealed class ClientContextTest : IAsyncLifetime private readonly RandomEndPoint _endPoint = new(); private Guid _token; - public ClientContextTest() + public interface ITestService1 : IService { - _serverContext = new() { EndPoint = _endPoint }; } - public interface ITestService1 : IService + public interface ITestService2 : IService { } - public interface ITestService2 : IService + sealed class TestService1 : ClientService { } + sealed class TestService2 : ClientService + { + } + + public ClientContextTest() + { + _serverContext = new() { EndPoint = _endPoint }; + } + [Fact] public void Constructor_Test() { var clientContext0 = new ClientContext(); Assert.Empty(clientContext0.Services); - var clientContext1 = new ClientContext(new TestService2()); + var clientContext1 = new ClientContext(services: + [ + new TestService2(), + ]); Assert.Single(clientContext1.Services); - var clientContext2 = new ClientContext(new TestService1(), new TestService2()); + var clientContext2 = new ClientContext(services: + [ + new TestService1(), + new TestService2(), + ]); Assert.Equal(2, clientContext2.Services.Count); } @@ -99,7 +131,7 @@ public async Task Open_Cancel_Abort_TestAsync() { var endPoint = _serverContext.EndPoint; var clientContext = new ClientContext() { EndPoint = endPoint }; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); await Assert.ThrowsAnyAsync( () => clientContext.OpenAsync(cancellationTokenSource.Token)); Assert.Equal(ServiceState.Faulted, clientContext.ServiceState); @@ -125,7 +157,7 @@ public async Task Open_Close_Cancel_Abort_TestAsync() { var endPoint = _serverContext.EndPoint; var clientContext = new ClientContext() { EndPoint = endPoint }; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); var token = await clientContext.OpenAsync(cancellationToken: default); await Assert.ThrowsAnyAsync( () => clientContext.CloseAsync(token, cancellationTokenSource.Token)); @@ -153,7 +185,7 @@ public async Task Opened_TestAsync() var endPoint = _serverContext.EndPoint; var clientContext = new ClientContext() { EndPoint = endPoint }; var token = Guid.Empty; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 5000); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 5000); var result = await EventTestUtility.RaisesAsync( h => clientContext.Opened += h, h => clientContext.Opened -= h, @@ -168,7 +200,7 @@ public async Task Closed_TestAsync() { var endPoint = _serverContext.EndPoint; var clientContext = new ClientContext() { EndPoint = endPoint }; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 5000); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 5000); var token = await clientContext.OpenAsync(cancellationToken: default); var result = await EventTestUtility.RaisesAsync( h => clientContext.Closed += h, @@ -183,7 +215,7 @@ public async Task Faulted_TestAsync() { var endPoint = _serverContext.EndPoint; var clientContext = new ClientContext() { EndPoint = endPoint }; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); var result = await EventTestUtility.RaisesAsync( h => clientContext.Faulted += h, h => clientContext.Faulted -= h, @@ -195,7 +227,6 @@ public async Task Faulted_TestAsync() } catch { - // do nothing } }); @@ -219,7 +250,6 @@ public async Task Disconnected_TestAsync() } catch { - // do nothing } }); @@ -252,15 +282,6 @@ public async Task DisposeAsync() { await _serverContext.CloseAsync(_token, cancellationToken: default); } - _endPoint.Dispose(); } - - private sealed class TestService1 : ClientService - { - } - - private sealed class TestService2 : ClientService - { - } } diff --git a/test/JSSoft.Communication.Tests/ClientTestBase.cs b/test/JSSoft.Communication.Tests/ClientTestBase.cs index e2c1529..76b72ca 100644 --- a/test/JSSoft.Communication.Tests/ClientTestBase.cs +++ b/test/JSSoft.Communication.Tests/ClientTestBase.cs @@ -1,10 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - -// File may only contain a single type -#pragma warning disable SA1402 +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using JSSoft.Communication.Tests.Extensions; @@ -29,10 +43,6 @@ protected ClientTestBase(ServerService serverService) _clientContext = new(_clientService) { EndPoint = _endPoint }; } - protected TService Client => _client!; - - protected ServerService ServerService { get; } - public async Task InitializeAsync() { _serverToken = await _serverContext.OpenAsync(cancellationToken: default); @@ -46,6 +56,10 @@ public async Task DisposeAsync() await _clientContext.ReleaseAsync(_clientToken); _endPoint.Dispose(); } + + protected TService Client => _client!; + + protected ServerService ServerService { get; } } public abstract class ClientTestBase : IAsyncLifetime @@ -68,10 +82,6 @@ protected ClientTestBase(TServerSevice serverService) _clientContext = new(_clientService) { EndPoint = _endPoint }; } - protected TService Client => _client!; - - protected TServerSevice ServerService { get; } - public async Task InitializeAsync() { _serverToken = await _serverContext.OpenAsync(cancellationToken: default); @@ -85,4 +95,8 @@ public async Task DisposeAsync() await _clientContext.ReleaseAsync(_clientToken); _endPoint.Dispose(); } + + protected TService Client => _client!; + + protected TServerSevice ServerService { get; } } diff --git a/test/JSSoft.Communication.Tests/DispatcherTest.cs b/test/JSSoft.Communication.Tests/DispatcherTest.cs index 9c8c391..b3125b7 100644 --- a/test/JSSoft.Communication.Tests/DispatcherTest.cs +++ b/test/JSSoft.Communication.Tests/DispatcherTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using JSSoft.Communication.Threading; @@ -68,6 +85,17 @@ await Assert.ThrowsAsync(async () => }); } + [Fact] + public async Task InvokeAsync_FailTest2() + { + var dispatcher = new Dispatcher(new()); + dispatcher.Dispose(); + await Assert.ThrowsAsync(async () => + { + await dispatcher.InvokeAsync(() => { }); + }); + } + [Fact] public async Task InvokeGenericAsync_Test() { @@ -87,24 +115,20 @@ await Assert.ThrowsAsync(async () => }); } - // "Thread.Sleep" should not be used in tests -#pragma warning disable S2925 [Fact] - public async Task InvokeGenericAsync_WaitTest() + public void InvokeGenericAsync_WaitTest() { var dispatcher = new Dispatcher(new()); var b = false; - var task = dispatcher.InvokeAsync(() => + dispatcher.InvokeAsync(() => { Thread.Sleep(1000); b = true; }); - await Task.Delay(10); + Thread.Sleep(10); dispatcher.Dispose(); - await task; Assert.True(b); } -#pragma warning restore S2925 [Fact] public void Post_Test() diff --git a/test/JSSoft.Communication.Tests/EventTestUtility.cs b/test/JSSoft.Communication.Tests/EventTestUtility.cs index 9232995..2034a28 100644 --- a/test/JSSoft.Communication.Tests/EventTestUtility.cs +++ b/test/JSSoft.Communication.Tests/EventTestUtility.cs @@ -1,17 +1,33 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests; public static class EventTestUtility { - public static async Task RaisesAsync( - Action attach, Action detach, Func testCode) + public async static Task RaisesAsync(Action attach, Action detach, Func testCode) { using var manualResetEvent = new ManualResetEvent(initialState: false); - attach(Handler); + attach(handler); try { await testCode(); @@ -22,12 +38,10 @@ public static async Task RaisesAsync( } finally { - detach(Handler); + detach(handler); } - return manualResetEvent.WaitOne(millisecondsTimeout: 10000); - - void Handler(object? s, EventArgs args) + void handler(object? s, EventArgs args) { manualResetEvent.Set(); } diff --git a/test/JSSoft.Communication.Tests/Exceptions/ArgumentExceptionTest.cs b/test/JSSoft.Communication.Tests/Exceptions/ArgumentExceptionTest.cs index a06a5db..4440e4e 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/ArgumentExceptionTest.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/ArgumentExceptionTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; diff --git a/test/JSSoft.Communication.Tests/Exceptions/ArgumentNullExceptionTest.cs b/test/JSSoft.Communication.Tests/Exceptions/ArgumentNullExceptionTest.cs index b782a70..b85b73a 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/ArgumentNullExceptionTest.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/ArgumentNullExceptionTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; diff --git a/test/JSSoft.Communication.Tests/Exceptions/ArgumentOutOfRangeExceptionTest.cs b/test/JSSoft.Communication.Tests/Exceptions/ArgumentOutOfRangeExceptionTest.cs index 07ff1ff..19f9a53 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/ArgumentOutOfRangeExceptionTest.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/ArgumentOutOfRangeExceptionTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; diff --git a/test/JSSoft.Communication.Tests/Exceptions/ExceptionTestBase.cs b/test/JSSoft.Communication.Tests/Exceptions/ExceptionTestBase.cs index 2186dbe..b369da7 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/ExceptionTestBase.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/ExceptionTestBase.cs @@ -1,12 +1,28 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; -public abstract class ExceptionTestBase - : ClientTestBase.ITestService> +public abstract class ExceptionTestBase : ClientTestBase.ITestService> where TException : Exception { protected ExceptionTestBase() @@ -16,30 +32,21 @@ protected ExceptionTestBase() public interface ITestService { - void Invoke() - => throw (TException)Activator.CreateInstance(typeof(TException), nameof(Invoke))!; + void Invoke() => throw (TException)Activator.CreateInstance(typeof(TException), args: [nameof(Invoke)])!; - Task InvokeAsync() - => throw (TException)Activator.CreateInstance(typeof(TException), nameof(Invoke))!; + Task InvokeAsync() => throw (TException)Activator.CreateInstance(typeof(TException), args: [nameof(Invoke)])!; - Task InvokeAndReturnAsync() - => throw (TException)Activator.CreateInstance(typeof(TException), nameof(Invoke))!; + Task InvokeAndReturnAsync() => throw (TException)Activator.CreateInstance(typeof(TException), args: [nameof(Invoke)])!; + } + + sealed class TestServer : ServerService, ITestService + { } [Fact] public void Invoke_Test() { - var b = true; - try - { - Client.Invoke(); - } - catch - { - b = false; - } - - Assert.True(b); + Client.Invoke(); } [Fact] @@ -65,8 +72,4 @@ public async Task InvokeAndReturnAsyncWithCancellation_Test() { await Assert.ThrowsAsync(() => Client.InvokeAndReturnAsync()); } - - private sealed class TestServer : ServerService, ITestService - { - } } diff --git a/test/JSSoft.Communication.Tests/Exceptions/IndexOutOfRangeExceptionTest.cs b/test/JSSoft.Communication.Tests/Exceptions/IndexOutOfRangeExceptionTest.cs index b669af4..3ee5230 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/IndexOutOfRangeExceptionTest.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/IndexOutOfRangeExceptionTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; diff --git a/test/JSSoft.Communication.Tests/Exceptions/InvalidOperationExceptionTest.cs b/test/JSSoft.Communication.Tests/Exceptions/InvalidOperationExceptionTest.cs index bfcb5bd..5c00c78 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/InvalidOperationExceptionTest.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/InvalidOperationExceptionTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; diff --git a/test/JSSoft.Communication.Tests/Exceptions/NotSupportedExceptionTest.cs b/test/JSSoft.Communication.Tests/Exceptions/NotSupportedExceptionTest.cs index 5a21e16..119211b 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/NotSupportedExceptionTest.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/NotSupportedExceptionTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; diff --git a/test/JSSoft.Communication.Tests/Exceptions/NullReferenceExceptionTest.cs b/test/JSSoft.Communication.Tests/Exceptions/NullReferenceExceptionTest.cs index fd67257..0ef9020 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/NullReferenceExceptionTest.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/NullReferenceExceptionTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; diff --git a/test/JSSoft.Communication.Tests/Exceptions/ObjectDisposedExceptionTest.cs b/test/JSSoft.Communication.Tests/Exceptions/ObjectDisposedExceptionTest.cs index 2438b41..d0e4ca8 100644 --- a/test/JSSoft.Communication.Tests/Exceptions/ObjectDisposedExceptionTest.cs +++ b/test/JSSoft.Communication.Tests/Exceptions/ObjectDisposedExceptionTest.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Exceptions; diff --git a/test/JSSoft.Communication.Tests/Extensions/ServiceContextExtensions.cs b/test/JSSoft.Communication.Tests/Extensions/ServiceContextExtensions.cs index d00de6d..77c15e5 100644 --- a/test/JSSoft.Communication.Tests/Extensions/ServiceContextExtensions.cs +++ b/test/JSSoft.Communication.Tests/Extensions/ServiceContextExtensions.cs @@ -1,11 +1,28 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests.Extensions; -internal static class ServiceContextExtensions +static class ServiceContextExtensions { public static async Task ReleaseAsync(this IServiceContext @this, Guid token) { @@ -17,10 +34,8 @@ public static async Task ReleaseAsync(this IServiceContext @this, Guid token) } catch { - // do nothing } } - if (@this.ServiceState == ServiceState.Faulted) { await @this.AbortAsync(); diff --git a/test/JSSoft.Communication.Tests/GlobalUsings.cs b/test/JSSoft.Communication.Tests/GlobalUsings.cs index 2ce8465..3fab5bf 100644 --- a/test/JSSoft.Communication.Tests/GlobalUsings.cs +++ b/test/JSSoft.Communication.Tests/GlobalUsings.cs @@ -1,6 +1,23 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. -global using Xunit; +global using Xunit; \ No newline at end of file diff --git a/test/JSSoft.Communication.Tests/InvokeTest.cs b/test/JSSoft.Communication.Tests/InvokeTest.cs index 70a96ce..6b0b4d8 100644 --- a/test/JSSoft.Communication.Tests/InvokeTest.cs +++ b/test/JSSoft.Communication.Tests/InvokeTest.cs @@ -1,8 +1,3 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - namespace JSSoft.Communication.Tests; public class InvokeTest : ClientTestBase @@ -18,13 +13,47 @@ public interface ITestService Task InvokeAsync(); - Task InvokeAsync(CancellationToken cancellationToken); - Task InvokeAndReturnAsync(); + Task InvokeAsync(CancellationToken cancellationToken); + Task InvokeAndReturnAsync(CancellationToken cancellationToken); } + public sealed class TestServer : ServerService, ITestService + { + public object? Result { get; set; } + + public void Invoke() + { + Result = nameof(Invoke); + } + + public Task InvokeAsync() + { + Result = nameof(InvokeAsync); + return Task.CompletedTask; + } + + public Task InvokeAndReturnAsync() + { + Result = nameof(InvokeAndReturnAsync); + return Task.Run(() => 2); + } + + public Task InvokeAsync(CancellationToken cancellationToken) + { + Result = nameof(InvokeAsync) + nameof(CancellationToken); + return Task.CompletedTask; + } + + public Task InvokeAndReturnAsync(CancellationToken cancellationToken) + { + Result = nameof(InvokeAndReturnAsync) + nameof(CancellationToken); + return Task.Run(() => 3); + } + } + [Fact] public void Invoke_Test() { @@ -58,42 +87,7 @@ public async Task InvokeAsyncWithCancellation_Test() public async Task InvokeAndReturnAsyncWithCancellation_Test() { var actualValue = await Client.InvokeAndReturnAsync(CancellationToken.None); - var expectedResult = nameof(Client.InvokeAndReturnAsync) + nameof(CancellationToken); - Assert.Equal(expectedResult, ServerService.Result); + Assert.Equal(nameof(Client.InvokeAndReturnAsync) + nameof(CancellationToken), ServerService.Result); Assert.Equal(3, actualValue); } - - public sealed class TestServer : ServerService, ITestService - { - public object? Result { get; set; } - - public void Invoke() - { - Result = nameof(Invoke); - } - - public Task InvokeAsync() - { - Result = nameof(InvokeAsync); - return Task.CompletedTask; - } - - public Task InvokeAsync(CancellationToken cancellationToken) - { - Result = nameof(InvokeAsync) + nameof(CancellationToken); - return Task.CompletedTask; - } - - public Task InvokeAndReturnAsync() - { - Result = nameof(InvokeAndReturnAsync); - return Task.Run(() => 2); - } - - public Task InvokeAndReturnAsync(CancellationToken cancellationToken) - { - Result = nameof(InvokeAndReturnAsync) + nameof(CancellationToken); - return Task.Run(() => 3); - } - } } diff --git a/test/JSSoft.Communication.Tests/RandomEndPoint.cs b/test/JSSoft.Communication.Tests/RandomEndPoint.cs index 171a8bb..7981fad 100644 --- a/test/JSSoft.Communication.Tests/RandomEndPoint.cs +++ b/test/JSSoft.Communication.Tests/RandomEndPoint.cs @@ -1,14 +1,31 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using System.Net; using System.Net.Sockets; namespace JSSoft.Communication.Tests; -internal sealed class RandomEndPoint : IDisposable +sealed class RandomEndPoint : IDisposable { private static readonly object LockObject = new(); private static readonly List PortList = []; @@ -33,13 +50,15 @@ public override string ToString() public void Dispose() { - ObjectDisposedException.ThrowIf(_isDisposed, this); + if (_isDisposed == true) + { + throw new ObjectDisposedException($"{this}"); + } lock (LockObject) { PortList.Remove(_endPoint.Port); } - _isDisposed = true; } diff --git a/test/JSSoft.Communication.Tests/ServerContextTest.cs b/test/JSSoft.Communication.Tests/ServerContextTest.cs index 32d017b..3c859d1 100644 --- a/test/JSSoft.Communication.Tests/ServerContextTest.cs +++ b/test/JSSoft.Communication.Tests/ServerContextTest.cs @@ -1,8 +1,26 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// - +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Security.Principal; using JSSoft.Communication.Extensions; namespace JSSoft.Communication.Tests; @@ -19,14 +37,29 @@ public interface ITestService2 : IService { } + sealed class TestService1 : ServerService, ITestService1 + { + } + + sealed class TestService2 : ServerService, ITestService2 + { + } + [Fact] public void Constructor_Test() { var serverContext0 = new ServerContext(); Assert.Empty(serverContext0.Services); - var serverContext1 = new ServerContext(new TestService2()); + var serverContext1 = new ServerContext(services: + [ + new TestService2(), + ]); Assert.Single(serverContext1.Services); - var serverContext2 = new ServerContext(new TestService1(), new TestService2()); + var serverContext2 = new ServerContext(services: + [ + new TestService1(), + new TestService2(), + ]); Assert.Equal(2, serverContext2.Services.Count); } @@ -92,7 +125,7 @@ public async Task Open_Cancel_Abort_TestAsync() { using var endPoint = new RandomEndPoint(); var serverContext = new ServerContext() { EndPoint = endPoint }; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); await Assert.ThrowsAnyAsync( () => serverContext.OpenAsync(cancellationTokenSource.Token)); Assert.Equal(ServiceState.Faulted, serverContext.ServiceState); @@ -116,7 +149,7 @@ public async Task Open_Close_Cancel_Abort_TestAsync() { using var endPoint = new RandomEndPoint(); var serverContext = new ServerContext() { EndPoint = endPoint }; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); var token = await serverContext.OpenAsync(cancellationToken: default); await Assert.ThrowsAnyAsync( () => serverContext.CloseAsync(token, cancellationTokenSource.Token)); @@ -145,7 +178,7 @@ public async Task Opened_TestAsync() using var endPoint = new RandomEndPoint(); var serverContext = new ServerContext() { EndPoint = endPoint }; var token = Guid.Empty; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: Timeout); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: Timeout); var result = await EventTestUtility.RaisesAsync( h => serverContext.Opened += h, h => serverContext.Opened -= h, @@ -160,7 +193,7 @@ public async Task Closed_TestAsync() { using var endPoint = new RandomEndPoint(); var serverContext = new ServerContext() { EndPoint = endPoint }; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: Timeout); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: Timeout); var token = await serverContext.OpenAsync(cancellationToken: default); var result = await EventTestUtility.RaisesAsync( h => serverContext.Closed += h, @@ -175,7 +208,7 @@ public async Task Faulted_TestAsync() { using var endPoint = new RandomEndPoint(); var serverContext = new ServerContext() { EndPoint = endPoint }; - using var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); + var cancellationTokenSource = new CancellationTokenSource(millisecondsDelay: 0); var result = await EventTestUtility.RaisesAsync( h => serverContext.Faulted += h, h => serverContext.Faulted -= h, @@ -187,7 +220,6 @@ public async Task Faulted_TestAsync() } catch { - // do nothing } }); @@ -208,12 +240,4 @@ public async Task ServiceStateChanged_TestAsync() Assert.True(result); await serverContext.ReleaseAsync(token); } - - private sealed class TestService1 : ServerService, ITestService1 - { - } - - private sealed class TestService2 : ServerService, ITestService2 - { - } } diff --git a/test/JSSoft.Communication.Tests/TestLogger.cs b/test/JSSoft.Communication.Tests/TestLogger.cs index ff2e341..2fabe3a 100644 --- a/test/JSSoft.Communication.Tests/TestLogger.cs +++ b/test/JSSoft.Communication.Tests/TestLogger.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. using JSSoft.Communication.Logging; using Xunit.Abstractions; diff --git a/test/JSSoft.Communication.Tests/UnitTest1.cs b/test/JSSoft.Communication.Tests/UnitTest1.cs index 70fede0..99a0c56 100644 --- a/test/JSSoft.Communication.Tests/UnitTest1.cs +++ b/test/JSSoft.Communication.Tests/UnitTest1.cs @@ -1,7 +1,24 @@ -// -// Copyright (c) 2024 Jeesu Choi. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.md in the project root for license information. -// +// MIT License +// +// Copyright (c) 2024 Jeesu Choi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. namespace JSSoft.Communication.Tests; @@ -17,6 +34,21 @@ public interface ITestClient void OnMessageSend(string message); } + sealed class TestServer : ServerService, ITestServer + { + public async Task SendMessageAsync(string message, CancellationToken cancellationToken) + { + await Task.Delay(1000, cancellationToken); + } + } + + sealed class TestClient : ClientService, ITestClient + { + public void OnMessageSend(string message) + { + } + } + [Fact] public async Task OpenAndClientCloseAsync() { @@ -29,9 +61,6 @@ public async Task OpenAndClientCloseAsync() await clientContext.CloseAsync(clientToken, CancellationToken.None); await serverContext.CloseAsync(serverToken, CancellationToken.None); - - Assert.Equal(ServiceState.None, clientContext.ServiceState); - Assert.Equal(ServiceState.None, serverContext.ServiceState); } [Fact] @@ -41,9 +70,9 @@ public async Task OpenAndServerCloseAsync() var serverContext = new ServerContext(new TestServer()) { EndPoint = endPoint }; var clientContext = new ClientContext(new TestClient()) { EndPoint = endPoint }; - var autoResetEvent = new AutoResetEvent(initialState: false); var serverToken = await serverContext.OpenAsync(CancellationToken.None); - await clientContext.OpenAsync(CancellationToken.None); + var clientToken = await clientContext.OpenAsync(CancellationToken.None); + var autoResetEvent = new AutoResetEvent(initialState: false); clientContext.Disconnected += (s, e) => autoResetEvent.Set(); await serverContext.CloseAsync(serverToken, CancellationToken.None); @@ -63,19 +92,15 @@ public async Task MultipleOpenAndClientCloseAsync() var count = 20; using var endPoint = new RandomEndPoint(); var serverContext = new ServerContext(new TestServer()) { EndPoint = endPoint }; - var clientContexts = Enumerable.Range(0, count) - .Select(CreateClientContext) - .ToArray(); + var clientContexts = Enumerable.Range(0, count).Select(item => new ClientContext(new TestClient()) { EndPoint = endPoint }).ToArray(); var serverToken = await serverContext.OpenAsync(CancellationToken.None); - var openTasks = clientContexts.Select(item => item.OpenAsync(CancellationToken.None)) - .ToArray(); + var openTasks = clientContexts.Select(item => item.OpenAsync(CancellationToken.None)).ToArray(); var tokens = await Task.WhenAll(openTasks); var closeTasks = new Task[count]; for (var i = 0; i < count; i++) { closeTasks[i] = clientContexts[i].CloseAsync(tokens[i], CancellationToken.None); } - await Task.WhenAll(closeTasks); await serverContext.CloseAsync(serverToken, CancellationToken.None); Assert.Equal(ServiceState.None, serverContext.ServiceState); @@ -83,14 +108,6 @@ public async Task MultipleOpenAndClientCloseAsync() { Assert.Equal(ServiceState.None, clientContexts[i].ServiceState); } - - ClientContext CreateClientContext(int index) - { - return new ClientContext(new TestClient()) - { - EndPoint = endPoint, - }; - } } [Fact] @@ -109,23 +126,5 @@ public async Task OpenAndInvokeAndClientCloseAsync() await clientContext.CloseAsync(clientToken, CancellationToken.None); await serverContext.CloseAsync(serverToken, CancellationToken.None); - - Assert.Equal(ServiceState.None, clientContext.ServiceState); - Assert.Equal(ServiceState.None, serverContext.ServiceState); - } - - private sealed class TestServer : ServerService, ITestServer - { - public async Task SendMessageAsync(string message, CancellationToken cancellationToken) - { - await Task.Delay(1000, cancellationToken); - } - } - - private sealed class TestClient : ClientService, ITestClient - { - public void OnMessageSend(string message) - { - } } }