You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2021/02/05 14:40:11 UTC

[ignite] branch master updated: IGNITE-14020 .NET: Services exceptions interoperability between java and .NET - Fixes #8746.

This is an automated email from the ASF dual-hosted git repository.

alexpl 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 82d2a2c  IGNITE-14020 .NET: Services exceptions interoperability between java and .NET - Fixes #8746.
82d2a2c is described below

commit 82d2a2c66a03db6678d2fa332475200316fda5a6
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Fri Feb 5 17:36:51 2021 +0300

    IGNITE-14020 .NET: Services exceptions interoperability between java and .NET - Fixes #8746.
    
    Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
 .../platform/services/PlatformServices.java        | 13 +---
 .../processors/service/GridServiceProxy.java       | 14 ++--
 .../ignite/platform/PlatformDeployServiceTask.java | 36 +++++++++
 .../Services/IJavaService.cs                       |  3 +
 .../Services/JavaServiceDynamicProxy.cs            |  6 ++
 .../Services/ServicesTest.cs                       | 88 +++++++++++++++++++++-
 .../Impl/Plugin/PluginProcessor.cs                 | 34 ++++++++-
 7 files changed, 171 insertions(+), 23 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
index f183637..421b35e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
@@ -41,7 +41,6 @@ import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
 import org.apache.ignite.internal.processors.platform.utils.PlatformWriterBiClosure;
 import org.apache.ignite.internal.processors.platform.utils.PlatformWriterClosure;
 import org.apache.ignite.internal.processors.service.GridServiceProxy;
-import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.internal.util.typedef.T3;
 import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.lang.IgnitePredicate;
@@ -291,7 +290,7 @@ public class PlatformServices extends PlatformAbstractTarget {
 
                     PlatformUtils.writeInvocationResult(writer, result, null);
                 }
-                catch (Exception e) {
+                catch (Throwable e) {
                     PlatformUtils.writeInvocationResult(writer, null, e);
                 }
 
@@ -610,8 +609,7 @@ public class PlatformServices extends PlatformAbstractTarget {
          * @throws IgniteCheckedException On error.
          * @throws NoSuchMethodException On error.
          */
-        public Object invoke(String mthdName, boolean srvKeepBinary, Object[] args)
-            throws IgniteCheckedException, NoSuchMethodException {
+        public Object invoke(String mthdName, boolean srvKeepBinary, Object[] args) throws Throwable {
             if (isPlatformService())
                 return ((PlatformService)proxy).invokeMethod(mthdName, srvKeepBinary, args);
             else {
@@ -624,12 +622,7 @@ public class PlatformServices extends PlatformAbstractTarget {
                 Method mtd = getMethod(serviceClass, mthdName, args);
                 convertArrayArgs(args, mtd);
 
-                try {
-                    return ((GridServiceProxy)proxy).invokeMethod(mtd, args);
-                }
-                catch (Throwable t) {
-                    throw IgniteUtils.cast(t);
-                }
+                return ((GridServiceProxy)proxy).invokeMethod(mtd, args);
             }
         }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
index 3c92ebc..cc65f34 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
@@ -191,14 +191,8 @@ public class GridServiceProxy<T> implements Serializable {
                         if (svcCtx != null) {
                             Service svc = svcCtx.service();
 
-                            if (svc != null) {
-                                try {
-                                    return callServiceLocally(svc, mtd, args);
-                                }
-                                catch (InvocationTargetException e) {
-                                    throw e.getTargetException();
-                                }
-                            }
+                            if (svc != null)
+                                return callServiceLocally(svc, mtd, args);
                         }
                     }
                     else {
@@ -214,6 +208,10 @@ public class GridServiceProxy<T> implements Serializable {
                             true).get();
                     }
                 }
+                catch (InvocationTargetException e) {
+                    // For local services rethrow original exception.
+                    throw e.getTargetException();
+                }
                 catch (RuntimeException | Error e) {
                     throw e;
                 }
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
index 5208f7d..449de12 100644
--- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
@@ -622,6 +622,18 @@ public class PlatformDeployServiceTask extends ComputeTaskAdapter<String, Object
         }
 
         /** */
+        public void testException(String exCls) throws Exception {
+            switch (exCls) {
+                case "InterruptedException": throw new InterruptedException("Test");
+                case "IllegalArgumentException": throw new IllegalArgumentException("Test");
+                case "TestMapped1Exception": throw new TestMapped1Exception("Test");
+                case "TestMapped2Exception": throw new TestMapped2Exception("Test");
+                case "TestUnmappedException": throw new TestUnmappedException("Test");
+                default: throw new IgniteException("Unexpected exception class: " + exCls);
+            }
+        }
+
+        /** */
         public void sleep(long delayMs) {
             try {
                 U.sleep(delayMs);
@@ -631,4 +643,28 @@ public class PlatformDeployServiceTask extends ComputeTaskAdapter<String, Object
             }
         }
     }
+
+    /** */
+    public static class TestMapped1Exception extends RuntimeException {
+        /** */
+        public TestMapped1Exception(String msg) {
+            super(msg);
+        }
+    }
+
+    /** */
+    public static class TestMapped2Exception extends RuntimeException {
+        /** */
+        public TestMapped2Exception(String msg) {
+            super(msg);
+        }
+    }
+
+    /** */
+    public static class TestUnmappedException extends RuntimeException {
+        /** */
+        public TestUnmappedException(String msg) {
+            super(msg);
+        }
+    }
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs
index 6942c89..ebd727f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs
@@ -205,6 +205,9 @@ namespace Apache.Ignite.Core.Tests.Services
         void testLocalDateFromCache();
 
         /** */
+        void testException(string exceptionClass);
+
+        /** */
         void sleep(long delayMs);
     }
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs
index bab002f..d3c5b21 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs
@@ -384,6 +384,12 @@ namespace Apache.Ignite.Core.Tests.Services
         }
 
         /** <inheritDoc /> */
+        public void testException(string exceptionClass)
+        {
+            _svc.testException(exceptionClass);
+        }
+
+        /** <inheritDoc /> */
         public void sleep(long delayMs)
         {
             _svc.sleep(delayMs);
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 f5dfb17..d557bc6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -987,6 +987,42 @@ namespace Apache.Ignite.Core.Tests.Services
             Assert.AreEqual(dt1, cache.Get(3));
             Assert.AreEqual(dt2, cache.Get(4));
 
+            // Test standard java checked exception.
+            Exception ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("InterruptedException"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<ThreadInterruptedException>(ex);
+            Assert.AreEqual("Test", ex.Message);
+
+            // Test standard java unchecked exception.
+            ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("IllegalArgumentException"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<ArgumentException>(ex);
+            Assert.AreEqual("Test", ex.Message);
+
+            // Test user defined exception mapping by pattern.
+            ((IIgniteInternal)Grid1).PluginProcessor.RegisterExceptionMapping(
+                "org.apache.ignite.platform.PlatformDeployServiceTask$TestMapped*",
+                (c, m, e, i) => new TestServiceException(m, e));
+
+            ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("TestMapped1Exception"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<TestServiceException>(ex);
+            Assert.AreEqual("Test", ex.Message);
+
+            ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("TestMapped2Exception"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<TestServiceException>(ex);
+            Assert.AreEqual("Test", ex.Message);
+
+            // Test user defined unmapped exception.
+            ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("TestUnmappedException"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<IgniteException>(ex);
+            var javaEx = ex.InnerException as JavaException;
+            Assert.IsNotNull(javaEx);
+            Assert.AreEqual("Test", javaEx.JavaMessage);
+            Assert.AreEqual("org.apache.ignite.platform.PlatformDeployServiceTask$TestUnmappedException", javaEx.JavaClassName);
+
 #if NETCOREAPP
             //This Date in Europe/Moscow have offset +4.
             DateTime dt3 = new DateTime(1982, 4, 1, 1, 0, 0, 0, DateTimeKind.Local);
@@ -1106,6 +1142,42 @@ namespace Apache.Ignite.Core.Tests.Services
 
             Assert.AreEqual(dt1, cache.Get(3));
             Assert.AreEqual(dt2, cache.Get(4));
+
+            // Test standard java checked exception.
+            Exception ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("InterruptedException"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<ThreadInterruptedException>(ex);
+            Assert.AreEqual("Test", ex.Message);
+
+            // Test standard java unchecked exception.
+            ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("IllegalArgumentException"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<ArgumentException>(ex);
+            Assert.AreEqual("Test", ex.Message);
+
+            // Test user defined exception mapping by pattern.
+            ((IIgniteInternal)Grid1).PluginProcessor.RegisterExceptionMapping(
+                "org.apache.ignite.platform.PlatformDeployServiceTask$TestMapped*",
+                (c, m, e, i) => new TestServiceException(m, e));
+
+            ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("TestMapped1Exception"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<TestServiceException>(ex);
+            Assert.AreEqual("Test", ex.Message);
+
+            ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("TestMapped2Exception"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<TestServiceException>(ex);
+            Assert.AreEqual("Test", ex.Message);
+
+            // Test user defined unmapped exception.
+            ex = Assert.Throws<ServiceInvocationException>(() => svc.testException("TestUnmappedException"));
+            ex = ex.InnerException;
+            Assert.IsInstanceOf<IgniteException>(ex);
+            var javaEx = ex.InnerException as JavaException;
+            Assert.IsNotNull(javaEx);
+            Assert.AreEqual("Test", javaEx.JavaMessage);
+            Assert.AreEqual("org.apache.ignite.platform.PlatformDeployServiceTask$TestUnmappedException", javaEx.JavaClassName);
         }
 
         /// <summary>
@@ -1635,7 +1707,21 @@ namespace Apache.Ignite.Core.Tests.Services
             /** */
             public int Field { get; set; }
         }
-        
+
+        /// <summary>
+        /// Test exception.
+        /// </summary>
+        private class TestServiceException : Exception
+        {
+            /// <summary>
+            /// Constructor.
+            /// </summary>
+            public TestServiceException(string message, Exception cause) : base(message, cause)
+            {
+                // No-op.
+            }
+        }
+
 #if NETCOREAPP
         /// <summary>
         /// Adds support of the local dates to the Ignite timestamp serialization.
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Plugin/PluginProcessor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Plugin/PluginProcessor.cs
index 11100ff..b33911b 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Plugin/PluginProcessor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Plugin/PluginProcessor.cs
@@ -42,8 +42,12 @@ namespace Apache.Ignite.Core.Impl.Plugin
         private readonly Dictionary<string, IPluginProviderProxy> _pluginProvidersByName
             = new Dictionary<string, IPluginProviderProxy>();
 
-        /** Plugin exception mappings. */
-        private readonly CopyOnWriteConcurrentDictionary<string, ExceptionFactory> _exceptionMappings
+        /** Plugin exception mappings by exact class name. */
+        private readonly CopyOnWriteConcurrentDictionary<string, ExceptionFactory> _exactExceptionMappings
+            = new CopyOnWriteConcurrentDictionary<string, ExceptionFactory>();
+
+        /** Plugin exception mappings by class name pattern. */
+        private readonly CopyOnWriteConcurrentDictionary<string, ExceptionFactory> _patternExceptionMappings
             = new CopyOnWriteConcurrentDictionary<string, ExceptionFactory>();
 
         /** Plugin callbacks. */
@@ -145,18 +149,40 @@ namespace Apache.Ignite.Core.Impl.Plugin
 
             ExceptionFactory res;
 
-            return _exceptionMappings.TryGetValue(className, out res) ? res : null;
+            if (_exactExceptionMappings.TryGetValue(className, out res))
+            {
+                return res;
+            }
+
+            foreach (var mapping in _patternExceptionMappings)
+            {
+                if (className.StartsWith(mapping.Key, StringComparison.Ordinal))
+                {
+                    return mapping.Value;
+                }
+            }
+
+            return null;
         }
 
         /// <summary>
         /// Registers the exception mapping.
         /// </summary>
+        /// <param name="className">Full class name of java exception or class name pattern (if ends with '*').</param>
+        /// <param name="factory">Exception factory for matched java class name.</param>
         public void RegisterExceptionMapping(string className, ExceptionFactory factory)
         {
             Debug.Assert(className != null);
             Debug.Assert(factory != null);
 
-            _exceptionMappings.GetOrAdd(className, _ => factory);
+            if (className.EndsWith("*", StringComparison.Ordinal))
+            {
+                _patternExceptionMappings.GetOrAdd(className.Substring(0, className.Length - 1), _ => factory);
+            }
+            else
+            {
+                _exactExceptionMappings.GetOrAdd(className, _ => factory);
+            }
         }
 
         /// <summary>