IGNITE-4885 .NET: Disallow abstract and open generic types in BinaryConfiguration
This closes #1733 Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/21d34757 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/21d34757 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/21d34757 Branch: refs/heads/ignite-2893 Commit: 21d34757c7132dff125b904f2d45c07707bc940c Parents: 17d00a4 Author: Pavel Tupitsyn <ptupit...@apache.org> Authored: Thu Apr 6 09:38:28 2017 +0300 Committer: Pavel Tupitsyn <ptupit...@apache.org> Committed: Thu Apr 6 09:38:28 2017 +0300 ---------------------------------------------------------------------- .../Binary/BinaryEqualityComparerTest.cs | 7 +++- .../BinaryConfigurationTest.cs | 43 ++++++++++++++++++++ .../Apache.Ignite.Core.Tests/ExecutableTest.cs | 5 ++- .../Plugin/PluginTest.cs | 16 +++++--- .../dotnet/Apache.Ignite.Core/Ignition.cs | 6 ++- .../Impl/Binary/Marshaller.cs | 27 ++++++++++++ 6 files changed, 94 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/21d34757/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs index f0550a8..7c6d769 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryEqualityComparerTest.cs @@ -79,9 +79,10 @@ namespace Apache.Ignite.Core.Tests.Binary } })); + Assert.IsNotNull(ex.InnerException); Assert.AreEqual("Unsupported IEqualityComparer<IBinaryObject> implementation: " + "Apache.Ignite.Core.Tests.Binary.BinaryEqualityComparerTest+MyComparer. " + - "Only predefined implementations are supported.", ex.Message); + "Only predefined implementations are supported.", ex.InnerException.Message); } /// <summary> @@ -216,7 +217,9 @@ namespace Apache.Ignite.Core.Tests.Binary } })); - Assert.AreEqual("BinaryFieldEqualityComparer.FieldNames can not be null or empty.", ex.Message); + Assert.IsNotNull(ex.InnerException); + Assert.AreEqual("BinaryFieldEqualityComparer.FieldNames can not be null or empty.", + ex.InnerException.Message); } /// <summary> http://git-wip-us.apache.org/repos/asf/ignite/blob/21d34757/modules/platforms/dotnet/Apache.Ignite.Core.Tests/BinaryConfigurationTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/BinaryConfigurationTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/BinaryConfigurationTest.cs index 74ae244..4f6bf02 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/BinaryConfigurationTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/BinaryConfigurationTest.cs @@ -18,10 +18,12 @@ namespace Apache.Ignite.Core.Tests { using System; + using System.Collections; using System.Collections.Generic; using System.Linq; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Common; using NUnit.Framework; /// <summary> @@ -113,6 +115,47 @@ namespace Apache.Ignite.Core.Tests } /// <summary> + /// Tests that invalid configuration produces meaningful error message. + /// </summary> + [Test] + public void TestInvalidConfiguration() + { + // Pass open generic type. + var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + // Open generics are not allowed + BinaryConfiguration = new BinaryConfiguration(typeof(List<>)) + }; + + var ex = Assert.Throws<IgniteException>(() => Ignition.Start(cfg)); + Assert.AreEqual("Failed to start Ignite.NET, check inner exception for details", ex.Message); + Assert.IsNotNull(ex.InnerException); + Assert.IsTrue(ex.InnerException.Message.StartsWith( + "Open generic types (Type.IsGenericTypeDefinition == true) are not allowed in BinaryConfiguration: " + + "System.Collections.Generic.List`1, mscorlib")); + + // Pass open generic type name. + cfg.BinaryConfiguration = new BinaryConfiguration {Types = new[] {typeof(IList<>).AssemblyQualifiedName}}; + + ex = Assert.Throws<IgniteException>(() => Ignition.Start(cfg)); + Assert.AreEqual("Failed to start Ignite.NET, check inner exception for details", ex.Message); + Assert.IsNotNull(ex.InnerException); + Assert.IsTrue(ex.InnerException.Message.StartsWith( + "Open generic types (Type.IsGenericTypeDefinition == true) are not allowed in BinaryConfiguration: " + + "System.Collections.Generic.IList`1, mscorlib")); + + // Pass interface. + cfg.BinaryConfiguration = new BinaryConfiguration(typeof(ICollection)); + + ex = Assert.Throws<IgniteException>(() => Ignition.Start(cfg)); + Assert.AreEqual("Failed to start Ignite.NET, check inner exception for details", ex.Message); + Assert.IsNotNull(ex.InnerException); + Assert.IsTrue(ex.InnerException.Message.StartsWith( + "Abstract types and interfaces are not allowed in BinaryConfiguration: " + + "System.Collections.ICollection, mscorlib")); + } + + /// <summary> /// Checks that specified types are binarizable and can be successfully used in cache. /// </summary> private void CheckBinarizableTypes(IEnumerable<Type> testTypes) http://git-wip-us.apache.org/repos/asf/ignite/blob/21d34757/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExecutableTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExecutableTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExecutableTest.cs index 0aebd78..ae945e9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExecutableTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ExecutableTest.cs @@ -323,8 +323,9 @@ namespace Apache.Ignite.Core.Tests checkError("assembly=", "ERROR: Apache.Ignite.Core.Common.IgniteException: Missing argument value: " + "'assembly'. See 'Apache.Ignite.exe /help'"); - checkError("assembly=x.dll", "ERROR: Apache.Ignite.Core.Common.IgniteException: " + - "Failed to load assembly: x.dll"); + checkError("assembly=x.dll", "ERROR: Apache.Ignite.Core.Common.IgniteException: Failed to start " + + "Ignite.NET, check inner exception for details ---> Apache.Ignite.Core." + + "Common.IgniteException: Failed to load assembly: x.dll"); checkError("configFileName=wrong.config", "ERROR: System.Configuration.ConfigurationErrorsException: " + "Specified config file does not exist: wrong.config"); http://git-wip-us.apache.org/repos/asf/ignite/blob/21d34757/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Plugin/PluginTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Plugin/PluginTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Plugin/PluginTest.cs index 7e766a0..00b1cca 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Plugin/PluginTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Plugin/PluginTest.cs @@ -176,13 +176,15 @@ namespace Apache.Ignite.Core.Tests.Plugin // Missing attribute. var ex = Assert.Throws<IgniteException>(() => check(new[] { new NoAttributeConfig(), })); + Assert.IsNotNull(ex.InnerException); Assert.AreEqual(string.Format("{0} of type {1} has no {2}", typeof(IPluginConfiguration), - typeof(NoAttributeConfig), typeof(PluginProviderTypeAttribute)), ex.Message); + typeof(NoAttributeConfig), typeof(PluginProviderTypeAttribute)), ex.InnerException.Message); // Empty plugin name. ex = Assert.Throws<IgniteException>(() => check(new[] {new EmptyNameConfig()})); + Assert.IsNotNull(ex.InnerException); Assert.AreEqual(string.Format("{0}.Name should not be null or empty: {1}", typeof(IPluginProvider<>), - typeof(EmptyNamePluginProvider)), ex.Message); + typeof(EmptyNamePluginProvider)), ex.InnerException.Message); // Duplicate plugin name. ex = Assert.Throws<IgniteException>(() => check(new[] @@ -190,17 +192,21 @@ namespace Apache.Ignite.Core.Tests.Plugin new TestIgnitePluginConfiguration(), new TestIgnitePluginConfiguration() })); + Assert.IsNotNull(ex.InnerException); Assert.AreEqual(string.Format("Duplicate plugin name 'TestPlugin1' is used by plugin providers " + - "'{0}' and '{0}'", typeof(TestIgnitePluginProvider)), ex.Message); + "'{0}' and '{0}'", typeof(TestIgnitePluginProvider)), + ex.InnerException.Message); // Provider throws an exception. PluginLog.Clear(); - var ioex = Assert.Throws<IOException>(() => check(new IPluginConfiguration[] + ex = Assert.Throws<IgniteException>(() => check(new IPluginConfiguration[] { new NormalConfig(), new ExceptionConfig() })); - Assert.AreEqual("Failure in plugin provider", ioex.Message); + Assert.IsNotNull(ex.InnerException); + Assert.IsInstanceOf<IOException>(ex.InnerException); + Assert.AreEqual("Failure in plugin provider", ex.InnerException.Message); // Verify that plugins are started and stopped in correct order: Assert.AreEqual( http://git-wip-us.apache.org/repos/asf/ignite/blob/21d34757/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs index 13e3f61..f3b8c98 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs @@ -276,7 +276,11 @@ namespace Apache.Ignite.Core // 3. Throw error further (use startup error if exists because it is more precise). if (_startup.Error != null) - throw _startup.Error; + { + // Wrap in a new exception to preserve original stack trace. + throw new IgniteException("Failed to start Ignite.NET, check inner exception for details", + _startup.Error); + } throw; } http://git-wip-us.apache.org/repos/asf/ignite/blob/21d34757/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs index 5effc5c..5a4460a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -557,12 +557,16 @@ namespace Apache.Ignite.Core.Impl.Binary if (type != null) { + ValidateUserType(type); + if (typeCfg.IsEnum != type.IsEnum) + { throw new BinaryObjectException( string.Format( "Invalid IsEnum flag in binary type configuration. " + "Configuration value: IsEnum={0}, actual type: IsEnum={1}", typeCfg.IsEnum, type.IsEnum)); + } // Type is found. var typeName = BinaryUtils.GetTypeName(type); @@ -740,5 +744,28 @@ namespace Apache.Ignite.Core.Impl.Binary "DateTime fields would not work in SQL; " + "sbyte, ushort, uint, ulong fields would not work in DML.", type, typeof(ISerializable)); } + + /// <summary> + /// Validates binary type. + /// </summary> + // ReSharper disable once UnusedParameter.Local + private static void ValidateUserType(Type type) + { + Debug.Assert(type != null); + + if (type.IsGenericTypeDefinition) + { + throw new BinaryObjectException( + "Open generic types (Type.IsGenericTypeDefinition == true) are not allowed " + + "in BinaryConfiguration: " + type.AssemblyQualifiedName); + } + + if (type.IsAbstract) + { + throw new BinaryObjectException( + "Abstract types and interfaces are not allowed in BinaryConfiguration: " + + type.AssemblyQualifiedName); + } + } } }