IGNITE-2380: Added ability to start Ignite using configuration from app.config. This closes #417.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/a4d8a049 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/a4d8a049 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/a4d8a049 Branch: refs/heads/ignite-1786 Commit: a4d8a049138a4d4bc86a59ccb273f7e214baf694 Parents: f7c1296 Author: Pavel Tupitsyn <[email protected]> Authored: Tue Feb 9 14:54:20 2016 +0300 Committer: vozerov-gridgain <[email protected]> Committed: Tue Feb 9 14:54:20 2016 +0300 ---------------------------------------------------------------------- .../Apache.Ignite.Core.Tests.csproj | 4 + .../Apache.Ignite.Core.Tests/FutureTest.cs | 1 + .../IgniteConfigurationSectionTest.cs | 69 +++ .../IgniteConfigurationSerializerTest.cs | 554 +++++++++++++++++++ .../dotnet/Apache.Ignite.Core.Tests/app.config | 54 ++ .../Apache.Ignite.Core.csproj | 11 + .../Binary/IBinarySerializer.cs | 6 +- .../IgniteConfigurationSection.cs | 80 +++ .../IgniteConfigurationSection.xsd | 281 ++++++++++ .../dotnet/Apache.Ignite.Core/Ignition.cs | 39 ++ .../Impl/Common/BooleanLowerCaseConverter.cs | 60 ++ .../Common/IgniteConfigurationXmlSerializer.cs | 410 ++++++++++++++ .../Impl/Common/TypeStringConverter.cs | 115 ++++ .../Impl/Events/EventTypeConverter.cs | 133 +++++ 14 files changed, 1814 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj index f5e98c5..fb14ed5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj @@ -55,6 +55,7 @@ <HintPath>..\libs\nunit.framework.dll</HintPath> </Reference> <Reference Include="System" /> + <Reference Include="System.configuration" /> <Reference Include="System.Core" /> <Reference Include="System.Runtime.Serialization" /> <Reference Include="System.XML" /> @@ -117,6 +118,8 @@ <Compile Include="ExceptionsTest.cs" /> <Compile Include="ExecutableTest.cs" /> <Compile Include="FutureTest.cs" /> + <Compile Include="IgniteConfigurationSectionTest.cs" /> + <Compile Include="IgniteConfigurationSerializerTest.cs" /> <Compile Include="IgniteConfigurationTest.cs" /> <Compile Include="IgniteTestBase.cs" /> <Compile Include="LifecycleTest.cs" /> @@ -251,6 +254,7 @@ <ItemGroup> <None Include="Apache.Ignite.Core.Tests.nunit" /> <None Include="Apache.Ignite.Core.Tests.snk" /> + <None Include="app.config" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <PropertyGroup> http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core.Tests/FutureTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/FutureTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/FutureTest.cs index f18be8c..bc1f08f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/FutureTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/FutureTest.cs @@ -67,6 +67,7 @@ namespace Apache.Ignite.Core.Tests [TestFixtureTearDown] public void TestFixtureTearDown() { + Ignition.StopAll(true); TestUtils.KillProcesses(); } http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs new file mode 100644 index 0000000..29aea90 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSectionTest.cs @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Tests +{ + using System; + using System.Configuration; + using System.Linq; + using Apache.Ignite.Core.Impl.Common; + using NUnit.Framework; + + /// <summary> + /// Tests <see cref="IgniteConfigurationSection"/>. + /// </summary> + public class IgniteConfigurationSectionTest + { + /// <summary> + /// Tests the read. + /// </summary> + [Test] + public void TestRead() + { + var section = (IgniteConfigurationSection) ConfigurationManager.GetSection("igniteConfiguration"); + + Assert.AreEqual("myGrid1", section.IgniteConfiguration.GridName); + Assert.AreEqual("cacheName", section.IgniteConfiguration.CacheConfiguration.Single().Name); + } + + /// <summary> + /// Tests the ignite start. + /// </summary> + [Test] + public void TestIgniteStart() + { + Environment.SetEnvironmentVariable(Classpath.EnvIgniteNativeTestClasspath, "true"); + + using (var ignite = Ignition.StartFromApplicationConfiguration("igniteConfiguration")) + { + Assert.AreEqual("myGrid1", ignite.Name); + Assert.IsNotNull(ignite.GetCache<int, int>("cacheName")); + } + + using (var ignite = Ignition.StartFromApplicationConfiguration("igniteConfiguration2")) + { + Assert.AreEqual("myGrid2", ignite.Name); + Assert.IsNotNull(ignite.GetCache<int, int>("cacheName2")); + } + + using (var ignite = Ignition.StartFromApplicationConfiguration()) + { + Assert.IsTrue(ignite.Name.StartsWith("myGrid")); + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs new file mode 100644 index 0000000..d8c52ee --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs @@ -0,0 +1,554 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable MemberCanBePrivate.Global +namespace Apache.Ignite.Core.Tests +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading; + using System.Xml; + using System.Xml.Schema; + using Apache.Ignite.Core.Binary; + using Apache.Ignite.Core.Cache.Configuration; + using Apache.Ignite.Core.Cache.Store; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Discovery.Tcp; + using Apache.Ignite.Core.Discovery.Tcp.Multicast; + using Apache.Ignite.Core.Events; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Lifecycle; + using Apache.Ignite.Core.Tests.Binary; + using NUnit.Framework; + + /// <summary> + /// Tests <see cref="IgniteConfiguration"/> serialization. + /// </summary> + public class IgniteConfigurationSerializerTest + { + /// <summary> + /// Tests the predefined XML. + /// </summary> + [Test] + public void TestPredefinedXml() + { + var xml = @"<igniteConfig workDirectory='c:' JvmMaxMemoryMb='1024' MetricsLogFrequency='0:0:10'> + <localhost>127.1.1.1</localhost> + <binaryConfiguration> + <defaultNameMapper type='Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+NameMapper, Apache.Ignite.Core.Tests' bar='testBar' /> + <types> + <string>Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+FooClass, Apache.Ignite.Core.Tests</string> + </types> + </binaryConfiguration> + <discoverySpi type='TcpDiscoverySpi' joinTimeout='0:1:0'> + <ipFinder type='TcpDiscoveryMulticastIpFinder' addressRequestAttempts='7' /> + </discoverySpi> + <jvmOptions><string>-Xms1g</string><string>-Xmx4g</string></jvmOptions> + <lifecycleBeans> + <iLifecycleBean type='Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+LifecycleBean, Apache.Ignite.Core.Tests' foo='15' /> + </lifecycleBeans> + <cacheConfiguration> + <cacheConfiguration cacheMode='Replicated'> + <queryEntities> + <queryEntity keyType='System.Int32' valueType='System.String'> + <fields> + <queryField name='length' fieldType='System.Int32' /> + </fields> + <aliases> + <queryAlias fullName='somefield.field' alias='shortField' /> + </aliases> + <indexes> + <queryIndex name='idx' indexType='Geospatial'> + <fields> + <queryIndexField name='indexFld' isDescending='true' /> + </fields> + </queryIndex> + </indexes> + </queryEntity> + </queryEntities> + </cacheConfiguration> + <cacheConfiguration name='secondCache' /> + </cacheConfiguration> + <includedEventTypes> + <int>42</int> + <int>TaskFailed</int> + <int>JobFinished</int> + </includedEventTypes> + </igniteConfig>"; + var reader = XmlReader.Create(new StringReader(xml)); + + var cfg = IgniteConfigurationXmlSerializer.Deserialize(reader); + + Assert.AreEqual("c:", cfg.WorkDirectory); + Assert.AreEqual("127.1.1.1", cfg.Localhost); + Assert.AreEqual(1024, cfg.JvmMaxMemoryMb); + Assert.AreEqual(TimeSpan.FromSeconds(10), cfg.MetricsLogFrequency); + Assert.AreEqual(TimeSpan.FromMinutes(1), ((TcpDiscoverySpi)cfg.DiscoverySpi).JoinTimeout); + Assert.AreEqual(7, + ((TcpDiscoveryMulticastIpFinder) ((TcpDiscoverySpi) cfg.DiscoverySpi).IpFinder).AddressRequestAttempts); + Assert.AreEqual(new[] { "-Xms1g", "-Xmx4g" }, cfg.JvmOptions); + Assert.AreEqual(15, ((LifecycleBean) cfg.LifecycleBeans.Single()).Foo); + Assert.AreEqual("testBar", ((NameMapper) cfg.BinaryConfiguration.DefaultNameMapper).Bar); + Assert.AreEqual( + "Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+FooClass, Apache.Ignite.Core.Tests", + cfg.BinaryConfiguration.Types.Single()); + Assert.AreEqual(new[] {42, EventType.TaskFailed, EventType.JobFinished}, cfg.IncludedEventTypes); + + Assert.AreEqual("secondCache", cfg.CacheConfiguration.Last().Name); + + var cacheCfg = cfg.CacheConfiguration.First(); + + Assert.AreEqual(CacheMode.Replicated, cacheCfg.CacheMode); + + var queryEntity = cacheCfg.QueryEntities.Single(); + Assert.AreEqual(typeof(int), queryEntity.KeyType); + Assert.AreEqual(typeof(string), queryEntity.ValueType); + Assert.AreEqual("length", queryEntity.Fields.Single().Name); + Assert.AreEqual(typeof(int), queryEntity.Fields.Single().FieldType); + Assert.AreEqual("somefield.field", queryEntity.Aliases.Single().FullName); + Assert.AreEqual("shortField", queryEntity.Aliases.Single().Alias); + Assert.AreEqual(QueryIndexType.Geospatial, queryEntity.Indexes.Single().IndexType); + Assert.AreEqual("indexFld", queryEntity.Indexes.Single().Fields.Single().Name); + Assert.AreEqual(true, queryEntity.Indexes.Single().Fields.Single().IsDescending); + } + + /// <summary> + /// Tests the serialize deserialize. + /// </summary> + [Test] + public void TestSerializeDeserialize() + { + // Test custom + CheckSerializeDeserialize(GetTestConfig()); + + // Test custom with different culture to make sure numbers are serialized properly + RunWithCustomCulture(() => CheckSerializeDeserialize(GetTestConfig())); + + // Test default + CheckSerializeDeserialize(new IgniteConfiguration()); + } + + /// <summary> + /// Tests the schema validation. + /// </summary> + [Test] + public void TestSchemaValidation() + { + CheckSchemaValidation(); + + RunWithCustomCulture(CheckSchemaValidation); + + // Check invalid xml + const string invalidXml = + @"<igniteConfiguration xmlns='http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection'> + <binaryConfiguration /><binaryConfiguration /> + </igniteConfiguration>"; + + Assert.Throws<XmlSchemaValidationException>(() => CheckSchemaValidation(invalidXml)); + } + + /// <summary> + /// Checks the schema validation. + /// </summary> + private static void CheckSchemaValidation() + { + var sb = new StringBuilder(); + + using (var xmlWriter = XmlWriter.Create(sb)) + { + IgniteConfigurationXmlSerializer.Serialize(GetTestConfig(), xmlWriter, "igniteConfiguration"); + } + + CheckSchemaValidation(sb.ToString()); + } + + /// <summary> + /// Checks the schema validation. + /// </summary> + /// <param name="xml">The XML.</param> + private static void CheckSchemaValidation(string xml) + { + var document = new XmlDocument(); + + document.Schemas.Add("http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection", + XmlReader.Create("IgniteConfigurationSection.xsd")); + + document.Load(new StringReader(xml)); + + document.Validate(null); + } + + /// <summary> + /// Checks the serialize deserialize. + /// </summary> + /// <param name="cfg">The config.</param> + private static void CheckSerializeDeserialize(IgniteConfiguration cfg) + { + var resCfg = SerializeDeserialize(cfg); + + AssertReflectionEqual(cfg, resCfg); + } + + /// <summary> + /// Serializes and deserializes a config. + /// </summary> + private static IgniteConfiguration SerializeDeserialize(IgniteConfiguration cfg) + { + var sb = new StringBuilder(); + + using (var xmlWriter = XmlWriter.Create(sb)) + { + IgniteConfigurationXmlSerializer.Serialize(cfg, xmlWriter, "igniteConfig"); + } + + var xml = sb.ToString(); + + using (var xmlReader = XmlReader.Create(new StringReader(xml))) + { + xmlReader.MoveToContent(); + return IgniteConfigurationXmlSerializer.Deserialize(xmlReader); + } + } + + /// <summary> + /// Asserts equality with reflection. + /// </summary> + private static void AssertReflectionEqual(object x, object y) + { + var type = x.GetType(); + + Assert.AreEqual(type, y.GetType()); + + if (type.IsValueType || type == typeof (string) || type.IsSubclassOf(typeof (Type))) + { + Assert.AreEqual(x, y); + return; + } + + var props = type.GetProperties(); + + foreach (var propInfo in props) + { + var propType = propInfo.PropertyType; + + var xVal = propInfo.GetValue(x, null); + var yVal = propInfo.GetValue(y, null); + + if (xVal == null || yVal == null) + { + Assert.IsNull(xVal); + Assert.IsNull(yVal); + } + else if (propType != typeof(string) && propType.IsGenericType + && propType.GetGenericTypeDefinition() == typeof (ICollection<>)) + { + var xCol = ((IEnumerable) xVal).OfType<object>().ToList(); + var yCol = ((IEnumerable) yVal).OfType<object>().ToList(); + + Assert.AreEqual(xCol.Count, yCol.Count); + + for (int i = 0; i < xCol.Count; i++) + AssertReflectionEqual(xCol[i], yCol[i]); + } + else + { + AssertReflectionEqual(xVal, yVal); + } + } + } + + /// <summary> + /// Gets the test configuration. + /// </summary> + private static IgniteConfiguration GetTestConfig() + { + return new IgniteConfiguration + { + GridName = "gridName", + JvmOptions = new[] {"1", "2"}, + Localhost = "localhost11", + JvmClasspath = "classpath", + Assemblies = new[] {"asm1", "asm2", "asm3"}, + BinaryConfiguration = new BinaryConfiguration + { + TypeConfigurations = new[] + { + new BinaryTypeConfiguration + { + IsEnum = true, + KeepDeserialized = true, + AffinityKeyFieldName = "affKeyFieldName", + TypeName = "typeName", + IdMapper = new IdMapper(), + NameMapper = new NameMapper(), + Serializer = new TestSerializer() + } + }, + DefaultIdMapper = new IdMapper(), + DefaultKeepDeserialized = true, + DefaultNameMapper = new NameMapper(), + DefaultSerializer = new TestSerializer() + }, + CacheConfiguration = new[] + { + new CacheConfiguration("cacheName") + { + AtomicWriteOrderMode = CacheAtomicWriteOrderMode.Primary, + AtomicityMode = CacheAtomicityMode.Transactional, + Backups = 15, + CacheMode = CacheMode.Partitioned, + CacheStoreFactory = new TestCacheStoreFactory(), + CopyOnRead = true, + EagerTtl = true, + EnableSwap = true, + EvictSynchronized = true, + EvictSynchronizedConcurrencyLevel = 13, + EvictSynchronizedKeyBufferSize = 14, + EvictSynchronizedTimeout = TimeSpan.FromMinutes(3), + Invalidate = true, + KeepBinaryInStore = true, + LoadPreviousValue = true, + LockTimeout = TimeSpan.FromSeconds(56), + LongQueryWarningTimeout = TimeSpan.FromSeconds(99), + MaxConcurrentAsyncOperations = 24, + MaxEvictionOverflowRatio = 5.6F, + MemoryMode = CacheMemoryMode.OffheapValues, + OffHeapMaxMemory = 567, + QueryEntities = new[] + { + new QueryEntity + { + Fields = new[] + { + new QueryField("field", typeof (int)) + }, + Indexes = new[] + { + new QueryIndex("field") { IndexType = QueryIndexType.FullText } + }, + Aliases = new[] + { + new QueryAlias("field.field", "fld") + }, + KeyType = typeof (string), + ValueType = typeof (long) + }, + }, + ReadFromBackup = true, + RebalanceBatchSize = 33, + RebalanceDelay = TimeSpan.MaxValue, + RebalanceMode = CacheRebalanceMode.Sync, + RebalanceThrottle = TimeSpan.FromHours(44), + RebalanceTimeout = TimeSpan.FromMinutes(8), + SqlEscapeAll = true, + SqlOnheapRowCacheSize = 679, + StartSize = 1023, + WriteBehindBatchSize = 45, + WriteBehindEnabled = true, + WriteBehindFlushFrequency = TimeSpan.FromSeconds(5), + WriteBehindFlushSize = 66, + WriteBehindFlushThreadCount = 2, + WriteSynchronizationMode = CacheWriteSynchronizationMode.FullAsync + } + }, + ClientMode = true, + DiscoverySpi = new TcpDiscoverySpi + { + NetworkTimeout = TimeSpan.FromSeconds(1), + SocketTimeout = TimeSpan.FromSeconds(2), + AckTimeout = TimeSpan.FromSeconds(3), + JoinTimeout = TimeSpan.FromSeconds(4), + MaxAckTimeout = TimeSpan.FromSeconds(5), + IpFinder = new TcpDiscoveryMulticastIpFinder + { + TimeToLive = 110, + MulticastGroup = "multicastGroup", + AddressRequestAttempts = 10, + MulticastPort = 987, + ResponseTimeout = TimeSpan.FromDays(1), + LocalAddress = "127.0.0.2", + Endpoints = new[] {"", "abc"} + } + }, + IgniteHome = "igniteHome", + IncludedEventTypes = EventType.CacheQueryAll, + JvmDllPath = @"c:\jvm", + JvmInitialMemoryMb = 1024, + JvmMaxMemoryMb = 2048, + LifecycleBeans = new[] {new LifecycleBean(), new LifecycleBean() }, + MetricsExpireTime = TimeSpan.FromSeconds(15), + MetricsHistorySize = 45, + MetricsLogFrequency = TimeSpan.FromDays(2), + MetricsUpdateFrequency = TimeSpan.MinValue, + NetworkSendRetryCount = 7, + NetworkSendRetryDelay = TimeSpan.FromSeconds(98), + NetworkTimeout = TimeSpan.FromMinutes(4), + SuppressWarnings = true, + WorkDirectory = @"c:\work" + }; + } + + /// <summary> + /// Runs the with custom culture. + /// </summary> + /// <param name="action">The action.</param> + private static void RunWithCustomCulture(Action action) + { + RunWithCulture(action, CultureInfo.InvariantCulture); + RunWithCulture(action, CultureInfo.GetCultureInfo("ru-RU")); + } + + /// <summary> + /// Runs the with culture. + /// </summary> + /// <param name="action">The action.</param> + /// <param name="cultureInfo">The culture information.</param> + private static void RunWithCulture(Action action, CultureInfo cultureInfo) + { + var oldCulture = Thread.CurrentThread.CurrentCulture; + + try + { + Thread.CurrentThread.CurrentCulture = cultureInfo; + + action(); + } + finally + { + Thread.CurrentThread.CurrentCulture = oldCulture; + } + } + + /// <summary> + /// Test bean. + /// </summary> + public class LifecycleBean : ILifecycleBean + { + /// <summary> + /// Gets or sets the foo. + /// </summary> + /// <value> + /// The foo. + /// </value> + public int Foo { get; set; } + + /// <summary> + /// This method is called when lifecycle event occurs. + /// </summary> + /// <param name="evt">Lifecycle event.</param> + public void OnLifecycleEvent(LifecycleEventType evt) + { + // No-op. + } + } + + /// <summary> + /// Test mapper. + /// </summary> + public class NameMapper : IBinaryNameMapper + { + /// <summary> + /// Gets or sets the bar. + /// </summary> + /// <value> + /// The bar. + /// </value> + public string Bar { get; set; } + + /// <summary> + /// Gets the type name. + /// </summary> + /// <param name="name">The name.</param> + /// <returns> + /// Type name. + /// </returns> + public string GetTypeName(string name) + { + return name; + } + + /// <summary> + /// Gets the field name. + /// </summary> + /// <param name="name">The name.</param> + /// <returns> + /// Field name. + /// </returns> + public string GetFieldName(string name) + { + return name; + } + } + + /// <summary> + /// Serializer. + /// </summary> + public class TestSerializer : IBinarySerializer + { + /// <summary> + /// Write portalbe object. + /// </summary> + /// <param name="obj">Object.</param> + /// <param name="writer">Poratble writer.</param> + public void WriteBinary(object obj, IBinaryWriter writer) + { + // No-op. + } + + /// <summary> + /// Read binary object. + /// </summary> + /// <param name="obj">Instantiated empty object.</param> + /// <param name="reader">Poratble reader.</param> + public void ReadBinary(object obj, IBinaryReader reader) + { + // No-op. + } + } + + /// <summary> + /// Test class. + /// </summary> + public class FooClass + { + // No-op. + } + + /// <summary> + /// Test factory. + /// </summary> + public class TestCacheStoreFactory : IFactory<ICacheStore> + { + /// <summary> + /// Creates an instance of the cache store. + /// </summary> + /// <returns> + /// New instance of the cache store. + /// </returns> + public ICacheStore CreateInstance() + { + return null; + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core.Tests/app.config ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/app.config b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/app.config new file mode 100644 index 0000000..c290c83 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/app.config @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<configuration> + <configSections> + <section name="igniteConfiguration" type="Apache.Ignite.Core.IgniteConfigurationSection, Apache.Ignite.Core" /> + <section name="igniteConfiguration2" type="Apache.Ignite.Core.IgniteConfigurationSection, Apache.Ignite.Core" /> + </configSections> + + <igniteConfiguration xmlns="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection" gridName="myGrid1"> + <discoverySpi type="TcpDiscoverySpi"> + <ipFinder type="TcpDiscoveryStaticIpFinder"> + <endpoints> + <string>127.0.0.1:47500..47501</string> + </endpoints> + </ipFinder> + </discoverySpi> + + <cacheConfiguration> + <cacheConfiguration name="cacheName" /> + </cacheConfiguration> + </igniteConfiguration> + + <igniteConfiguration2 gridName="myGrid2"> + <discoverySpi type="TcpDiscoverySpi"> + <ipFinder type="TcpDiscoveryStaticIpFinder"> + <endpoints> + <string>127.0.0.1:47500..47501</string> + </endpoints> + </ipFinder> + </discoverySpi> + + <cacheConfiguration> + <cacheConfiguration name="cacheName2" /> + </cacheConfiguration> + </igniteConfiguration2> + +</configuration> http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj index e2efd0a..d0ef352 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -60,7 +60,9 @@ </PropertyGroup> <ItemGroup> <Reference Include="System" /> + <Reference Include="System.configuration" /> <Reference Include="System.Core" /> + <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> <Compile Include="Binary\Package-Info.cs" /> @@ -183,6 +185,7 @@ <Compile Include="Events\TaskEvent.cs" /> <Compile Include="GlobalSuppressions.cs" /> <Compile Include="IgniteConfiguration.cs" /> + <Compile Include="IgniteConfigurationSection.cs" /> <Compile Include="Ignition.cs" /> <Compile Include="IIgnite.cs" /> <Compile Include="Impl\Binary\BinaryEnum.cs" /> @@ -222,11 +225,13 @@ <Compile Include="Impl\Collections\MultiValueDictionary.cs" /> <Compile Include="Impl\Collections\ReadOnlyCollection.cs" /> <Compile Include="Impl\Collections\ReadOnlyDictionary.cs" /> + <Compile Include="Impl\Common\BooleanLowerCaseConverter.cs" /> <Compile Include="Impl\Common\CancelledTask.cs" /> <Compile Include="Impl\Common\Classpath.cs" /> <Compile Include="Impl\Common\CopyOnWriteConcurrentDictionary.cs" /> <Compile Include="Impl\Common\DelegateConverter.cs" /> <Compile Include="Impl\Common\DelegateTypeDescriptor.cs" /> + <Compile Include="Impl\Events\EventTypeConverter.cs" /> <Compile Include="Impl\Common\Fnv1Hash.cs" /> <Compile Include="Impl\Common\Future.cs" /> <Compile Include="Impl\Common\FutureConverter.cs" /> @@ -234,10 +239,12 @@ <Compile Include="Impl\Common\IgniteArgumentCheck.cs" /> <Compile Include="Impl\Common\IFutureConverter.cs" /> <Compile Include="Impl\Common\IFutureInternal.cs" /> + <Compile Include="Impl\Common\IgniteConfigurationXmlSerializer.cs" /> <Compile Include="Impl\Common\IgniteHome.cs" /> <Compile Include="Impl\Common\LoadedAssembliesResolver.cs" /> <Compile Include="Impl\Common\ResizeableArray.cs" /> <Compile Include="Impl\Common\TypeCaster.cs" /> + <Compile Include="Impl\Common\TypeStringConverter.cs" /> <Compile Include="Impl\Compute\Closure\ComputeAbstractClosureTask.cs" /> <Compile Include="Impl\Compute\Closure\ComputeActionJob.cs" /> <Compile Include="Impl\Compute\Closure\ComputeFuncJob.cs" /> @@ -434,6 +441,10 @@ <ItemGroup> <None Include="Apache.Ignite.Core.ruleset" /> <None Include="Apache.Ignite.Core.snk" /> + <None Include="IgniteConfigurationSection.xsd"> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> </ItemGroup> <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinarySerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinarySerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinarySerializer.cs index 23dc811..3f924eb 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinarySerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/IBinarySerializer.cs @@ -23,17 +23,17 @@ namespace Apache.Ignite.Core.Binary public interface IBinarySerializer { /// <summary> - /// Write portalbe object. + /// Write binary object. /// </summary> /// <param name="obj">Object.</param> - /// <param name="writer">Poratble writer.</param> + /// <param name="writer">Binary writer.</param> void WriteBinary(object obj, IBinaryWriter writer); /// <summary> /// Read binary object. /// </summary> /// <param name="obj">Instantiated empty object.</param> - /// <param name="reader">Poratble reader.</param> + /// <param name="reader">Binary reader.</param> void ReadBinary(object obj, IBinaryReader reader); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.cs b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.cs new file mode 100644 index 0000000..51b963e --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.cs @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core +{ + using System.Configuration; + using System.Text; + using System.Xml; + using Apache.Ignite.Core.Impl.Common; + + /// <summary> + /// Ignite configuration section for app.config and web.config files. + /// </summary> + public class IgniteConfigurationSection : ConfigurationSection + { + /// <summary> + /// Gets or sets the ignite configuration. + /// </summary> + /// <value> + /// The ignite configuration. + /// </value> + public IgniteConfiguration IgniteConfiguration { get; private set; } + + /// <summary> + /// Reads XML from the configuration file. + /// </summary> + /// <param name="reader">The reader object, which reads from the configuration file.</param> + protected override void DeserializeSection(XmlReader reader) + { + IgniteArgumentCheck.NotNull(reader, "reader"); + + IgniteConfiguration = IgniteConfigurationXmlSerializer.Deserialize(reader); + } + + /// <summary> + /// Creates an XML string containing an unmerged view of the <see cref="ConfigurationSection" /> + /// object as a single section to write to a file. + /// </summary> + /// <param name="parentElement">The <see cref="ConfigurationElement" /> + /// instance to use as the parent when performing the un-merge.</param> + /// <param name="name">The name of the section to create.</param> + /// <param name="saveMode">The <see cref="ConfigurationSaveMode" /> instance + /// to use when writing to a string.</param> + /// <returns> + /// An XML string containing an unmerged view of the <see cref="ConfigurationSection" /> object. + /// </returns> + protected override string SerializeSection(ConfigurationElement parentElement, string name, + ConfigurationSaveMode saveMode) + { + IgniteArgumentCheck.NotNull(parentElement, "parentElement"); + IgniteArgumentCheck.NotNullOrEmpty(name, "name"); + + if (IgniteConfiguration == null) + return string.Format("<{0} />", name); + + var sb = new StringBuilder(); + + using (var xmlWriter = XmlWriter.Create(sb)) + { + IgniteConfigurationXmlSerializer.Serialize(IgniteConfiguration, xmlWriter, name); + + return sb.ToString(); + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd new file mode 100644 index 0000000..5181217 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd @@ -0,0 +1,281 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<xs:schema id="IgniteConfigurationSection" + targetNamespace="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection" + elementFormDefault="qualified" + xmlns="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection" + xmlns:mstns="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:element name="igniteConfiguration"> + <xs:complexType> + <xs:all> + <xs:element name="binaryConfiguration" minOccurs="0"> + <xs:complexType> + <xs:all> + <xs:element name="typeConfigurations" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="binaryTypeConfiguration"> + <xs:complexType> + <xs:all> + <xs:element name="nameMapper" minOccurs="0"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="idMapper" minOccurs="0"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="serializer" minOccurs="0"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="typeName" type="xs:string" /> + <xs:attribute name="affinityKeyFieldName" type="xs:string" /> + <xs:attribute name="keepDeserialized" type="xs:string" /> + <xs:attribute name="isEnum" type="xs:boolean" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="defaultNameMapper" minOccurs="0"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="defaultIdMapper" minOccurs="0"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="defaultSerializer" minOccurs="0"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="defaultKeepDeserialized" type="xs:boolean" /> + </xs:complexType> + </xs:element> + <xs:element name="cacheConfiguration" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="cacheConfiguration" maxOccurs="unbounded"> + <xs:complexType> + <xs:all> + <xs:element name="cacheStoreFactory" minOccurs="0"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="queryEntities" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="queryEntity" maxOccurs="unbounded"> + <xs:complexType> + <xs:all> + <xs:element name="fields" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="queryField" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="fieldType" type="xs:string" /> + <xs:attribute name="fieldTypeName" type="xs:string" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="aliases" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="queryAlias" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="fullName" type="xs:string" use="required" /> + <xs:attribute name="alias" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="indexes" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="queryIndex" maxOccurs="unbounded"> + <xs:complexType> + <xs:sequence> + <xs:element name="fields" minOccurs="1"> + <xs:complexType> + <xs:sequence> + <xs:element name="queryIndexField" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="isDescending" type="xs:boolean" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" type="xs:string" /> + <xs:attribute name="indexType" type="xs:string" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="keyTypeName" type="xs:string"/> + <xs:attribute name="keyType" type="xs:string" /> + <xs:attribute name="valueTypeName" type="xs:string" /> + <xs:attribute name="valueType" type="xs:string" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="name" type="xs:string" /> + <xs:attribute name="writeSynchronizationMode" type="xs:string" /> + <xs:attribute name="evictSynchronized" type="xs:boolean" /> + <xs:attribute name="evictSynchronizedKeyBufferSize" type="xs:int" /> + <xs:attribute name="evictSynchronizedConcurrencyLevel" type="xs:int" /> + <xs:attribute name="evictSynchronizedTimeout" type="xs:string" /> + <xs:attribute name="maxEvictionOverflowRatio" type="xs:decimal" /> + <xs:attribute name="startSize" type="xs:int" /> + <xs:attribute name="loadPreviousValue" type="xs:string" /> + <xs:attribute name="atomicityMode" type="xs:string" /> + <xs:attribute name="atomicWriteOrderMode" type="xs:string" /> + <xs:attribute name="backups" type="xs:int" /> + <xs:attribute name="lockTimeout" type="xs:string" /> + <xs:attribute name="invalidate" type="xs:boolean" /> + <xs:attribute name="rebalanceMode" type="xs:string" /> + <xs:attribute name="rebalanceBatchSize" type="xs:int" /> + <xs:attribute name="enableSwap" type="xs:boolean" /> + <xs:attribute name="maxConcurrentAsyncOperations" type="xs:int" /> + <xs:attribute name="writeBehindEnabled" type="xs:boolean" /> + <xs:attribute name="writeBehindFlushSize" type="xs:int" /> + <xs:attribute name="writeBehindFlushThreadCount" type="xs:int" /> + <xs:attribute name="writeBehindBatchSize" type="xs:int" /> + <xs:attribute name="rebalanceTimeout" type="xs:string" /> + <xs:attribute name="rebalanceDelay" type="xs:string" /> + <xs:attribute name="rebalanceThrottle" type="xs:string" /> + <xs:attribute name="offHeapMaxMemory" type="xs:int" /> + <xs:attribute name="memoryMode" type="xs:string" /> + <xs:attribute name="longQueryWarningTimeout" type="xs:string" /> + <xs:attribute name="sqlEscapeAll" type="xs:boolean" /> + <xs:attribute name="sqlOnheapRowCacheSize" type="xs:int" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="jvmOptions" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element maxOccurs="unbounded" name="string" type="xs:string" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="assemblies" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element maxOccurs="unbounded" name="string" type="xs:string" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="lifecycleBeans" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element maxOccurs="unbounded" name="iLifecycleBean"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="discoverySpi" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="ipFinder" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="endpoints" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element maxOccurs="unbounded" name="string" type="xs:string" /> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="type" type="xs:string" use="required" /> + <xs:attribute name="localAddress" type="xs:string" /> + <xs:attribute name="multicastGroup" type="xs:string" /> + <xs:attribute name="multicastPort" type="xs:int" /> + <xs:attribute name="addressRequestAttempts" type="xs:int" /> + <xs:attribute name="responseTimeout" type="xs:string" /> + <xs:attribute name="timeToLive" type="xs:byte" /> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="socketTimeout" type="xs:string" /> + <xs:attribute name="ackTimeout" type="xs:string" /> + <xs:attribute name="maxAckTimeout" type="xs:string" /> + <xs:attribute name="networkTimeout" type="xs:string" /> + <xs:attribute name="joinTimeout" type="xs:string" /> + <xs:attribute name="type" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="includedEventTypes" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element maxOccurs="unbounded" name="int" type="xs:string" /> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:all> + <xs:attribute name="gridName" type="xs:string" /> + <xs:attribute name="jvmDllPath" type="xs:string" /> + <xs:attribute name="igniteHome" type="xs:string" /> + <xs:attribute name="jvmClasspath" type="xs:string" /> + <xs:attribute name="suppressWarnings" type="xs:boolean" /> + <xs:attribute name="jvmInitialMemoryMb" type="xs:int" /> + <xs:attribute name="jvmMaxMemoryMb" type="xs:int" /> + <xs:attribute name="clientMode" type="xs:boolean" /> + <xs:attribute name="metricsExpireTime" type="xs:string" /> + <xs:attribute name="metricsHistorySize" type="xs:int" /> + <xs:attribute name="metricsLogFrequency" type="xs:string" /> + <xs:attribute name="metricsUpdateFrequency" type="xs:string" /> + <xs:attribute name="networkSendRetryCount" type="xs:int" /> + <xs:attribute name="networkSendRetryDelay" type="xs:string" /> + <xs:attribute name="networkTimeout" type="xs:string" /> + <xs:attribute name="workDirectory" type="xs:string" /> + <xs:attribute name="localhost" type="xs:string" /> + </xs:complexType> + </xs:element> +</xs:schema> http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/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 0549010..70d7422 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs @@ -20,6 +20,7 @@ namespace Apache.Ignite.Core { using System; using System.Collections.Generic; + using System.Configuration; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -116,6 +117,44 @@ namespace Apache.Ignite.Core } /// <summary> + /// Reads <see cref="IgniteConfiguration"/> from first <see cref="IgniteConfigurationSection"/> in the + /// application configuration and starts Ignite. + /// </summary> + /// <returns>Started Ignite.</returns> + public static IIgnite StartFromApplicationConfiguration() + { + var cfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + + var section = cfg.Sections.OfType<IgniteConfigurationSection>().FirstOrDefault(); + + if (section == null) + throw new ConfigurationErrorsException( + string.Format("Could not find {0} in current application configuration", + typeof(IgniteConfigurationSection).Name)); + + return Start(section.IgniteConfiguration); + } + + /// <summary> + /// Reads <see cref="IgniteConfiguration"/> from application configuration + /// <see cref="IgniteConfigurationSection"/> with specified name and starts Ignite. + /// </summary> + /// <param name="sectionName">Name of the section.</param> + /// <returns>Started Ignite.</returns> + public static IIgnite StartFromApplicationConfiguration(string sectionName) + { + IgniteArgumentCheck.NotNullOrEmpty(sectionName, "sectionName"); + + var section = ConfigurationManager.GetSection(sectionName) as IgniteConfigurationSection; + + if (section == null) + throw new ConfigurationErrorsException(string.Format("Could not find {0} with name '{1}'", + typeof(IgniteConfigurationSection).Name, sectionName)); + + return Start(section.IgniteConfiguration); + } + + /// <summary> /// Starts Ignite with given configuration. /// </summary> /// <returns>Started Ignite.</returns> http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/BooleanLowerCaseConverter.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/BooleanLowerCaseConverter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/BooleanLowerCaseConverter.cs new file mode 100644 index 0000000..e8cced2 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/BooleanLowerCaseConverter.cs @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Common +{ + using System; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + + /// <summary> + /// Bollean converter that returns lower-case strings, for XML serialization. + /// </summary> + internal class BooleanLowerCaseConverter : BooleanConverter + { + /// <summary> + /// Default instance. + /// </summary> + public static readonly BooleanLowerCaseConverter Instance = new BooleanLowerCaseConverter(); + + /// <summary> + /// Converts the given value object to the specified type, using the specified context and culture information. + /// </summary> + /// <param name="context">An <see cref="ITypeDescriptorContext" /> that provides a format context.</param> + /// <param name="culture"> + /// A <see cref="CultureInfo" />. If null is passed, the current culture is assumed. + /// </param> + /// <param name="value">The <see cref="object" /> to convert.</param> + /// <param name="destinationType"> + /// The <see cref="Type" /> to convert the <paramref name="value" /> parameter to. + /// </param> + /// <returns> + /// An <see cref="object" /> that represents the converted value. + /// </returns> + [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] + [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "2")] + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, + Type destinationType) + { + if (destinationType == typeof (string)) + return value.ToString().ToLowerInvariant(); + + return base.ConvertTo(context, culture, value, destinationType); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/a4d8a049/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs new file mode 100644 index 0000000..af25bfa --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/IgniteConfigurationXmlSerializer.cs @@ -0,0 +1,410 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Common +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + using System.Configuration; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Reflection; + using System.Xml; + using Apache.Ignite.Core.Impl.Events; + + /// <summary> + /// Serializes <see cref="IgniteConfiguration"/> to XML. + /// </summary> + internal static class IgniteConfigurationXmlSerializer + { + /** Attribute that specifies a type for abstract properties, such as IpFinder. */ + private const string TypNameAttribute = "type"; + + /** Xmlns. */ + private const string XmlnsAttribute = "xmlns"; + + /** Schema. */ + private const string Schema = "http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection"; + + /// <summary> + /// Deserializes <see cref="IgniteConfiguration"/> from specified <see cref="XmlReader"/>. + /// </summary> + /// <param name="reader">The reader.</param> + /// <returns>Resulting <see cref="IgniteConfiguration"/>.</returns> + public static IgniteConfiguration Deserialize(XmlReader reader) + { + IgniteArgumentCheck.NotNull(reader, "reader"); + + var cfg = new IgniteConfiguration(); + + if (reader.NodeType == XmlNodeType.Element || reader.Read()) + ReadElement(reader, cfg); + + return cfg; + } + + /// <summary> + /// Serializes specified <see cref="IgniteConfiguration" /> to <see cref="XmlWriter" />. + /// </summary> + /// <param name="configuration">The configuration.</param> + /// <param name="writer">The writer.</param> + /// <param name="rootElementName">Name of the root element.</param> + public static void Serialize(IgniteConfiguration configuration, XmlWriter writer, string rootElementName) + { + IgniteArgumentCheck.NotNull(configuration, "configuration"); + IgniteArgumentCheck.NotNull(writer, "writer"); + IgniteArgumentCheck.NotNullOrEmpty(rootElementName, "rootElementName"); + + WriteElement(configuration, writer, rootElementName, typeof(IgniteConfiguration)); + } + + /// <summary> + /// Writes new element. + /// </summary> + private static void WriteElement(object obj, XmlWriter writer, string rootElementName, Type valueType, + PropertyInfo property = null) + { + if (valueType == typeof(IgniteConfiguration)) + writer.WriteStartElement(rootElementName, Schema); // write xmlns for the root element + else + writer.WriteStartElement(rootElementName); + + if (IsBasicType(valueType)) + WriteBasicProperty(obj, writer, valueType, property); + else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof (ICollection<>)) + WriteCollectionProperty(obj, writer, valueType, property); + else + WriteComplexProperty(obj, writer, valueType); + + writer.WriteEndElement(); + } + + /// <summary> + /// Writes the property of a basic type (primitives, strings, types). + /// </summary> + [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] + private static void WriteBasicProperty(object obj, XmlWriter writer, Type valueType, PropertyInfo property) + { + var converter = GetConverter(property, valueType); + + var stringValue = converter.ConvertToInvariantString(obj); + + writer.WriteString(stringValue); + } + + /// <summary> + /// Writes the collection property. + /// </summary> + private static void WriteCollectionProperty(object obj, XmlWriter writer, Type valueType, PropertyInfo property) + { + var elementType = valueType.GetGenericArguments().Single(); + + var elementTypeName = PropertyNameToXmlName(elementType.Name); + + foreach (var element in (IEnumerable)obj) + WriteElement(element, writer, elementTypeName, elementType, property); + } + + /// <summary> + /// Writes the complex property (nested object). + /// </summary> + [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] + private static void WriteComplexProperty(object obj, XmlWriter writer, Type valueType) + { + var props = GetNonDefaultProperties(obj).ToList(); + + // Specify type for interfaces and abstract classes + if (valueType.IsAbstract) + writer.WriteAttributeString(TypNameAttribute, TypeStringConverter.Convert(obj.GetType())); + + // Write attributes + foreach (var prop in props.Where(p => IsBasicType(p.PropertyType))) + { + var converter = GetConverter(prop, prop.PropertyType); + var stringValue = converter.ConvertToInvariantString(prop.GetValue(obj, null)); + writer.WriteAttributeString(PropertyNameToXmlName(prop.Name), stringValue); + } + + // Write elements + foreach (var prop in props.Where(p => !IsBasicType(p.PropertyType))) + WriteElement(prop.GetValue(obj, null), writer, PropertyNameToXmlName(prop.Name), + prop.PropertyType, prop); + } + + /// <summary> + /// Reads the element. + /// </summary> + private static void ReadElement(XmlReader reader, object target) + { + var targetType = target.GetType(); + + // Read attributes + while (reader.MoveToNextAttribute()) + { + var name = reader.Name; + var val = reader.Value; + + SetProperty(target, name, val); + } + + // Read content + reader.MoveToElement(); + + while (reader.Read()) + { + if (reader.NodeType != XmlNodeType.Element) + continue; + + var name = reader.Name; + var prop = GetPropertyOrThrow(name, reader.Value, targetType); + var propType = prop.PropertyType; + + if (IsBasicType(propType)) + { + // Regular property in xmlElement form + SetProperty(target, name, reader.ReadString()); + } + else if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof (ICollection<>)) + { + // Collection + ReadCollectionProperty(reader, prop, target); + } + else + { + // Nested object (complex property) + prop.SetValue(target, ReadComplexProperty(reader, propType, prop.Name, targetType), null); + } + } + } + + /// <summary> + /// Reads the complex property (nested object). + /// </summary> + private static object ReadComplexProperty(XmlReader reader, Type propType, string propName, Type targetType) + { + if (propType.IsAbstract) + { + var typeName = reader.GetAttribute(TypNameAttribute); + + var derivedTypes = GetConcreteDerivedTypes(propType); + + propType = typeName == null + ? null + : Type.GetType(typeName, false) ?? derivedTypes.FirstOrDefault(x => x.Name == typeName); + + if (propType == null) + { + var message = string.Format("'type' attribute is required for '{0}.{1}' property", targetType.Name, + propName); + + if (typeName != null) + { + message += ", specified type cannot be resolved: " + typeName; + } + else if (derivedTypes.Any()) + message += ", possible values are: " + string.Join(", ", derivedTypes.Select(x => x.Name)); + + throw new ConfigurationErrorsException(message); + } + } + + var nestedVal = Activator.CreateInstance(propType); + + using (var subReader = reader.ReadSubtree()) + { + subReader.Read(); // read first element + + ReadElement(subReader, nestedVal); + } + + return nestedVal; + } + + /// <summary> + /// Reads the collection. + /// </summary> + private static void ReadCollectionProperty(XmlReader reader, PropertyInfo prop, object target) + { + var elementType = prop.PropertyType.GetGenericArguments().Single(); + + var listType = typeof (List<>).MakeGenericType(elementType); + + var list = (IList) Activator.CreateInstance(listType); + + var converter = IsBasicType(elementType) ? GetConverter(prop, elementType) : null; + + using (var subReader = reader.ReadSubtree()) + { + subReader.Read(); // skip list head + while (subReader.Read()) + { + if (subReader.NodeType != XmlNodeType.Element) + continue; + + if (subReader.Name != PropertyNameToXmlName(elementType.Name)) + throw new ConfigurationErrorsException( + string.Format("Invalid list element in IgniteConfiguration: expected '{0}', but was '{1}'", + PropertyNameToXmlName(elementType.Name), subReader.Name)); + + list.Add(converter != null + ? converter.ConvertFromInvariantString(subReader.ReadString()) + : ReadComplexProperty(subReader, elementType, prop.Name, target.GetType())); + } + } + + prop.SetValue(target, list, null); + } + + /// <summary> + /// Sets the property. + /// </summary> + private static void SetProperty(object target, string propName, string propVal) + { + if (propName == TypNameAttribute || propName == XmlnsAttribute) + return; + + var type = target.GetType(); + var property = GetPropertyOrThrow(propName, propVal, type); + + var converter = GetConverter(property, property.PropertyType); + + var convertedVal = converter.ConvertFromInvariantString(propVal); + + property.SetValue(target, convertedVal, null); + } + + /// <summary> + /// Gets concrete derived types. + /// </summary> + private static List<Type> GetConcreteDerivedTypes(Type type) + { + return type.Assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && type.IsAssignableFrom(t)).ToList(); + } + + /// <summary> + /// Gets specified property from a type or throws an exception. + /// </summary> + private static PropertyInfo GetPropertyOrThrow(string propName, string propVal, Type type) + { + var property = type.GetProperty(XmlNameToPropertyName(propName)); + + if (property == null) + throw new ConfigurationErrorsException( + string.Format( + "Invalid IgniteConfiguration attribute '{0}={1}', there is no such property on '{2}'", + propName, propVal, type)); + + return property; + } + + /// <summary> + /// Converts an XML name to CLR name. + /// </summary> + private static string XmlNameToPropertyName(string name) + { + Debug.Assert(name.Length > 0); + + if (name == "int") + return "Int32"; // allow aliases + + return char.ToUpperInvariant(name[0]) + name.Substring(1); + } + + /// <summary> + /// Converts a CLR name to XML name. + /// </summary> + private static string PropertyNameToXmlName(string name) + { + Debug.Assert(name.Length > 0); + + if (name == "Int32") + return "int"; // allow aliases + + return char.ToLowerInvariant(name[0]) + name.Substring(1); + } + + /// <summary> + /// Determines whether specified type is a basic built-in type. + /// </summary> + private static bool IsBasicType(Type propertyType) + { + Debug.Assert(propertyType != null); + + return propertyType.IsValueType || propertyType == typeof(string) || propertyType == typeof(Type); + } + + /// <summary> + /// Gets converter for a property. + /// </summary> + private static TypeConverter GetConverter(PropertyInfo property, Type propertyType) + { + Debug.Assert(property != null); + Debug.Assert(propertyType != null); + + if (propertyType.IsEnum) + return new GenericEnumConverter(propertyType); + + if (propertyType == typeof (Type)) + return TypeStringConverter.Instance; + + if (propertyType == typeof(bool)) + return BooleanLowerCaseConverter.Instance; + + if (property.DeclaringType == typeof (IgniteConfiguration) && property.Name == "IncludedEventTypes") + return EventTypeConverter.Instance; + + var converter = TypeDescriptor.GetConverter(propertyType); + + if (converter == null || !converter.CanConvertFrom(typeof(string)) || + !converter.CanConvertTo(typeof(string))) + throw new ConfigurationErrorsException("No converter for type " + propertyType); + + return converter; + } + + /// <summary> + /// Gets properties with non-default value. + /// </summary> + private static IEnumerable<PropertyInfo> GetNonDefaultProperties(object obj) + { + Debug.Assert(obj != null); + + return obj.GetType().GetProperties().Where(p => !Equals(p.GetValue(obj, null), GetDefaultValue(p))); + } + + /// <summary> + /// Gets the default value for a property. + /// </summary> + private static object GetDefaultValue(PropertyInfo property) + { + var attr = property.GetCustomAttributes(true).OfType<DefaultValueAttribute>().FirstOrDefault(); + + if (attr != null) + return attr.Value; + + var propertyType = property.PropertyType; + + if (propertyType.IsValueType) + return Activator.CreateInstance(propertyType); + + return null; + } + } +}
