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.1net8.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