You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2022/10/20 06:17:52 UTC
[ignite] branch master updated: IGNITE-17796 .NET: Support default interface methods in Services (#10335)
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 8a799ef35e5 IGNITE-17796 .NET: Support default interface methods in Services (#10335)
8a799ef35e5 is described below
commit 8a799ef35e52444d6172e034d32caad344f4d9c6
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Thu Oct 20 09:17:47 2022 +0300
IGNITE-17796 .NET: Support default interface methods in Services (#10335)
When resolving service method in `ServiceProxyInvoker`, also check default interface methods.
---
.../Services/ServicesTest.cs | 85 +++++++++++++++++++++-
.../Impl/Services/ServiceProxyInvoker.cs | 20 ++++-
2 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
index 2fbb7a4f2b0..16955aa55b9 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -42,7 +42,7 @@ namespace Apache.Ignite.Core.Tests.Services
/// <summary>
/// Services tests.
/// </summary>
-#pragma warning disable 618
+#pragma warning disable CS0618 // Method is obsolete.
public class ServicesTest
{
/** */
@@ -1676,6 +1676,41 @@ namespace Apache.Ignite.Core.Tests.Services
}
}
+#if NETCOREAPP
+ /// <summary>
+ /// Tests service method with default interface implementation.
+ /// </summary>
+ [Test]
+ public void TestDefaultInterfaceMethod()
+ {
+ var name = nameof(TestDefaultInterfaceMethod);
+ Services.DeployClusterSingleton(name, new TestServiceWithDefaultImpl());
+
+ var prx = Services.GetServiceProxy<ITestServiceWithDefaultImpl>(name);
+ var res = prx.GetInt();
+
+ Assert.AreEqual(42, res);
+ }
+
+ /// <summary>
+ /// Tests service method with default interface implementation that is overridden in the class.
+ /// </summary>
+ [Test]
+ public void TestDefaultInterfaceMethodOverridden()
+ {
+ var svcImpl = new TestServiceWithDefaultImplOverridden();
+
+ var name = nameof(TestDefaultInterfaceMethodOverridden);
+ Services.DeployClusterSingleton(name, svcImpl);
+
+ var prx = Services.GetServiceProxy<ITestServiceWithDefaultImpl>(name);
+ var res = prx.GetInt();
+
+ Assert.AreEqual(43, ((ITestServiceWithDefaultImpl)svcImpl).GetInt());
+ Assert.AreEqual(43, res);
+ }
+#endif
+
/// <summary>
/// Starts the grids.
/// </summary>
@@ -2424,6 +2459,54 @@ namespace Apache.Ignite.Core.Tests.Services
}
#if NETCOREAPP
+ public interface ITestServiceWithDefaultImpl
+ {
+ int GetInt() => 42;
+
+ int GetInt(int x) => x + 42;
+ }
+
+ private class TestServiceWithDefaultImpl : ITestServiceWithDefaultImpl, IService
+ {
+ public void Init(IServiceContext context)
+ {
+ // No-op.
+ }
+
+ public void Execute(IServiceContext context)
+ {
+ // No-op.
+ }
+
+ public void Cancel(IServiceContext context)
+ {
+ // No-op.
+ }
+
+ // ReSharper disable once UnusedMember.Local (ensure overload resolution)
+ int GetInt(string x) => x.Length;
+ }
+
+ private class TestServiceWithDefaultImplOverridden : ITestServiceWithDefaultImpl, IService
+ {
+ public void Init(IServiceContext context)
+ {
+ // No-op.
+ }
+
+ public void Execute(IServiceContext context)
+ {
+ // No-op.
+ }
+
+ public void Cancel(IServiceContext context)
+ {
+ // No-op.
+ }
+
+ int ITestServiceWithDefaultImpl.GetInt() => 43;
+ }
+
/// <summary>
/// Adds support of the local dates to the Ignite timestamp serialization.
/// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs
index 25cdebd25fa..24354f9de74 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/ServiceProxyInvoker.cs
@@ -74,6 +74,11 @@ namespace Apache.Ignite.Core.Impl.Services
/// <summary>
/// Finds suitable method in the specified type, or throws an exception.
+ /// <para />
+ /// We do not try to cover all kinds of intricate use cases with complex class hierarchies,
+ /// explicit interface implementations, and so on.
+ /// It is not possible given only the type, name, and arguments - the call can come from other languages too.
+ /// So we do our best or throw an error.
/// </summary>
private static Func<object, object[], object> GetMethodOrThrow(Type svcType, string methodName,
object[] arguments)
@@ -91,10 +96,21 @@ namespace Apache.Ignite.Core.Impl.Services
return res;
// 1) Find methods by name
- var methods = svcType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+
+ var methods = svcType.GetMethods(bindingFlags)
.Where(m => CleanupMethodName(m) == methodName && m.GetParameters().Length == argsLength)
.ToArray();
+ if (methods.Length == 0)
+ {
+ // Check default interface implementations.
+ // This does not cover exotic use cases when methods with same signature come from multiple interfaces.
+ methods = svcType.GetInterfaces().SelectMany(x => x.GetMethods(bindingFlags))
+ .Where(m => !m.IsAbstract && CleanupMethodName(m) == methodName && m.GetParameters().Length == argsLength)
+ .ToArray();
+ }
+
if (methods.Length == 1)
{
// Update cache only when there is a single method with a given name and arg count.
@@ -104,7 +120,7 @@ namespace Apache.Ignite.Core.Impl.Services
if (methods.Length == 0)
throw new InvalidOperationException(
string.Format(CultureInfo.InvariantCulture,
- "Failed to invoke proxy: there is no method '{0}' in type '{1}' with {2} arguments",
+ "Failed to invoke proxy: there is no method '{0}' in type '{1}' with {2} arguments",
methodName, svcType, argsLength));
// 2) There is more than 1 method with specified name - resolve with argument types.