diff --git a/lang/cs/Org.Apache.REEF.Tang.Tests/Configuration/TestConfiguration.cs b/lang/cs/Org.Apache.REEF.Tang.Tests/Configuration/TestConfiguration.cs index 0c10dfd36a..b2970ae0d1 100644 --- a/lang/cs/Org.Apache.REEF.Tang.Tests/Configuration/TestConfiguration.cs +++ b/lang/cs/Org.Apache.REEF.Tang.Tests/Configuration/TestConfiguration.cs @@ -15,8 +15,6 @@ // specific language governing permissions and limitations // under the License. -using System; -using System.Collections.Generic; using Org.Apache.REEF.Common.Tasks; using Org.Apache.REEF.Examples.Tasks.HelloTask; using Org.Apache.REEF.Tang.Annotations; @@ -31,6 +29,9 @@ using Org.Apache.REEF.Tang.Protobuf; using Org.Apache.REEF.Tang.Tests.ScenarioTest; using Org.Apache.REEF.Tang.Util; +using System; +using System.Collections.Generic; +using System.Linq; using Xunit; namespace Org.Apache.REEF.Tang.Tests.Configuration @@ -425,6 +426,144 @@ public void TestTimerConfigurationWithClassHierarchy() timer.sleep(); } + [Fact] + public void TestListConfig() + { + IList stringList1 = new List(); + stringList1.Add("foo"); + stringList1.Add("bar"); + + IList stringList2 = new List(); + stringList2.Add("test1"); + stringList2.Add("test2"); + stringList2.Add("test3"); + stringList2.Add("test4"); + + IConfiguration conf = TangFactory.GetTang().NewConfigurationBuilder() + .BindList(GenericType.Class, stringList1) + .BindList(GenericType.Class, stringList2) + .Build(); + + string s = ConfigurationFile.ToConfigurationString(conf); + IConfiguration conf2 = ConfigurationFile.GetConfiguration(s); + + ListInjectTest injectTest = (ListInjectTest)TangFactory.GetTang(). + NewInjector(conf2).GetInstance(typeof(ListInjectTest)); + + Assert.Equal(stringList1, injectTest.List1); + Assert.Equal(stringList2, injectTest.List2); + } + + [Fact] + public void TestListConfigWithEmptyList() + { + IList stringList1 = new List(); + + IConfiguration conf = TangFactory.GetTang().NewConfigurationBuilder() + .BindList(GenericType.Class, stringList1) + .Build(); + string s = ConfigurationFile.ToConfigurationString(conf); + // string will be empty since there is nothing to save + Assert.Equal(0, s.Length); + } + + [Fact] + public void TestListConfigWithEmptyString() + { + IList stringList1 = new List(); + stringList1.Add(""); + + try + { + IConfiguration conf = TangFactory.GetTang().NewConfigurationBuilder() + .BindList(GenericType.Class, stringList1) + .Build(); + } + catch(BindException) + { + return; + } + + Assert.True(false, "Failed to throw expected exception."); + + } + + [Fact] + public void TestListConfigWithNullStringValue() + { + IList stringList1 = new List(); + stringList1.Add(null); + + try + { + IConfiguration conf = TangFactory.GetTang().NewConfigurationBuilder() + .BindList(GenericType.Class, stringList1) + .Build(); + } + catch(BindException) + { + return; + } + + Assert.True(false, "Failed to throw expected exception."); + } + + private void TestSerializeListHelper(IList items, int expectedLists = 1) + { + IConfiguration conf = TangFactory.GetTang().NewConfigurationBuilder() + .BindList(GenericType.Class, items) + .Build(); + + var serializer = new AvroConfigurationSerializer(); + byte[] bytes = serializer.ToByteArray(conf); + IConfiguration conf2 = serializer.FromByteArray(bytes); + Assert.Equal(expectedLists, conf2.GetBoundList().Count); + if (expectedLists > 1) + { + var actualList = conf2.GetBoundList().First().Value; + Assert.Equal(items, actualList); + } + } + + [Fact] + public void TestListSerialize() + { + IList stringList = new List(); + stringList.Add("foo"); + stringList.Add("bar"); + TestSerializeListHelper(stringList); + } + + [Fact] + public void TestListSerializeNullStringValue() + { + string msg = null; + IList stringList = new List(); + stringList.Add(null); + + var builder = TangFactory.GetTang().NewConfigurationBuilder(); + Assert.Throws(() => builder.BindList(GenericType.Class, stringList)); + } + + [Fact] + public void TestListSerializeEmptyList() + { + IList stringList = new List(); + TestSerializeListHelper(stringList, 0); + } + + [Fact] + public void TestListSerializeEmptyStrings() + { + string msg = null; + IList stringList = new List(); + stringList.Add(""); + stringList.Add(""); + + var builder = TangFactory.GetTang().NewConfigurationBuilder(); + Assert.Throws(() => builder.BindList(GenericType.Class, stringList)); + } + [Fact] public void TestSetConfig() { @@ -448,6 +587,7 @@ public void TestSetConfig() Assert.True(actual.Contains("six")); } + [Fact] public void TestSetConfigWithAvroSerialization() { @@ -471,6 +611,7 @@ public void TestSetConfigWithAvroSerialization() Assert.True(actual.Contains("six")); } + [Fact] public void TestNullStringValue() { @@ -508,11 +649,38 @@ public void TestSetConfigNullValue() } } + [NamedParameter] + class ListOfStrings : Name> + { + + } + + [NamedParameter] + class ListOfStrings2 : Name> + { + + } + + class ListInjectTest + { + public IList List1; + public IList List2; + + [Inject] + ListInjectTest([Parameter(typeof(ListOfStrings))] IList list1, + [Parameter(typeof(ListOfStrings2))] IList list2) + { + this.List1 = list1; + this.List2 = list2; + } + } + [NamedParameter(DefaultValues = new string[] { "one", "two", "three" })] class SetOfNumbers : Name> { } + class Box { public ISet Numbers; @@ -560,4 +728,4 @@ public string GetString() return str; } } -} \ No newline at end of file +} diff --git a/lang/cs/Org.Apache.REEF.Tang/Formats/AvroConfigurationSerializer.cs b/lang/cs/Org.Apache.REEF.Tang/Formats/AvroConfigurationSerializer.cs index 565d4fdd57..c09321e073 100644 --- a/lang/cs/Org.Apache.REEF.Tang/Formats/AvroConfigurationSerializer.cs +++ b/lang/cs/Org.Apache.REEF.Tang/Formats/AvroConfigurationSerializer.cs @@ -15,13 +15,6 @@ // specific language governing permissions and limitations // under the License. -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; using Microsoft.Hadoop.Avro; using Microsoft.Hadoop.Avro.Container; using Newtonsoft.Json; @@ -34,6 +27,13 @@ using Org.Apache.REEF.Tang.Types; using Org.Apache.REEF.Tang.Util; using Org.Apache.REEF.Utilities.Logging; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; namespace Org.Apache.REEF.Tang.Formats { @@ -254,12 +254,32 @@ public AvroConfiguration ToAvroConfiguration(IConfiguration c) } else { - Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException(), LOGGER); + throw new TangApplicationException("Unable to serialize set of type {e.Value.GetType()}"); } l.Add(new ConfigurationEntry(e.Key.GetFullName(), val)); } + foreach (var kvp in conf.GetBoundList()) + { + foreach (var item in kvp.Value) + { + string val = null; + if (item is string) + { + val = (string)item; + } + else if (item is INode) + { + val = ((INode)item).GetFullName(); + } + else + { + throw new TangApplicationException("Unable to serialize list of type {item.GetType()}"); + } + l.Add(new ConfigurationEntry(kvp.Key.GetFullName(), val)); + } + } return new AvroConfiguration(Language.Cs.ToString(), l); } diff --git a/lang/cs/Org.Apache.REEF.Tang/Formats/ConfigurationFile.cs b/lang/cs/Org.Apache.REEF.Tang/Formats/ConfigurationFile.cs index 01a20c8a26..63cf800603 100644 --- a/lang/cs/Org.Apache.REEF.Tang/Formats/ConfigurationFile.cs +++ b/lang/cs/Org.Apache.REEF.Tang/Formats/ConfigurationFile.cs @@ -15,12 +15,6 @@ // specific language governing permissions and limitations // under the License. -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; using Org.Apache.REEF.Tang.Exceptions; using Org.Apache.REEF.Tang.Implementations.Configuration; using Org.Apache.REEF.Tang.Implementations.Tang; @@ -28,6 +22,12 @@ using Org.Apache.REEF.Tang.Types; using Org.Apache.REEF.Tang.Util; using Org.Apache.REEF.Utilities.Logging; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; namespace Org.Apache.REEF.Tang.Formats { @@ -133,12 +133,33 @@ public static HashSet ToConfigurationStringList(IConfiguration c) } else { - Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException(), LOGGER); + throw new BindException($"Failed to serialize set of unsupported type {e.Value.GetType()}"); } l.Add(GetFullName(e.Key) + '=' + Escape(val)); } + foreach(var kvp in conf.GetBoundList()) + { + foreach (var item in kvp.Value) + { + string val = null; + if (item is string) + { + val = GetFullName((string)item); + } + else if (kvp.Value is INode) + { + val = GetFullName((INode)kvp.Value); + } + else + { + throw new BindException($"Failed to serialize list of unsupported type {item.GetType()}"); + } + l.Add(GetFullName(kvp.Key) + '=' + Escape(val)); + } + } + return l; } diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationBuilderImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationBuilderImpl.cs index 4eab9a1e8a..b3dff636fb 100644 --- a/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationBuilderImpl.cs +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationBuilderImpl.cs @@ -275,6 +275,10 @@ public void BindParameter(INamedParameterNode name, string value) { BindSetEntry((INamedParameterNode)name, value); } + else if (name.IsList()) + { + BindList((INamedParameterNode)name, value); + } else { try @@ -289,6 +293,17 @@ public void BindParameter(INamedParameterNode name, string value) } } + public void BindList(INamedParameterNode iface, string impl) + { + IList l; + if (!BoundLists.TryGetValue(iface, out l)) + { + l = new List(); + BoundLists.Add(iface, l); + } + l.Add((object)impl); + } + public void BindImplementation(IClassNode n, IClassNode m) { if (this.ClassHierarchy.IsImplementation(n, m)) @@ -337,6 +352,11 @@ public void BindList(INamedParameterNode iface, IList impl) IList l = new List(); foreach (var n in impl) { + if (string.IsNullOrEmpty(n)) + { + throw new ArgumentException("List cannot contain string that are null or empty"); + } + l.Add((object)n); } BoundLists.Add(iface, l); diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/CsConfigurationBuilderImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/CsConfigurationBuilderImpl.cs index 569d4670a1..922aefded6 100644 --- a/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/CsConfigurationBuilderImpl.cs +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/CsConfigurationBuilderImpl.cs @@ -399,7 +399,15 @@ ICsInternalConfigurationBuilder ICsInternalConfigurationBuilder.BindList(Type if Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); } - BindList((INamedParameterNode)n, implList); + try + { + BindList((INamedParameterNode)n, implList); + } + catch(ArgumentException ex) + { + throw new BindException($"BindList failed to bind for {iface.Name}, reason: {ex.Message}"); + } + return this; } @@ -490,4 +498,4 @@ private INode GetNode(Type c) return ((ICsClassHierarchy)ClassHierarchy).GetNode(c); } } -} \ No newline at end of file +}